circular curve for arm movements added
This commit is contained in:
parent
c95f140d30
commit
ff9bb8f566
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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: [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue