circular curve for arm movements added

This commit is contained in:
Gomathi 2025-04-30 15:27:02 +05:30
parent c95f140d30
commit ff9bb8f566
3 changed files with 201 additions and 74 deletions

View File

@ -1,13 +1,13 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { useFrame } from '@react-three/fiber'; import { useFrame } from '@react-three/fiber';
import * as THREE from 'three'; import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { import {
useAnimationPlaySpeed, useAnimationPlaySpeed,
usePauseButtonStore, usePauseButtonStore,
usePlayButtonStore, usePlayButtonStore,
useResetButtonStore useResetButtonStore
} from '../../../../../store/usePlayButtonStore'; } from '../../../../../store/usePlayButtonStore';
import { Line } from '@react-three/drei';
function RoboticArmAnimator({ function RoboticArmAnimator({
HandleCallback, HandleCallback,
@ -22,24 +22,35 @@ function RoboticArmAnimator({
const curveRef = useRef<THREE.Vector3[] | null>(null); const curveRef = useRef<THREE.Vector3[] | null>(null);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const [curvePoints, setCurvePoints] = useState<THREE.Vector3[] | null>(null); 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 // Zustand stores
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
const { isReset } = useResetButtonStore(); // optional use depending on your logic const { isReset } = useResetButtonStore();
const { speed } = useAnimationPlaySpeed(); const { speed } = useAnimationPlaySpeed();
// Update path state whenever `path` prop changes
useEffect(() => { useEffect(() => {
setCurrentPath(path) setCurrentPath(path);
}, [path]) }, [path]);
// Reset logic when `isPlaying` changes
useEffect(() => { useEffect(() => {
if (!isPlaying) { if (!isPlaying) {
setCurrentPath([]) setCurrentPath([]);
curveRef.current = null curveRef.current = null;
} }
}, [isPlaying]) }, [isPlaying]);
// Handle path generation (including CatmullRomCurve3)
useEffect(() => { useEffect(() => {
if (currentPath && currentPath.length === 3) { if (currentPath && currentPath.length === 3) {
const [start, mid, end] = currentPath; const [start, mid, end] = currentPath;
@ -51,18 +62,152 @@ function RoboticArmAnimator({
new THREE.Vector3(end[0], end[1], end[2]) new THREE.Vector3(end[0], end[1], end[2])
]; ];
const generatedCurve = new THREE.CatmullRomCurve3(points, false, 'centripetal', 1).getPoints(100); const generatedCurve = new THREE.CatmullRomCurve3(points, false, 'centripetal', 1).getPoints(100);
// console.log('generatedCurve: ', generatedCurve);
curveRef.current = generatedCurve; curveRef.current = generatedCurve;
setCurvePoints(generatedCurve); setCurvePoints(generatedCurve);
} }
}, [currentPath]); }, [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]);
}
setCirclePoints(points);
}, [armBot.position]);
// 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 points = [
new THREE.Vector3(start[0], start[1], start[2]),
new THREE.Vector3(start[0], start[1] + 0.5, start[2]),
new THREE.Vector3(start[0], start[1] + 0.5, start[2]),
new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]),
new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]),
new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]),
new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]),
new THREE.Vector3(end[0], end[1] + 0.5, end[2]),
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 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])
];
const customCurve = new THREE.CatmullRomCurve3(finalPath, false, 'centripetal', 1);
const customCurveGeneratedPoints = customCurve.getPoints(100);
setCustomCurvePoints(customCurveGeneratedPoints);
}
}, [circlePoints, currentPath]);
// Frame update for animation
useFrame((_, delta) => { useFrame((_, delta) => {
if (ikSolver && curveRef.current && currentPath.length >= 0) { if (ikSolver && curveRef.current && currentPath.length >= 0) {
const bone = ikSolver.mesh.skeleton.bones.find( const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
(b: any) => b.name === targetBone
);
if (!bone) return; if (!bone) return;
if (isPlaying) { if (isPlaying) {
@ -70,71 +215,62 @@ function RoboticArmAnimator({
const curvePoints = curveRef.current; const curvePoints = curveRef.current;
const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); const speedAdjustedProgress = progressRef.current + (speed * armBot.speed);
const index = Math.floor(speedAdjustedProgress); const index = Math.floor(speedAdjustedProgress);
if (index >= curvePoints.length) {
HandleCallback(); if (curvePoints) {
setCurrentPath([]);
curveRef.current = null; if (index >= curvePoints.length) {
progressRef.current = 0; HandleCallback();
} else { setCurrentPath([]);
const point = curvePoints[index]; curveRef.current = null;
bone.position.copy(point); progressRef.current = 0;
progressRef.current = speedAdjustedProgress; } else {
const point = curvePoints[index];
bone.position.copy(point);
progressRef.current = speedAdjustedProgress;
}
} }
} else {
logStatus(armBot.modelUuid, 'Simulation Play Exited');
} }
} else {
logStatus(armBot.modelUuid, 'Simulation Play Exited'); ikSolver.update();
// bone.position.copy(restPosition);
} }
ikSolver.update();
} else if (ikSolver && !isPlaying && currentPath.length === 0) { } else if (ikSolver && !isPlaying && currentPath.length === 0) {
const bone = ikSolver.mesh.skeleton.bones.find( const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
(b: any) => b.name === targetBone bone.position.copy(restPosition);
);
// bone.position.copy(restPosition);
ikSolver.update(); ikSolver.update();
}; }
}); });
return ( return (
<> <>
{/* {currentPath.length > 0 && ( {customCurvePoints && currentPath && (
<mesh rotation={armBot.rotation}> <mesh rotation={armBot.rotation}>
<Line points={currentPath} color="green" lineWidth={3} /> <Line points={customCurvePoints.map(p => [p.x, p.y, p.z])} color="green" lineWidth={5} dashed={false} />
{currentPath.map((point, index) => ( {/* <Line points={currentPath} color="green" lineWidth={5} dashed={false} /> */}
<mesh key={index} position={point} rotation={armBot.rotation}>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
))}
</mesh>
)} */}
{curvePoints && (
<mesh rotation={armBot.rotation}>
<Line
points={curvePoints.map(p => [p.x, p.y, p.z])}
color="green"
lineWidth={5}
dashed={false}
/>
{currentPath.length >= 1 && ( {currentPath.length >= 1 && (
<> <>
{/* First point */} {/* First point */}
<mesh position={currentPath[0]} rotation={armBot.rotation}> {/* <mesh position={nearestStartPoint || currentPath[0]} rotation={armBot.rotation}>
<sphereGeometry args={[0.1, 16, 16]} /> <sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" /> <meshStandardMaterial color="red" />
</mesh> </mesh> */}
{/* Last point */} {/* Last point */}
<mesh position={currentPath[currentPath.length - 1]} rotation={armBot.rotation}> {/* <mesh position={nearestEndPoint || currentPath[currentPath.length - 1]} rotation={armBot.rotation}>
<sphereGeometry args={[0.1, 16, 16]} /> <sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" /> <meshStandardMaterial color="red" />
</mesh> </mesh> */}
</> </>
)} )}
</mesh> </mesh>
)} )}
<mesh position={[armBot.position[0], armBot.position[1] + 1.5, armBot.position[2]]} rotation={[-Math.PI / 2, 0, 0]}>
<ringGeometry args={[1.59, 1.61, 64]} />
<meshBasicMaterial color="green" side={THREE.DoubleSide} />
</mesh>
</> </>
); );
} }

View File

@ -67,21 +67,20 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns
setIkSolver(solver); setIkSolver(solver);
const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05) const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05)
groupRef.current.add(helper); // groupRef.current.add(helper);
console.log('OOI.Target_Bone: ', OOI.Target_Bone);
setSelectedArm(OOI.Target_Bone); setSelectedArm(OOI.Target_Bone);
scene.add(helper) // scene.add(helper)
}, [gltf]); }, [gltf]);
return ( return (
<> <>
<group ref={groupRef} position={armBot.position} rotation={armBot.rotation} onClick={() =>{ <group ref={groupRef} position={armBot.position} rotation={armBot.rotation} onClick={() => {
setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) setSelectedArm(groupRef.current?.getObjectByName(targetBoneName))
} }}>
}>
<primitive <primitive
uuid={"ArmBot-X200"} uuid={"ArmBot-X200"}
object={cloned} object={cloned}

View File

@ -27,19 +27,9 @@ function RoboticArm() {
process: { process: {
startPoint: [-1, 2, 1], startPoint: [-1, 2, 1],
endPoint: [-2, 1, -1], endPoint: [-2, 1, -1],
// startPoint: [-2, 1, -1],
// endPoint: [-1, 2, 1],
}, },
// process: {
// "startPoint": [
// 0.37114476008711866,
// 1.9999999999999998,
// 1.8418816116721384
// ],
// "endPoint": [
// -0.42197069459490777,
// 1,
// -3.159515927851809
// ]
// },
triggers: [ triggers: [
{ {
triggerUuid: "trigger-001", triggerUuid: "trigger-001",
@ -104,8 +94,10 @@ function RoboticArm() {
actionName: "Pick Component", actionName: "Pick Component",
actionType: "pickAndPlace", actionType: "pickAndPlace",
process: { process: {
startPoint: [2.52543010919071, 0, 8.433681161200905], // startPoint: [2.52543010919071, 0, 8.433681161200905],
endPoint: [95.3438373267953, 0, 9.0279187421610025], // endPoint: [95.3438373267953, 0, 9.0279187421610025],
startPoint: null,
endPoint: null,
}, },
triggers: [ triggers: [
{ {