|
|
|
@ -3,240 +3,260 @@ import { useFrame } from '@react-three/fiber';
|
|
|
|
|
import * as THREE from 'three';
|
|
|
|
|
import { Line } from '@react-three/drei';
|
|
|
|
|
import {
|
|
|
|
|
useAnimationPlaySpeed,
|
|
|
|
|
usePauseButtonStore,
|
|
|
|
|
usePlayButtonStore,
|
|
|
|
|
useResetButtonStore
|
|
|
|
|
useAnimationPlaySpeed,
|
|
|
|
|
usePauseButtonStore,
|
|
|
|
|
usePlayButtonStore,
|
|
|
|
|
useResetButtonStore
|
|
|
|
|
} from '../../../../../store/usePlayButtonStore';
|
|
|
|
|
|
|
|
|
|
function RoboticArmAnimator({
|
|
|
|
|
HandleCallback,
|
|
|
|
|
restPosition,
|
|
|
|
|
ikSolver,
|
|
|
|
|
targetBone,
|
|
|
|
|
armBot,
|
|
|
|
|
logStatus,
|
|
|
|
|
path
|
|
|
|
|
HandleCallback,
|
|
|
|
|
restPosition,
|
|
|
|
|
ikSolver,
|
|
|
|
|
targetBone,
|
|
|
|
|
armBot,
|
|
|
|
|
logStatus,
|
|
|
|
|
path
|
|
|
|
|
}: any) {
|
|
|
|
|
const progressRef = useRef(0);
|
|
|
|
|
const curveRef = useRef<THREE.Vector3[] | null>(null);
|
|
|
|
|
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
|
|
|
|
const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]);
|
|
|
|
|
const [customCurvePoints, setCustomCurvePoints] = useState<THREE.Vector3[] | null>(null);
|
|
|
|
|
let curveHeight = 1.75
|
|
|
|
|
const totalDistanceRef = useRef(0);
|
|
|
|
|
const startTimeRef = useRef<number | null>(null);
|
|
|
|
|
const segmentDistancesRef = useRef<number[]>([]);
|
|
|
|
|
// Zustand stores
|
|
|
|
|
const { isPlaying } = usePlayButtonStore();
|
|
|
|
|
const { isPaused } = usePauseButtonStore();
|
|
|
|
|
const { isReset, setReset } = useResetButtonStore();
|
|
|
|
|
const { speed } = useAnimationPlaySpeed();
|
|
|
|
|
const progressRef = useRef(0);
|
|
|
|
|
const curveRef = useRef<THREE.Vector3[] | null>(null);
|
|
|
|
|
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
|
|
|
|
const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]);
|
|
|
|
|
const [customCurvePoints, setCustomCurvePoints] = useState<THREE.Vector3[] | null>(null);
|
|
|
|
|
let curveHeight = 1.75
|
|
|
|
|
const totalDistanceRef = useRef(0);
|
|
|
|
|
const startTimeRef = useRef<number | null>(null);
|
|
|
|
|
const segmentDistancesRef = useRef<number[]>([]);
|
|
|
|
|
// Zustand stores
|
|
|
|
|
const { isPlaying } = usePlayButtonStore();
|
|
|
|
|
const { isPaused } = usePauseButtonStore();
|
|
|
|
|
const { isReset, setReset } = useResetButtonStore();
|
|
|
|
|
const { speed } = useAnimationPlaySpeed();
|
|
|
|
|
|
|
|
|
|
// Update path state whenever `path` prop changes
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setCurrentPath(path);
|
|
|
|
|
}, [path]);
|
|
|
|
|
// Update path state whenever `path` prop changes
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setCurrentPath(path);
|
|
|
|
|
}, [path]);
|
|
|
|
|
|
|
|
|
|
// Handle circle points based on armBot position
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const points = generateRingPoints(1.6, 64)
|
|
|
|
|
setCirclePoints(points);
|
|
|
|
|
}, [armBot.position]);
|
|
|
|
|
// Handle circle points based on armBot position
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const points = generateRingPoints(1.6, 64)
|
|
|
|
|
setCirclePoints(points);
|
|
|
|
|
}, [armBot.position]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isReset || !isPlaying) {
|
|
|
|
|
progressRef.current = 0;
|
|
|
|
|
curveRef.current = null;
|
|
|
|
|
setCurrentPath([]);
|
|
|
|
|
setCustomCurvePoints(null);
|
|
|
|
|
totalDistanceRef.current = 0;
|
|
|
|
|
startTimeRef.current = null;
|
|
|
|
|
segmentDistancesRef.current = [];
|
|
|
|
|
setReset(false);
|
|
|
|
|
//Handle Reset Animation
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isReset ) {
|
|
|
|
|
progressRef.current = 0;
|
|
|
|
|
curveRef.current = null;
|
|
|
|
|
setCurrentPath([]);
|
|
|
|
|
setCustomCurvePoints(null);
|
|
|
|
|
totalDistanceRef.current = 0;
|
|
|
|
|
startTimeRef.current = null;
|
|
|
|
|
segmentDistancesRef.current = [];
|
|
|
|
|
if (!ikSolver) return
|
|
|
|
|
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
|
|
|
|
if (!bone) return;
|
|
|
|
|
bone.position.copy(restPosition)
|
|
|
|
|
ikSolver.update();
|
|
|
|
|
}
|
|
|
|
|
}, [isReset, isPlaying])
|
|
|
|
|
|
|
|
|
|
//Generate Circle Points
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}, [isReset, isPlaying])
|
|
|
|
|
// Function for find nearest Circlepoints Index
|
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 start = currentPath[0];
|
|
|
|
|
const end = currentPath[currentPath.length - 1];
|
|
|
|
|
|
|
|
|
|
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 findNearest = (target: [number, number, number]) => {
|
|
|
|
|
//function to find nearest Circlepoints
|
|
|
|
|
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;
|
|
|
|
|
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]);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handle nearest points and final path (including arc points)
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (circlePoints.length > 0 && currentPath.length > 0) {
|
|
|
|
|
|
|
|
|
|
const nearestToStart = findNearest(raisedStart);
|
|
|
|
|
const nearestToEnd = findNearest(raisedEnd);
|
|
|
|
|
const start = currentPath[0];
|
|
|
|
|
const end = currentPath[currentPath.length - 1];
|
|
|
|
|
|
|
|
|
|
const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints);
|
|
|
|
|
const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints);
|
|
|
|
|
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 clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64;
|
|
|
|
|
const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64;
|
|
|
|
|
const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance;
|
|
|
|
|
// 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]);
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
let arcPoints: [number, number, number][] = [];
|
|
|
|
|
const nearestToStart = findNearest(raisedStart);
|
|
|
|
|
const nearestToEnd = findNearest(raisedEnd);
|
|
|
|
|
|
|
|
|
|
const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints);
|
|
|
|
|
const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints);
|
|
|
|
|
|
|
|
|
|
const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64;
|
|
|
|
|
const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64;
|
|
|
|
|
const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance;
|
|
|
|
|
|
|
|
|
|
let arcPoints: [number, number, number][] = [];
|
|
|
|
|
|
|
|
|
|
if (clockwiseIsShorter) {
|
|
|
|
|
if (indexOfNearestStart <= indexOfNearestEnd) {
|
|
|
|
|
arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1);
|
|
|
|
|
} else {
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const pathVectors = [
|
|
|
|
|
new THREE.Vector3(start[0], start[1], start[2]),
|
|
|
|
|
new THREE.Vector3(start[0], curveHeight, start[2]),
|
|
|
|
|
new THREE.Vector3(nearestToStart[0], curveHeight, nearestToStart[2]),
|
|
|
|
|
...arcPoints.map(point => new THREE.Vector3(point[0], curveHeight, point[2])),
|
|
|
|
|
new THREE.Vector3(nearestToEnd[0], curveHeight, nearestToEnd[2]),
|
|
|
|
|
new THREE.Vector3(end[0], curveHeight, end[2]),
|
|
|
|
|
new THREE.Vector3(end[0], end[1], end[2])
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const pathSegments: [THREE.Vector3, THREE.Vector3][] = [];
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < pathVectors.length - 1; i++) {
|
|
|
|
|
pathSegments.push([pathVectors[i], pathVectors[i + 1]]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const segmentDistances = pathSegments.map(([p1, p2]) => p1.distanceTo(p2));
|
|
|
|
|
segmentDistancesRef.current = segmentDistances;
|
|
|
|
|
const totalDistance = segmentDistances.reduce((sum, d) => sum + d, 0);
|
|
|
|
|
totalDistanceRef.current = totalDistance;
|
|
|
|
|
|
|
|
|
|
const movementSpeed = speed * armBot.speed;
|
|
|
|
|
const totalMoveTime = totalDistance / movementSpeed;
|
|
|
|
|
|
|
|
|
|
const segmentTimes = segmentDistances.map(distance => (distance / totalDistance) * totalMoveTime);
|
|
|
|
|
|
|
|
|
|
setCustomCurvePoints(pathVectors);
|
|
|
|
|
|
|
|
|
|
if (clockwiseIsShorter) {
|
|
|
|
|
if (indexOfNearestStart <= indexOfNearestEnd) {
|
|
|
|
|
arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1);
|
|
|
|
|
} else {
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}, [circlePoints, currentPath]);
|
|
|
|
|
|
|
|
|
|
// Frame update for animation
|
|
|
|
|
useFrame((state, delta) => {
|
|
|
|
|
if (!ikSolver) return;
|
|
|
|
|
|
|
|
|
|
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
|
|
|
|
if (!bone) return;
|
|
|
|
|
|
|
|
|
|
if (isPlaying) {
|
|
|
|
|
if (isReset) {
|
|
|
|
|
bone.position.copy(restPosition);
|
|
|
|
|
setCustomCurvePoints([]);
|
|
|
|
|
ikSolver.update();
|
|
|
|
|
}
|
|
|
|
|
if (!isPaused && customCurvePoints && customCurvePoints.length > 0) {
|
|
|
|
|
const distances = segmentDistancesRef.current;
|
|
|
|
|
const totalDistance = totalDistanceRef.current;
|
|
|
|
|
|
|
|
|
|
progressRef.current += delta * (speed * armBot.speed);
|
|
|
|
|
const coveredDistance = progressRef.current;
|
|
|
|
|
|
|
|
|
|
let index = 0;
|
|
|
|
|
let accumulatedDistance = 0;
|
|
|
|
|
|
|
|
|
|
while (index < distances.length && coveredDistance > accumulatedDistance + distances[index]) {
|
|
|
|
|
accumulatedDistance += distances[index];
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index < distances.length) {
|
|
|
|
|
const startPoint = customCurvePoints[index];
|
|
|
|
|
const endPoint = customCurvePoints[index + 1];
|
|
|
|
|
const segmentDistance = distances[index];
|
|
|
|
|
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
|
|
|
|
|
if (startPoint && endPoint) {
|
|
|
|
|
const position = startPoint.clone().lerp(endPoint, t);
|
|
|
|
|
bone.position.copy(position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progressRef.current >= totalDistance) {
|
|
|
|
|
HandleCallback();
|
|
|
|
|
setCurrentPath([]);
|
|
|
|
|
setCustomCurvePoints([]);
|
|
|
|
|
curveRef.current = null;
|
|
|
|
|
progressRef.current = 0;
|
|
|
|
|
startTimeRef.current = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ikSolver.update();
|
|
|
|
|
}
|
|
|
|
|
} else if (!isPlaying && currentPath.length === 0) {
|
|
|
|
|
bone.position.copy(restPosition);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const pathVectors = [
|
|
|
|
|
new THREE.Vector3(start[0], start[1], start[2]),
|
|
|
|
|
new THREE.Vector3(start[0], curveHeight, start[2]),
|
|
|
|
|
new THREE.Vector3(nearestToStart[0], curveHeight, nearestToStart[2]),
|
|
|
|
|
...arcPoints.map(point => new THREE.Vector3(point[0], curveHeight, point[2])),
|
|
|
|
|
new THREE.Vector3(nearestToEnd[0], curveHeight, nearestToEnd[2]),
|
|
|
|
|
new THREE.Vector3(end[0], curveHeight, end[2]),
|
|
|
|
|
new THREE.Vector3(end[0], end[1], end[2])
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const pathSegments: [THREE.Vector3, THREE.Vector3][] = [];
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < pathVectors.length - 1; i++) {
|
|
|
|
|
pathSegments.push([pathVectors[i], pathVectors[i + 1]]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const segmentDistances = pathSegments.map(([p1, p2]) => p1.distanceTo(p2));
|
|
|
|
|
segmentDistancesRef.current = segmentDistances;
|
|
|
|
|
const totalDistance = segmentDistances.reduce((sum, d) => sum + d, 0);
|
|
|
|
|
totalDistanceRef.current = totalDistance;
|
|
|
|
|
|
|
|
|
|
const movementSpeed = speed * armBot.speed;
|
|
|
|
|
const totalMoveTime = totalDistance / movementSpeed;
|
|
|
|
|
|
|
|
|
|
const segmentTimes = segmentDistances.map(distance => (distance / totalDistance) * totalMoveTime);
|
|
|
|
|
|
|
|
|
|
setCustomCurvePoints(pathVectors);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}, [circlePoints, currentPath]);
|
|
|
|
|
|
|
|
|
|
// Frame update for animation
|
|
|
|
|
useFrame((state, delta) => {
|
|
|
|
|
if (!ikSolver) return;
|
|
|
|
|
|
|
|
|
|
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
|
|
|
|
if (!bone) return;
|
|
|
|
|
|
|
|
|
|
if (isPlaying) {
|
|
|
|
|
if (!isPaused && customCurvePoints && customCurvePoints.length > 0) {
|
|
|
|
|
const distances = segmentDistancesRef.current; // distances between each pair of points
|
|
|
|
|
const totalDistance = totalDistanceRef.current;
|
|
|
|
|
|
|
|
|
|
progressRef.current += delta * (speed * armBot.speed);
|
|
|
|
|
const coveredDistance = progressRef.current;
|
|
|
|
|
|
|
|
|
|
let index = 0;
|
|
|
|
|
let accumulatedDistance = 0;
|
|
|
|
|
|
|
|
|
|
// Find which segment we are currently in
|
|
|
|
|
while (index < distances.length && coveredDistance > accumulatedDistance + distances[index]) {
|
|
|
|
|
accumulatedDistance += distances[index];
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
if (index < distances.length) {
|
|
|
|
|
const startPoint = customCurvePoints[index];
|
|
|
|
|
const endPoint = customCurvePoints[index + 1];
|
|
|
|
|
const segmentDistance = distances[index];
|
|
|
|
|
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
|
|
|
|
|
if (startPoint && endPoint) {
|
|
|
|
|
const position = startPoint.clone().lerp(endPoint, t);
|
|
|
|
|
bone.position.copy(position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (progressRef.current >= totalDistance) {
|
|
|
|
|
HandleCallback();
|
|
|
|
|
setCurrentPath([]);
|
|
|
|
|
setCustomCurvePoints([]);
|
|
|
|
|
curveRef.current = null;
|
|
|
|
|
progressRef.current = 0;
|
|
|
|
|
startTimeRef.current = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ikSolver.update();
|
|
|
|
|
}
|
|
|
|
|
} else if ((!isPlaying && currentPath.length === 0) || isReset) {
|
|
|
|
|
bone.position.copy(restPosition);
|
|
|
|
|
ikSolver.update();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{customCurvePoints && customCurvePoints?.length >= 2 && 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]}>
|
|
|
|
|
<ringGeometry args={[1.59, 1.61, 64]} />
|
|
|
|
|
<meshBasicMaterial color="green" side={THREE.DoubleSide} />
|
|
|
|
|
</mesh>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{customCurvePoints && customCurvePoints?.length >= 2 && 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]}>
|
|
|
|
|
<ringGeometry args={[1.59, 1.61, 64]} />
|
|
|
|
|
<meshBasicMaterial color="green" side={THREE.DoubleSide} />
|
|
|
|
|
</mesh>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default RoboticArmAnimator;
|