From d83b934e61554c0dd17c76b060a7d477da1a9476 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Fri, 2 May 2025 17:35:52 +0530 Subject: [PATCH] armbot circular path updated and ui dynamic updation --- .../IntialLoad/loadInitialFloorItems.ts | 2 +- .../events/points/creator/pointsCreator.tsx | 55 ++- .../instances/animator/roboticArmAnimator.tsx | 316 +++++++---------- .../armInstance/roboticArmInstance.tsx | 312 +---------------- .../instances/ikInstance/ikInstance.tsx | 3 +- .../instances/roboticArmInstances.tsx | 2 - .../simulation/roboticArm/roboticArm.tsx | 42 ++- .../simulation/ui/arm/PickDropPoints.tsx | 20 +- .../modules/simulation/ui/arm/armBotUI.tsx | 328 ++++++++++-------- .../simulation/ui/arm/useDraggableGLTF.ts | 56 +-- 10 files changed, 438 insertions(+), 698 deletions(-) diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index a351d73..9515188 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -206,7 +206,7 @@ function processLoadedModel( actionType: "travel", unLoadDuration: 5, loadCapacity: 10, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [] diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index c6ec316..e4eaafb 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -23,6 +23,9 @@ function PointsCreator() { const eventData = getEventByModelUuid( selectedEventSphere.userData.modelUuid ); + + + if (eventData) { setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); } else { @@ -80,7 +83,10 @@ function PointsCreator() { {events.map((event, i) => { if (event.type === "transfer") { return ( - + {event.points.map((point, j) => ( { if (selectedEventData?.data.type !== 'vehicle') { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); } setTransformMode(null); }} key={`${i}-${j}`} position={new THREE.Vector3(...point.position)} - // rotation={new THREE.Euler(...point.rotation)} - userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }} + userData={{ + modelUuid: event.modelUuid, + pointUuid: point.uuid, + }} > @@ -111,7 +119,10 @@ function PointsCreator() { ); } else if (event.type === "vehicle") { return ( - + @@ -137,7 +150,10 @@ function PointsCreator() { ); } else if (event.type === "roboticArm") { return ( - + { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} - // rotation={new THREE.Euler(...event.point.rotation)} - userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} > @@ -163,7 +181,10 @@ function PointsCreator() { ); } else if (event.type === "machine") { return ( - + { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} - // rotation={new THREE.Euler(...event.point.rotation)} - userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} > @@ -208,4 +231,4 @@ function PointsCreator() { ); } -export default PointsCreator; +export default PointsCreator; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index d1bf523..d32a6c3 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; import { Line } from '@react-three/drei'; @@ -21,14 +21,8 @@ function RoboticArmAnimator({ const progressRef = useRef(0); const curveRef = useRef(null); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const [curvePoints, setCurvePoints] = useState(null); const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); - const [nearestStartPoint, setNearestStartPoint] = useState<[number, number, number] | null>(null); - const [nearestEndPoint, setNearestEndPoint] = useState<[number, number, number] | null>(null); const [customCurvePoints, setCustomCurvePoints] = useState(null); - const stepSegmentsRef = useRef([]); - const [currentStep, setCurrentStep] = useState(0); - const elapsedTimeRef = useRef(0); // Zustand stores const { isPlaying } = usePlayButtonStore(); @@ -41,7 +35,6 @@ function RoboticArmAnimator({ setCurrentPath(path); }, [path]); - // Reset logic when `isPlaying` changes useEffect(() => { if (!isPlaying) { @@ -50,221 +43,172 @@ function RoboticArmAnimator({ } }, [isPlaying]); - // Handle path generation (including CatmullRomCurve3) - useEffect(() => { - if (currentPath && currentPath.length === 3) { - const [start, mid, end] = currentPath; - const points = [ - new THREE.Vector3(start[0], start[1], start[2]), - new THREE.Vector3(start[0], mid[1] + 0.5, start[2]), - new THREE.Vector3(mid[0], mid[1] + 0.5, mid[2]), - new THREE.Vector3(mid[0], end[1] + 0.5, mid[2]), - new THREE.Vector3(end[0], end[1], end[2]) - ]; - const generatedCurve = new THREE.CatmullRomCurve3(points, false, 'centripetal', 1).getPoints(100); - curveRef.current = generatedCurve; - setCurvePoints(generatedCurve); - } - }, [currentPath]); - // Handle circle points based on armBot position useEffect(() => { - const radius = 1.6; // Circle radius - const segments = 64; - const points: [number, number, number][] = []; - - for (let i = 0; i <= segments; i++) { - const theta = (i / segments) * Math.PI * 2; - const x = radius * Math.cos(theta) + armBot.position[0]; - const z = radius * Math.sin(theta) + armBot.position[2]; - const y = armBot.position[1]; - points.push([x, y, z]); - } - + const points = generateRingPoints(1.6, 64) setCirclePoints(points); }, [armBot.position]); + + function generateRingPoints(radius: any, segments: any) { + const points: [number, number, number][] = []; + for (let i = 0; i < segments; i++) { + // Calculate angle for current segment + const angle = (i / segments) * Math.PI * 2; + + // Calculate x and z coordinates (y remains the same for a flat ring) + const x = Math.cos(angle) * radius; + const z = Math.sin(angle) * radius; + + points.push([x, 1.5, z]); + } + return points; + } + + const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { + for (let i = 0; i < points.length; i++) { + const [x, y, z] = points[i]; + if ( + Math.abs(x - nearestPoint[0]) < epsilon && + Math.abs(y - nearestPoint[1]) < epsilon && + Math.abs(z - nearestPoint[2]) < epsilon + ) { + return i; // Found the matching index + } + } + return -1; // Not found + }; + + // Handle nearest points and final path (including arc points) useEffect(() => { if (circlePoints.length > 0 && currentPath.length > 0) { - const startPoint = [currentPath[0][0], currentPath[0][1] + 0.5, currentPath[0][2]] as [number, number, number]; - const endPoint = [currentPath[currentPath.length - 1][0], currentPath[currentPath.length - 1][1] + 0.5, currentPath[currentPath.length - 1][2]] as [number, number, number]; - - const findNearest = (target: [number, number, number]) => { - let nearestPoint = circlePoints[0]; - let minDistance = Infinity; - - for (const point of circlePoints) { - const dx = target[0] - point[0]; - const dy = target[1] - point[1]; - const dz = target[2] - point[2]; - const distance = Math.sqrt(dx * dx + dy * dy + dz * dz); - if (distance < minDistance) { - minDistance = distance; - nearestPoint = point; - } - } - return nearestPoint; - }; - - const nearestToStart = findNearest(startPoint); - const nearestToEnd = findNearest(endPoint); - - setNearestStartPoint(nearestToStart); - setNearestEndPoint(nearestToEnd); - - const radius = 1.6; - const segments = 64; - const center = armBot.position; - - const startAngle = Math.atan2(nearestToStart[2] - center[2], nearestToStart[0] - center[0]); - const endAngle = Math.atan2(nearestToEnd[2] - center[2], nearestToEnd[0] - center[0]); - let deltaAngle = endAngle - startAngle; - - if (deltaAngle > Math.PI) { - deltaAngle -= 2 * Math.PI; - } else if (deltaAngle < -Math.PI) { - deltaAngle += 2 * Math.PI; - } - - const arcPoints: [number, number, number][] = []; - - for (let i = 0; i <= segments; i++) { - const t = i / segments; - const angle = startAngle + deltaAngle * t; - const x = center[0] + radius * Math.cos(angle); - const z = center[2] + radius * Math.sin(angle); - const y = startPoint[1]; - arcPoints.push([x, y, z]); - } const start = currentPath[0]; const end = currentPath[currentPath.length - 1]; - let step1 = [ - [start[0], start[1], start[2]], - [start[0], start[1] + 0.5, start[2]] - ]; - let step2 = [ - [start[0], start[1] + 0.5, start[2]], - [nearestToStart[0], start[1] + 0.5, nearestToStart[2]] - ]; - let step3 = [ - [nearestToStart[0], start[1] + 0.5, nearestToStart[2]], - [end[0], arcPoints[arcPoints.length - 1][1], end[2]] - ] - let step4 = [ - [end[0], arcPoints[arcPoints.length - 1][1], end[2]], - [end[0], end[1] + 0.5, end[2]] - ] - let step5 = [ - [end[0], end[1] + 0.5, end[2]], - [end[0], end[1], end[2]], - ] + const raisedStart = [start[0], start[1] + 0.5, start[2]] as [number, number, number]; + const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [number, number, number]; - const points = [ - new THREE.Vector3(start[0], start[1], start[2]), - new THREE.Vector3(start[0], start[1] + 0.5, start[2]), + const findNearest = (target: [number, number, number]) => { + return circlePoints.reduce((nearest, point) => { + const distance = Math.hypot( + target[0] - point[0], + target[1] - point[1], + target[2] - point[2] + ); + const nearestDistance = Math.hypot( + target[0] - nearest[0], + target[1] - nearest[1], + target[2] - nearest[2] + ); + return distance < nearestDistance ? point : nearest; + }, circlePoints[0]); + }; - new THREE.Vector3(start[0], start[1] + 0.5, start[2]), - new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), + const nearestToStart = findNearest(raisedStart); - new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), - new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), + const nearestToEnd = findNearest(raisedEnd); - new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), - new THREE.Vector3(end[0], end[1] + 0.5, end[2]), + const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); - new THREE.Vector3(end[0], end[1] + 0.5, end[2]), - new THREE.Vector3(end[0], end[1], end[2]) - ]; - const steps = [ - [points[0], points[1]], // step1 - [points[2], points[3]], // step2 - [points[4], points[5]], // step3 - [points[6], points[7]], // step4 - [points[8], points[9]] // step5 - ]; - stepSegmentsRef.current = steps; - setCurrentStep(0); - elapsedTimeRef.current = 0; + const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); - const finalPath = [ - new THREE.Vector3(start[0], start[1], start[2]), - new THREE.Vector3(start[0], start[1] + 0.5, start[2]), - new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), - ...arcPoints.map(([x, y, z]) => new THREE.Vector3(x, y, z)), - new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), - new THREE.Vector3(end[0], end[1], end[2]) + // Find clockwise and counter-clockwise distances + const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; + + const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; + + const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; + + // Collect arc points between start and end + let arcPoints: [number, number, number][] = []; + + if (clockwiseIsShorter) { + if (indexOfNearestStart <= indexOfNearestEnd) { + arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1); + } else { + // Wrap around + arcPoints = [ + ...circlePoints.slice(indexOfNearestStart, 64), + ...circlePoints.slice(0, indexOfNearestEnd + 1) + ]; + } + } else { + if (indexOfNearestStart >= indexOfNearestEnd) { + for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { + arcPoints.push(circlePoints[i]); + } + } else { + for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { + arcPoints.push(circlePoints[i]); + } + } + } + + // Continue your custom path logic + const pathVectors = [ + new THREE.Vector3(start[0], start[1], start[2]), // start + new THREE.Vector3(raisedStart[0], raisedStart[1], raisedStart[2]), // lift up + new THREE.Vector3(nearestToStart[0], raisedStart[1], nearestToStart[2]), // move to arc start + ...arcPoints.map(point => new THREE.Vector3(point[0], raisedStart[1], point[2])), + new THREE.Vector3(nearestToEnd[0], raisedEnd[1], nearestToEnd[2]), // move from arc end + new THREE.Vector3(raisedEnd[0], raisedEnd[1], raisedEnd[2]), // lowered end + new THREE.Vector3(end[0], end[1], end[2]) // end ]; - const customCurve = new THREE.CatmullRomCurve3(finalPath, false, 'centripetal', 1); - const customCurveGeneratedPoints = customCurve.getPoints(100); - - setCustomCurvePoints(customCurveGeneratedPoints); + const customCurve = new THREE.CatmullRomCurve3(pathVectors, false, 'centripetal', 1); + const generatedPoints = customCurve.getPoints(100); + setCustomCurvePoints(generatedPoints); } }, [circlePoints, currentPath]); // Frame update for animation useFrame((_, delta) => { - if (ikSolver && curveRef.current && currentPath.length >= 0) { - const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); - if (!bone) return; + if (!ikSolver) return; - if (isPlaying) { - if (!isPaused) { - const curvePoints = curveRef.current; - const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); - const index = Math.floor(speedAdjustedProgress); + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + if (!bone) return; - if (curvePoints) { - - if (index >= curvePoints.length) { - HandleCallback(); - setCurrentPath([]); - curveRef.current = null; - progressRef.current = 0; - } else { - const point = curvePoints[index]; - bone.position.copy(point); - progressRef.current = speedAdjustedProgress; - } - } + if (isPlaying) { + if (!isPaused && customCurvePoints && currentPath.length > 0) { + const curvePoints = customCurvePoints; + const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); + const index = Math.floor(speedAdjustedProgress); + if (index >= curvePoints.length) { + // Reached the end of the curve + HandleCallback(); + setCurrentPath([]); + curveRef.current = null; + progressRef.current = 0; } else { - logStatus(armBot.modelUuid, 'Simulation Play Exited'); + const point = curvePoints[index]; + bone.position.copy(point); + progressRef.current = speedAdjustedProgress; } - - ikSolver.update(); + } else if (isPaused) { + logStatus(armBot.modelUuid, 'Simulation Paused'); } - } else if (ikSolver && !isPlaying && currentPath.length === 0) { - const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + + ikSolver.update(); + } else if (!isPlaying && currentPath.length === 0) { + // Not playing anymore, reset to rest bone.position.copy(restPosition); ikSolver.update(); } }); + return ( - <> - {customCurvePoints && currentPath && ( - - [p.x, p.y, p.z])} color="green" lineWidth={5} dashed={false} /> - {/* */} - {currentPath.length >= 1 && ( - <> - {/* First point */} - {/* - - - */} - - {/* Last point */} - {/* - - - */} - - )} + {customCurvePoints && currentPath && isPlaying && ( + + [p.x, p.y, p.z] as [number, number, number])} + color="green" + lineWidth={5} + dashed={false} + /> )} diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 7660ad1..ff5e7e3 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -8,6 +8,7 @@ import { useThree } from "@react-three/fiber"; import { useFloorItems } from '../../../../../store/store'; import useModuleStore from '../../../../../store/useModuleStore'; import * as THREE from "three"; +import { useSelectedAction } from '../../../../../store/simulation/useSimulationStore'; function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { @@ -28,6 +29,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const { isPlaying } = usePlayButtonStore(); const { isReset, setReset } = useResetButtonStore(); const { isPaused } = usePauseButtonStore(); + const { selectedAction } = useSelectedAction(); function firstFrame() { startTime = performance.now(); @@ -78,6 +80,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { startTime=0; const endPoint = armBot.point.actions[0].process.endPoint; if (endPoint) { + let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); if (curve) { logStatus(armBot.modelUuid, "dropping the object"); @@ -93,21 +96,17 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { }, [isPaused]); useEffect(() => { - let armItems = floorItems?.filter((val: any) => - val.modelUuid === "3abf5d46-b59e-4e6b-9c02-a4634b64b82d" - ); - // Get the first matching item - let armItem = armItems?.[0]; - if (armItem) { - const targetMesh = scene?.getObjectByProperty("uuid", armItem.modelUuid); + const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); + if (targetMesh) { targetMesh.visible = activeModule !== "simulation" } - } + const targetBones = ikSolver?.mesh.skeleton.bones.find( (b: any) => b.name === targetBone ); if (isReset) { + logStatus(armBot.modelUuid, "Simulation Play Reset Successfully") removeCurrentAction(armBot.modelUuid) setArmBotActive(armBot.modelUuid, true) @@ -127,8 +126,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") } if (isPlaying) { + //Moving armBot from initial point to rest position. if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { + setArmBotActive(armBot.modelUuid, true) setArmBotState(armBot.modelUuid, "running") setCurrentPhase("init-to-rest"); @@ -144,12 +145,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") const timeoutId = setTimeout(() => { - addCurrentAction(armBot.modelUuid, 'action-003'); + let actionId=armBot.point.actions[0].actionUuid + // addCurrentAction(armBot.modelUuid,actionId); + addCurrentAction(armBot.modelUuid, selectedAction?.actionId); }, 3000); return () => clearTimeout(timeoutId); } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { if (armBot.currentAction) { + setArmBotActive(armBot.modelUuid, true); setArmBotState(armBot.modelUuid, "running"); setCurrentPhase("rest-to-start"); @@ -253,7 +257,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } } const logStatus = (id: string, status: string) => { - // console.log('status: ', status); + // } return ( @@ -261,296 +265,8 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { - ) } export default RoboticArmInstance; - - - - - - - - - - - - -// import React, { useEffect, useRef, useState } from 'react'; -// import IKInstance from '../ikInstance/ikInstance'; -// import RoboticArmAnimator from '../animator/roboticArmAnimator'; -// import { usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; -// import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; -// import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_4.glb"; -// import { useThree } from "@react-three/fiber"; -// import { useFloorItems } from '../../../../../store/store'; -// import useModuleStore from '../../../../../store/useModuleStore'; -// import * as THREE from "three"; - -// function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { - -// const [currentPhase, setCurrentPhase] = useState("init"); -// const [path, setPath] = useState<[number, number, number][]>([]); -// const [ikSolver, setIkSolver] = useState(null); -// const { scene } = useThree(); -// const restPosition = new THREE.Vector3(0, 1, -1.6); -// const targetBone = "Target"; -// const groupRef = useRef(null); - -// // Zustand Stores -// const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); -// const { floorItems } = useFloorItems(); -// const { activeModule } = useModuleStore(); -// const { isPlaying } = usePlayButtonStore(); -// const { isReset, setReset } = useResetButtonStore(); - -// // Generate path according to constraints -// const generateArmPath = ( -// startPoint: [number, number, number], -// endPoint: [number, number, number], -// restPos: THREE.Vector3 = new THREE.Vector3(0, 1, -1.6) -// ): [number, number, number][] => { -// const path: [number, number, number][] = []; - -// const startVec = new THREE.Vector3(...startPoint); -// const endVec = new THREE.Vector3(...endPoint); - -// // Helper functions -// const addLinePoints = (from: THREE.Vector3, to: THREE.Vector3, segments: number) => { -// for (let i = 0; i <= segments; i++) { -// const t = i / segments; -// const point = new THREE.Vector3().lerpVectors(from, to, t); -// path.push([point.x, point.y, point.z]); -// } -// }; - -// const addCurveFromTo = (from: THREE.Vector3, to: THREE.Vector3, segments: number = 15) => { -// const mid = new THREE.Vector3() -// .addVectors(from, to) -// .multiplyScalar(0.5) -// .setY(Math.max(from.y, to.y) + 0.5); -// const curve = new THREE.CatmullRomCurve3([from, mid, to]); -// const points = curve.getPoints(segments); -// for (const p of points) { -// path.push([p.x, p.y, p.z]); -// } -// }; - -// // Step 1: Move vertically to align Y with start point -// const step1Start = startVec.clone().setY(restPos.y); -// const step1End = startVec.clone(); -// addLinePoints(step1Start, step1End, 10); - -// // Step 2: Move horizontally XZ to reach start point -// const step2Start = step1End.clone(); -// const step2End = startVec.clone(); // ✅ Fixed here -// addLinePoints(step2Start, step2End, 10); -// addLinePoints(step1End, step2End, 10); - -// // Optional Step 3: Move to rest/midpoint if needed -// const distanceToRest = startVec.distanceTo(restPos); -// if (distanceToRest > 1) { -// addCurveFromTo(step2End, restPos, 15); -// } - -// // Step 4: Move up/down to align Y with end point -// const step4Start = endVec.clone().setY(restPos.y); -// const step4End = endVec.clone(); -// addLinePoints(step4Start, step4End, 10); - -// // Step 5: Move horizontally XZ to end point -// addLinePoints(step4End, endVec.clone(), 10); - -// // Step 6: Return to rest or mid-position after dropping -// const finalRestHeight = restPos.clone().setY(endVec.y + 0.5); -// addCurveFromTo(endVec, finalRestHeight, 15); - -// return path; -// }; - -// useEffect(() => { -// let armItems = floorItems?.filter((val: any) => -// val.modeluuid === armBot.modelUuid -// ); -// let armItem = armItems?.[0]; -// if (armItem) { -// const targetMesh = scene?.getObjectByProperty("uuid", armItem.modeluuid); -// if (targetMesh) { -// targetMesh.visible = activeModule !== "simulation"; -// } -// } - -// const targetBones = ikSolver?.mesh.skeleton.bones.find( -// (b: any) => b.name === targetBone -// ); - -// if (isReset) { -// logStatus(armBot.modelUuid, "Simulation Play Reset Successfully"); -// removeCurrentAction(armBot.modelUuid); -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("init-to-rest"); - -// if (targetBones) { -// const startPoint = new THREE.Vector3(targetBones.position.x, targetBones.position.y, targetBones.position.z); -// const curve = new THREE.CatmullRomCurve3([ -// startPoint, -// startPoint.clone().setY(restPosition.y + 0.5), -// restPosition.clone(), -// ]); -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// } - -// setReset(false); -// logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position."); -// } - -// if (isPlaying) { -// // Phase 1: Initial -> Rest -// if (!armBot.isActive && armBot.state === "idle" && currentPhase === "init") { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("init-to-rest"); - -// if (targetBones) { -// const startPoint = new THREE.Vector3(targetBones.position.x, targetBones.position.y, targetBones.position.z); -// const curve = new THREE.CatmullRomCurve3([ -// startPoint, -// startPoint.clone().setY(restPosition.y + 0.5), -// restPosition.clone(), -// ]); -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// } - -// logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position."); -// } - -// // Phase 2: Wait for trigger -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { -// logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction"); -// const timeoutId = setTimeout(() => { -// addCurrentAction(armBot.modelUuid, 'action-003'); -// }, 3000); -// return () => clearTimeout(timeoutId); -// } - -// // Phase 3: Rest -> Start Point -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("rest-to-start"); - -// const startPoint = armBot.point.actions[0].process.startPoint; -// if (startPoint) { -// const start = new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]); -// const curve = new THREE.CatmullRomCurve3([restPosition, restPosition.clone().setY(start.y), start]); -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// } - -// logStatus(armBot.modelUuid, "Moving armBot from rest point to start position."); -// } - -// // Phase 4: Start -> End (Pick & Move Object) -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("start-to-end"); - -// const startPoint = armBot.point.actions[0].process.startPoint; -// const endPoint = armBot.point.actions[0].process.endPoint; - -// if (startPoint && endPoint) { -// const path = generateArmPath(startPoint, endPoint, restPosition); -// logStatus(armBot.modelUuid, "Waiting to pick."); -// setTimeout(()=>{ -// setPath(path); -// },3000) -// } - -// logStatus(armBot.modelUuid, "Moving armBot from start point to end position."); -// } - -// // Phase 5: End -> Rest (Drop & Return) -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("end-to-rest"); - -// const endPoint = armBot.point.actions[0].process.endPoint; -// if (endPoint) { -// const start = new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]); -// const curve = new THREE.CatmullRomCurve3([start, start.clone().setY(restPosition.y), restPosition]); -// logStatus(armBot.modelUuid, "Waiting to drop."); -// setTimeout(()=>{ -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// },3000) - -// } - -// logStatus(armBot.modelUuid, "Moving armBot from end point to rest position."); -// } -// } else { -// logStatus(armBot.modelUuid, "Simulation Play Stopped"); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("init"); -// setPath([]); -// removeCurrentAction(armBot.modelUuid); -// } - -// }, [currentPhase, armBot, isPlaying, ikSolver, isReset]); - -// const HandleCallback = () => { -// if (armBot.isActive && armBot.state === "running" && currentPhase === "init-to-rest") { -// logStatus(armBot.modelUuid, "Callback triggered: rest"); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("rest"); -// setPath([]); -// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "rest-to-start") { -// logStatus(armBot.modelUuid, "Callback triggered: pick."); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("picking"); -// setPath([]); -// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "start-to-end") { -// logStatus(armBot.modelUuid, "Callback triggered: drop."); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("dropping"); -// setPath([]); -// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "end-to-rest") { -// logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed."); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("rest"); -// setPath([]); -// removeCurrentAction(armBot.modelUuid); -// } -// }; - -// const logStatus = (id: string, status: string) => { -// console.log( status); -// // console.log(`status [${id}]: ${status}`); -// }; - -// return ( -// <> -// -// -// -// ); -// } - -// export default RoboticArmInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index c40bc92..e692ca5 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -71,8 +71,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setSelectedArm(OOI.Target_Bone); - // scene.add(helper) - + scene.add(helper) }, [gltf]); diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx index a313278..0acc1d9 100644 --- a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -1,4 +1,3 @@ -import React from 'react' import RoboticArmInstance from './armInstance/roboticArmInstance'; import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; @@ -10,7 +9,6 @@ function RoboticArmInstances() { {armBots?.map((robot: ArmBotStatus) => ( ))} - ) } diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index 5a5bd38..e4f2355 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -2,24 +2,26 @@ import { useEffect } from "react"; import RoboticArmInstances from "./instances/roboticArmInstances"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; import { useFloorItems } from "../../../store/store"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; -import { useEventsStore } from "../../../store/simulation/useEventsStore"; +import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from "../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../store/simulation/useProductStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import ArmBotUI from "../ui/arm/armBotUI"; function RoboticArm() { const { armBots, addArmBot, removeArmBot } = useArmBotStore(); - const { addEvent } = useEventsStore(); + const { getProductById } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore(); const { floorItems } = useFloorItems(); + const armBotStatusSample: RoboticArmEventSchema[] = [ { state: "idle", - modelUuid: "3abf5d46-b59e-4e6b-9c02-a4634b64b82d", + modelUuid: "8790b4d5-fb6e-49e0-8161-04945fe3fdc4", modelName: "ArmBot-X200", - position: [0.20849215906958463, 0, 0.32079278127773675], + position: [4.317833205016363, 0, -3.2829924989068715], rotation: [-1.3768690876192207e-15, 1.4883085074751308, 1.5407776675834467e-15], type: "roboticArm", speed: 1.5, @@ -156,24 +158,36 @@ function RoboticArm() { ]; useEffect(() => { + if (selectedProduct.productId) { + const product = getProductById(selectedProduct.productId); + if (product) { + product.eventDatas.forEach(events => { + if (events.type === 'roboticArm') { + removeArmBot(events.modelUuid); + addArmBot(selectedProduct.productId, events); + } + }); + } + } + }, [selectedProduct]); - removeArmBot(armBotStatusSample[0].modelUuid); - addArmBot('123', armBotStatusSample[0]); - // addArmBot('123', armBotStatusSample[1]); - // addCurrentAction('armbot-xyz-001', 'action-001'); - }, []); + // useEffect(()=>{ + // // removeArmBot("123", armBotStatusSample[0]); + // addArmBot("123", armBotStatusSample[0]); + // },[]) + + useEffect(() => { + + }, [armBots]) useEffect(() => { - console.log('isPlaying: ', isPlaying); - console.log('selectedEventData: ', selectedEventData); - console.log('selectedEventSphere: ', selectedEventSphere); }, [selectedEventData, selectedEventSphere, isPlaying]); return ( <> - {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && !isPlaying && + {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && < ArmBotUI /> } diff --git a/app/src/modules/simulation/ui/arm/PickDropPoints.tsx b/app/src/modules/simulation/ui/arm/PickDropPoints.tsx index 4544a46..831da7f 100644 --- a/app/src/modules/simulation/ui/arm/PickDropPoints.tsx +++ b/app/src/modules/simulation/ui/arm/PickDropPoints.tsx @@ -9,7 +9,7 @@ interface PickDropProps { actionType: "pick" | "drop"; actionUuid: string; gltfScene: THREE.Group; - selectedPoint: THREE.Mesh | null; + handlePointerDown: (e: ThreeEvent) => void; isSelected: boolean; } @@ -21,10 +21,11 @@ const PickDropPoints: React.FC = ({ actionType, actionUuid, gltfScene, - selectedPoint, handlePointerDown, isSelected, }) => { + + const groupRef = useRef(null); return ( @@ -36,15 +37,24 @@ const PickDropPoints: React.FC = ({ : new THREE.Vector3(0, 0, 0) } onPointerDown={(e) => { - e.stopPropagation(); // Important to prevent event bubbling + + e.stopPropagation(); // Prevent event bubbling if (!isSelected) return; handlePointerDown(e); }} userData={{ modelUuid, pointUuid, actionType, actionUuid }} > { + const cloned = gltfScene.clone(); + cloned.traverse((child: any) => { + if (child.isMesh) { + child.userData = { modelUuid, pointUuid, actionType, actionUuid }; + } + }); + return cloned; + })()} + position={[0, 0, 0]} scale={[0.5, 0.5, 0.5]} /> diff --git a/app/src/modules/simulation/ui/arm/armBotUI.tsx b/app/src/modules/simulation/ui/arm/armBotUI.tsx index 7e111e3..d616206 100644 --- a/app/src/modules/simulation/ui/arm/armBotUI.tsx +++ b/app/src/modules/simulation/ui/arm/armBotUI.tsx @@ -1,155 +1,189 @@ -import React from 'react' -import PickDropPoints from './PickDropPoints'; -import { useSelectedEventData, useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; -import useModuleStore from '../../../../store/useModuleStore'; import { useGLTF } from '@react-three/drei'; +import { useThree } from '@react-three/fiber'; +import { useProductStore } from '../../../../store/simulation/useProductStore'; +import PickDropPoints from './PickDropPoints'; +import useDraggableGLTF from './useDraggableGLTF'; +import * as THREE from 'three'; + import armPick from "../../../../assets/gltf-glb/arm_ui_pick.glb"; import armDrop from "../../../../assets/gltf-glb/arm_ui_drop.glb"; -import useDraggableGLTF from './useDraggableGLTF'; -import * as THREE from "three"; -import { useThree } from '@react-three/fiber'; +import useModuleStore from '../../../../store/useModuleStore'; + +type Positions = { + pick: [number, number, number]; + drop: [number, number, number]; + default: [number, number, number]; +}; + const ArmBotUI = () => { - console.log("called"); - const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedEventData } = useSelectedEventData(); - const { armBots, updateArmBot } = useArmBotStore(); - const armUiPick = useGLTF(armPick) as any; - const { scene } = useThree(); - const armUiDrop = useGLTF(armDrop) as any; + const { getEventByModelUuid } = useProductStore(); + const { selectedEventData } = useSelectedEventData(); + const { selectedProduct } = useSelectedProduct(); + const { armBots, updateStartPoint, updateEndPoint } = useArmBotStore(); + const { scene } = useThree(); - const getDefaultPositions = (modelUuid: string) => { - const modelData = getModelByUuid(modelUuid); - if (modelData) { - - const baseX = modelData.position?.[0] || 0; - const baseY = 2.6; - const baseZ = modelData.position?.[2] || 0; - return { - pick: [baseX, baseY, baseZ + 0.4], - drop: [baseX, baseY, baseZ - 1.2], - default: [baseX, baseY, baseZ - 1.5], - }; + const armUiPick = useGLTF(armPick) as any; + const armUiDrop = useGLTF(armDrop) as any; + + const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 0, 0]); + const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 0, 0]); + const [selectedArmBotData, setSelectedArmBotData] = useState(null); + + // Fetch and setup selected ArmBot data + useEffect(() => { + if (selectedEventData?.data.type === "roboticArm") { + const selectedArmBot = getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid); + + if (selectedArmBot) { + if (selectedArmBot.type === "roboticArm") { + + setSelectedArmBotData(selectedArmBot); + + const defaultPositions = getDefaultPositions(selectedArmBot.modelUuid); + + selectedArmBot?.point?.actions?.forEach((action: any) => { + if (action.actionType === "pickAndPlace") { + const startPoint = action.process.startPoint; + const pickPosition = (!startPoint || (Array.isArray(startPoint) && startPoint.every(v => v === 0))) + ? defaultPositions.pick + : startPoint; + const endPoint = action.process.endPoint; + const dropPosition = (!endPoint || (Array.isArray(endPoint) && endPoint.every(v => v === 0))) + ? defaultPositions.drop + : endPoint; + setStartPosition(pickPosition); + setEndPosition(dropPosition) + + } + }); } - return { - pick: [0.5, 1.5, 0], - drop: [-0.5, 1.5, 0], - default: [0, 1.5, 0], - }; - }; - const getModelByUuid = (modelUuid: string) => { - try { - const modelsJson = localStorage.getItem("FloorItems"); - if (modelsJson) { - const models = JSON.parse(modelsJson); - return models.find((m: any) => m.modeluuid === modelUuid); - } - const storeModels = (useModuleStore.getState() as any).models || []; - return storeModels.find((m: any) => m.modelUuid === modelUuid); - } catch (error) { } - return null; - }; - const updatePointToState = (obj: THREE.Object3D) => { - const { modelUuid, pointUuid, actionType, actionUuid } = obj.userData; - - const newPosition = new THREE.Vector3(); - obj.getWorldPosition(newPosition); - const worldPositionArray = newPosition.toArray(); - - const armBot = armBots.find((a) => a.modelUuid === modelUuid); - if (!armBot) return; - - const updatedActions = armBot.point.actions.map((action: any) => { - if (action.actionUuid === actionUuid) { - const updatedProcess = { ...action.process }; - - if (actionType === "pick") { - updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray); - } - if (actionType === "drop") { - updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray); - } - return { - ...action, - process: updatedProcess, - }; - } - return action; - }); - - updateArmBot(modelUuid, { - point: { - ...armBot.point, - actions: updatedActions, - }, - }); - }; - function getLocalPosition(parentUuid: string, worldPosArray: [number, number, number] | null): [number, number, number] | null { - if (worldPosArray) { - const worldPos = new THREE.Vector3(...worldPosArray); - const localPos = worldPos.clone(); - - const parentObject = scene.getObjectByProperty('uuid', parentUuid); - if (parentObject) { - parentObject.worldToLocal(localPos); - - return [localPos.x, localPos.y, localPos.z]; - } else { - - } - } - return null; - } - const { handlePointerDown } = useDraggableGLTF(updatePointToState); - - return ( - <> - {armBots.map((event: any) => { - const defaultPositions = getDefaultPositions(event.modelUuid); - const isSelected = - selectedEventSphere?.userData?.modelUuid === event.modelUuid; - - return event.point.actions.map((action: any) => { - if (action.actionType === "pickAndPlace") { - const pickPosition = action.process.startPoint || defaultPositions.pick; - const dropPosition = action.process.endPoint || defaultPositions.drop; - - return ( - - {/* Pick Point */} - - - {/* Drop Point */} - - - ); - } - return null; - }); - })} - - ); - + } + } + }, [selectedEventData, selectedProduct, getEventByModelUuid]); -} -export default ArmBotUI \ No newline at end of file + function getDefaultPositions(modelUuid: string): Positions { + const modelData = getEventByModelUuid(selectedProduct.productId, modelUuid); + + if (modelData?.type === "roboticArm") { + const baseX = modelData.point.position?.[0] || 0; + const baseY = modelData.point.position?.[1] || 0;; + const baseZ = modelData.point.position?.[2] || 0; + return { + pick: [baseX, baseY , baseZ + 0.5], + drop: [baseX, baseY , baseZ - 0.5], + default: [baseX, baseY, baseZ], + }; + } + + return { + pick: [0.5, 1.5, 0], + drop: [-0.5, 1.5, 0], + default: [0, 1.5, 0], + }; + } + + function getLocalPosition(parentUuid: string, worldPosArray: [number, number, number] | null): [number, number, number] | null { + if (worldPosArray) { + const worldPos = new THREE.Vector3(...worldPosArray); + const parentObject = scene.getObjectByProperty('uuid', parentUuid); + + if (parentObject) { + const localPos = worldPos.clone(); + parentObject.worldToLocal(localPos); + return [localPos.x, localPos.y, localPos.z]; + } + } + return null; + } + + const updatePointToState = (obj: THREE.Object3D) => { + const { modelUuid, actionType, actionUuid } = obj.userData; + const newPosition = new THREE.Vector3(); + obj.getWorldPosition(newPosition); + const worldPositionArray = newPosition.toArray() as [number, number, number]; + + if (selectedEventData?.data.type === "roboticArm") { + const selectedArmBot = getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid); + + const armBot = selectedArmBot?.modelUuid === modelUuid ? selectedArmBot : null; + if (!armBot) return; + + if (armBot.type === "roboticArm") { + armBot?.point?.actions?.map((action: any) => { + if (action.actionUuid === actionUuid) { + const updatedProcess = { ...action.process }; + + if (actionType === "pick") { + updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray); + // updatedProcess.startPoint = worldPositionArray + setStartPosition(updatedProcess.startPoint) + + updateStartPoint(modelUuid, actionUuid, updatedProcess.startPoint); + } else if (actionType === "drop") { + updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray); + // updatedProcess.endPoint = worldPositionArray + setEndPosition(updatedProcess.endPoint) + updateEndPoint(modelUuid, actionUuid, updatedProcess.endPoint); + } + return { + ...action, + process: updatedProcess, + }; + } + return action; + }); + + } + } + } + + useEffect(() => { + + +}, [armBots]) + + const { handlePointerDown } = useDraggableGLTF(updatePointToState); + + if (!selectedArmBotData || !Array.isArray(selectedArmBotData.point?.actions)) { + return null; // avoid rendering if no data yet + } + + return ( + <> + {selectedArmBotData.point.actions.map((action: any) => ( + + + + + + + ))} + + ); +}; + +export default ArmBotUI; diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index d73b4a9..01b7932 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -1,11 +1,11 @@ -import { useRef } from "react"; +import { useRef, useState } from "react"; import * as THREE from "three"; import { ThreeEvent, useThree } from "@react-three/fiber"; type OnUpdateCallback = (object: THREE.Object3D) => void; export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { - const { camera, gl, controls, scene } = useThree(); + const { camera, gl, controls } = useThree(); const activeObjRef = useRef(null); const planeRef = useRef( new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) @@ -15,6 +15,7 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { const raycaster = new THREE.Raycaster(); const pointer = new THREE.Vector2(); + const [objectWorldPos, setObjectWorldPos] = useState(new THREE.Vector3()); const handlePointerDown = (e: ThreeEvent) => { e.stopPropagation(); @@ -33,10 +34,10 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { activeObjRef.current = obj; initialPositionRef.current.copy(obj.position); + console.log("obj.position: ", obj.position); // Get world position - const objectWorldPos = new THREE.Vector3(); - obj.getWorldPosition(objectWorldPos); + setObjectWorldPos(obj.getWorldPosition(objectWorldPos)); // Set plane at the object's Y level planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y); @@ -61,52 +62,56 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { const handlePointerMove = (e: PointerEvent) => { if (!activeObjRef.current) return; - + // Check if Shift key is pressed const isShiftKeyPressed = e.shiftKey; - + // Get the mouse position relative to the canvas const rect = gl.domElement.getBoundingClientRect(); pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; - + // Update raycaster to point to the mouse position raycaster.setFromCamera(pointer, camera); - + // Create a vector to store intersection point const intersection = new THREE.Vector3(); - const intersects = raycaster.ray.intersectPlane(planeRef.current, intersection); + const intersects = raycaster.ray.intersectPlane( + planeRef.current, + intersection + ); if (!intersects) return; - + // Add offset for dragging intersection.add(offsetRef.current); - - + // Get the parent's world matrix if exists const parent = activeObjRef.current.parent; const targetPosition = new THREE.Vector3(); - + + // OnPointerDown + initialPositionRef.current.copy(objectWorldPos); + + // OnPointerMove if (isShiftKeyPressed) { - console.log('isShiftKeyPressed: ', isShiftKeyPressed); - // For Y-axis only movement, maintain original X and Z - console.log('initialPositionRef: ', initialPositionRef); - console.log('intersection.y: ', intersection); - targetPosition.set( - initialPositionRef.current.x, - intersection.y, - initialPositionRef.current.z - ); + const { x: initialX, y: initialY } = initialPositionRef.current; + const { x: objectX, z: objectZ } = objectWorldPos; + + const deltaX = intersection.x - initialX; + + targetPosition.set(objectX, initialY + deltaX, objectZ); } else { // For free movement targetPosition.copy(intersection); } - + // Convert world position to local if object is nested inside a parent if (parent) { parent.worldToLocal(targetPosition); } - + // Update object position + activeObjRef.current.position.copy(targetPosition); }; @@ -126,6 +131,3 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { return { handlePointerDown }; } - - -