armbot circular path updated and ui dynamic updation

This commit is contained in:
Gomathi 2025-05-02 17:35:52 +05:30
parent 764e235a5f
commit d83b934e61
10 changed files with 438 additions and 698 deletions

View File

@ -206,7 +206,7 @@ function processLoadedModel(
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 10,
steeringAngle:0,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: []

View File

@ -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 (
<group key={i} position={[...event.position]} rotation={[...event.rotation]} >
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
{event.points.map((point, j) => (
<mesh
name="Event-Sphere"
@ -94,14 +100,16 @@ function PointsCreator() {
}}
onPointerMissed={() => {
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,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="orange" />
@ -111,7 +119,10 @@ function PointsCreator() {
);
} else if (event.type === "vehicle") {
return (
<group key={i} position={[...event.position]} rotation={[...event.rotation]} >
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
@ -127,8 +138,10 @@ function PointsCreator() {
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,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="blue" />
@ -137,7 +150,10 @@ function PointsCreator() {
);
} else if (event.type === "roboticArm") {
return (
<group key={i} position={[...event.position]} rotation={[...event.rotation]} >
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
@ -149,12 +165,14 @@ function PointsCreator() {
);
}}
onPointerMissed={() => {
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,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="green" />
@ -163,7 +181,10 @@ function PointsCreator() {
);
} else if (event.type === "machine") {
return (
<group key={i} position={[...event.position]} rotation={[...event.rotation]} >
<group
key={i}
position={new THREE.Vector3(...event.position)}
>
<mesh
name="Event-Sphere"
uuid={event.point.uuid}
@ -175,12 +196,14 @@ function PointsCreator() {
);
}}
onPointerMissed={() => {
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,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="purple" />
@ -208,4 +231,4 @@ function PointsCreator() {
);
}
export default PointsCreator;
export default PointsCreator;

View File

@ -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<THREE.Vector3[] | null>(null);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const [curvePoints, setCurvePoints] = useState<THREE.Vector3[] | null>(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<THREE.Vector3[] | null>(null);
const stepSegmentsRef = useRef<THREE.Vector3[][]>([]);
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 && (
<mesh rotation={armBot.rotation}>
<Line points={customCurvePoints.map(p => [p.x, p.y, p.z])} color="green" lineWidth={5} dashed={false} />
{/* <Line points={currentPath} color="green" lineWidth={5} dashed={false} /> */}
{currentPath.length >= 1 && (
<>
{/* First point */}
{/* <mesh position={nearestStartPoint || currentPath[0]} rotation={armBot.rotation}>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh> */}
{/* Last point */}
{/* <mesh position={nearestEndPoint || currentPath[currentPath.length - 1]} rotation={armBot.rotation}>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh> */}
</>
)}
{customCurvePoints && currentPath && isPlaying && (
<mesh rotation={armBot.rotation} position={armBot.position}>
<Line
points={customCurvePoints.map((p) => [p.x, p.y, p.z] as [number, number, number])}
color="green"
lineWidth={5}
dashed={false}
/>
</mesh>
)}
<mesh position={[armBot.position[0], armBot.position[1] + 1.5, armBot.position[2]]} rotation={[-Math.PI / 2, 0, 0]}>

View File

@ -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 }) {
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} armBot={armBot} groupRef={groupRef} />
<RoboticArmAnimator HandleCallback={HandleCallback} restPosition={restPosition} ikSolver={ikSolver} targetBone={targetBone} armBot={armBot}
logStatus={logStatus} path={path}currentPhase={currentPhase} />
</>
)
}
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<string>("init");
// const [path, setPath] = useState<[number, number, number][]>([]);
// const [ikSolver, setIkSolver] = useState<any>(null);
// const { scene } = useThree();
// const restPosition = new THREE.Vector3(0, 1, -1.6);
// const targetBone = "Target";
// const groupRef = useRef<any>(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 (
// <>
// <IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} armBot={armBot} groupRef={groupRef} />
// <RoboticArmAnimator
// HandleCallback={HandleCallback}
// restPosition={restPosition}
// ikSolver={ikSolver}
// targetBone={targetBone}
// armBot={armBot}
// logStatus={logStatus}
// path={path}
// />
// </>
// );
// }
// export default RoboticArmInstance;

View File

@ -71,8 +71,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns
setSelectedArm(OOI.Target_Bone);
// scene.add(helper)
scene.add(helper)
}, [gltf]);

View File

@ -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) => (
<RoboticArmInstance key={robot.modelUuid} armBot={robot} />
))}
</>
)
}

View File

@ -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 (
<>
<RoboticArmInstances />
{selectedEventSphere && selectedEventData?.data.type === "roboticArm" && !isPlaying &&
{selectedEventSphere && selectedEventData?.data.type === "roboticArm" &&
< ArmBotUI />
}
</>

View File

@ -9,7 +9,7 @@ interface PickDropProps {
actionType: "pick" | "drop";
actionUuid: string;
gltfScene: THREE.Group;
selectedPoint: THREE.Mesh | null;
handlePointerDown: (e: ThreeEvent<PointerEvent>) => void;
isSelected: boolean;
}
@ -21,10 +21,11 @@ const PickDropPoints: React.FC<PickDropProps> = ({
actionType,
actionUuid,
gltfScene,
selectedPoint,
handlePointerDown,
isSelected,
}) => {
const groupRef = useRef<THREE.Group>(null);
return (
@ -36,15 +37,24 @@ const PickDropPoints: React.FC<PickDropProps> = ({
: 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 }}
>
<primitive
object={gltfScene.clone()}
position={[0, 0, 0]} // Ensure this stays at origin
object={(() => {
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]}
/>
</group>

View File

@ -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<any>(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 (
<React.Fragment key={action.actionUuid}>
{/* Pick Point */}
<PickDropPoints
position={pickPosition}
modelUuid={event.modelUuid}
pointUuid={event.point.uuid}
actionType="pick"
actionUuid={action.actionUuid}
gltfScene={armUiPick.scene}
selectedPoint={selectedEventSphere}
handlePointerDown={handlePointerDown}
isSelected={isSelected}
/>
{/* Drop Point */}
<PickDropPoints
position={dropPosition}
modelUuid={event.modelUuid}
pointUuid={event.point.uuid}
actionType="drop"
actionUuid={action.actionUuid}
gltfScene={armUiDrop.scene}
selectedPoint={selectedEventSphere}
handlePointerDown={handlePointerDown}
isSelected={isSelected}
/>
</React.Fragment>
);
}
return null;
});
})}
</>
);
}
}
}, [selectedEventData, selectedProduct, getEventByModelUuid]);
}
export default ArmBotUI
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) => (
<React.Fragment key={action.actionUuid}>
<group
position={new THREE.Vector3(...selectedArmBotData.position)}
rotation={new THREE.Euler(...selectedArmBotData.rotation)}
>
<PickDropPoints
position={startPosition}
modelUuid={selectedArmBotData.modelUuid}
pointUuid={selectedArmBotData.point.uuid}
actionType="pick"
actionUuid={action.actionUuid}
gltfScene={armUiPick.scene}
handlePointerDown={handlePointerDown}
isSelected={true}
/>
<PickDropPoints
position={endPosition}
modelUuid={selectedArmBotData.modelUuid}
pointUuid={selectedArmBotData.point.uuid}
actionType="drop"
actionUuid={action.actionUuid}
gltfScene={armUiDrop.scene}
handlePointerDown={handlePointerDown}
isSelected={true}
/>
</group>
</React.Fragment>
))}
</>
);
};
export default ArmBotUI;

View File

@ -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<THREE.Object3D | null>(null);
const planeRef = useRef<THREE.Plane>(
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<PointerEvent>) => {
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 };
}