From 0b694944654383ce87499bd56e89cc7833d09f81 Mon Sep 17 00:00:00 2001
From: Gomathi9520 <gomathi@hexrfactory.com>
Date: Sat, 3 May 2025 19:11:24 +0530
Subject: [PATCH] arm points constraints defined and armbot movements bug
 cleared

---
 .../components/icons/ExportCommonIcons.tsx    |   2 +-
 app/src/components/icons/analysis.tsx         |   2 +-
 .../instances/animator/roboticArmAnimator.tsx | 138 ++++++++++--------
 .../armInstance/roboticArmInstance.tsx        |  58 ++++----
 .../instances/ikInstance/ikInstance.tsx       |   2 +-
 app/src/modules/simulation/simulation.tsx     |   2 +-
 .../simulation/ui/arm/useDraggableGLTF.ts     | 117 +++++++++------
 7 files changed, 189 insertions(+), 132 deletions(-)

diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx
index 4137498..b4ec2cf 100644
--- a/app/src/components/icons/ExportCommonIcons.tsx
+++ b/app/src/components/icons/ExportCommonIcons.tsx
@@ -844,7 +844,7 @@ export const LogTickIcon = () => {
       fill="none"
       xmlns="http://www.w3.org/2000/svg"
     >
-      <g clip-path="url(#clip0_3962_4223)">
+      <g clipPath="url(#clip0_3962_4223)">
         <path
           d="M5.00065 0.835938C2.70482 0.835938 0.833984 2.70677 0.833984 5.0026C0.833984 7.29844 2.70482 9.16927 5.00065 9.16927C7.29648 9.16927 9.16732 7.29844 9.16732 5.0026C9.16732 2.70677 7.29648 0.835938 5.00065 0.835938ZM6.99232 4.04427L4.62982 6.40677C4.57148 6.4651 4.49232 6.49844 4.40898 6.49844C4.32565 6.49844 4.24648 6.4651 4.18815 6.40677L3.00898 5.2276C2.88815 5.10677 2.88815 4.90677 3.00898 4.78594C3.12982 4.6651 3.32982 4.6651 3.45065 4.78594L4.40898 5.74427L6.55065 3.6026C6.67148 3.48177 6.87148 3.48177 6.99232 3.6026C7.11315 3.72344 7.11315 3.91927 6.99232 4.04427Z"
           fill="#49B841"
diff --git a/app/src/components/icons/analysis.tsx b/app/src/components/icons/analysis.tsx
index 591df60..ee2fcc6 100644
--- a/app/src/components/icons/analysis.tsx
+++ b/app/src/components/icons/analysis.tsx
@@ -68,7 +68,7 @@ export function PowerIcon() {
       fill="none"
       xmlns="http://www.w3.org/2000/svg"
     >
-      <g clip-path="url(#clip0_4107_3144)">
+      <g clipPath="url(#clip0_4107_3144)">
         <path
           d="M12.1277 1.76564L10.7174 9.17535L15.8265 9.19254L8.87213 19.2375L10.2824 11.0856L5.17369 11.0678L12.1277 1.76564ZM12.1287 0.515664C12.0949 0.515664 12.0612 0.516895 12.0281 0.519375C11.8075 0.537207 11.6612 0.610957 11.4878 0.72752C11.3901 0.792624 11.3021 0.871096 11.2262 0.960645C11.2034 0.987526 11.1819 1.01547 11.1618 1.04439L4.15775 10.3141C3.88119 10.6931 3.84056 11.1935 4.05306 11.6116C4.26525 12.0297 4.69431 12.2947 5.16463 12.2982L8.77275 12.3244L7.63838 19.0079C7.53056 19.5822 7.83681 20.1547 8.37588 20.3854C8.53254 20.4527 8.70128 20.4873 8.87179 20.4872C9.26461 20.4872 9.58742 20.3035 9.82963 19.9716L16.8424 9.92658C17.119 9.5475 17.1593 9.04656 16.9471 8.62906C16.7349 8.21094 16.3059 7.94592 15.8356 7.9425L12.2274 7.93625L13.3496 2.05969C13.3734 1.96348 13.3854 1.86473 13.3853 1.76562C13.3853 1.08938 12.8468 0.538125 12.1731 0.51625C12.1581 0.515625 12.1434 0.515625 12.1287 0.515625L12.1287 0.515664Z"
           fill="#F3C64D"
diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
index 10f90bf..8fcd5ed 100644
--- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
@@ -23,11 +23,14 @@ function RoboticArmAnimator({
     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 } = useResetButtonStore();
+    const { isReset, setReset } = useResetButtonStore();
     const { speed } = useAnimationPlaySpeed();
 
     // Update path state whenever `path` prop changes
@@ -35,20 +38,25 @@ function RoboticArmAnimator({
         setCurrentPath(path);
     }, [path]);
 
-    // Reset logic when `isPlaying` changes
-    useEffect(() => {
-        if (!isPlaying) {
-            setCurrentPath([]);
-            curveRef.current = null;
-        }
-    }, [isPlaying]);
-
     // 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);
+        }
+    }, [isReset, isPlaying])
+
 
     function generateRingPoints(radius: any, segments: any) {
         const points: [number, number, number][] = [];
@@ -77,10 +85,10 @@ function RoboticArmAnimator({
         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];
 
@@ -89,43 +97,28 @@ function RoboticArmAnimator({
 
             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]
-                    );
+                    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]);
             };
 
             const nearestToStart = findNearest(raisedStart);
-
             const nearestToEnd = findNearest(raisedEnd);
 
             const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints);
-
             const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints);
 
-            // 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)
@@ -143,63 +136,92 @@ function RoboticArmAnimator({
                 }
             }
 
-            // 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
+                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 customCurve = new THREE.CatmullRomCurve3(pathVectors, false, 'centripetal', 1);
-            const generatedPoints = customCurve.getPoints(100);
-            setCustomCurvePoints(generatedPoints);
+            
+            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((_, delta) => {
+    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 && currentPath.length > 0) {
-                const curvePoints = customCurvePoints;
-                const speedAdjustedProgress = progressRef.current + (speed * armBot.speed);
-                const index = Math.floor(speedAdjustedProgress);
+            if (!isPaused && customCurvePoints && customCurvePoints.length > 0) {
+                const distances = segmentDistancesRef.current; // distances between each pair of points
+                const totalDistance = totalDistanceRef.current;
 
-                if (index >= curvePoints.length) {
-                    // Reached the end of the curve
+                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;
-                } else {
-                    const point = curvePoints[index];
-                    bone.position.copy(point);
-                    progressRef.current = speedAdjustedProgress;
+                    startTimeRef.current = null;
                 }
-            } else if (isPaused) {
-                logStatus(armBot.modelUuid, 'Simulation Paused');
-            }
 
-            ikSolver.update();
-        } else if (!isPlaying && currentPath.length === 0) {
-            // Not playing anymore, reset to rest
+                ikSolver.update();
+            }
+        } else if ((!isPlaying && currentPath.length === 0) || isReset) {
             bone.position.copy(restPosition);
             ikSolver.update();
         }
-    });
 
+    });
 
     return (
         <>
-            {customCurvePoints && currentPath && isPlaying && (
+            {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])}
diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
index d2f2dba..60d4c33 100644
--- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
@@ -51,13 +51,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
             pauseTimeRef.current = null;
         }
         const elapsedTime = performance.now() - startTime;
-        if (elapsedTime < 1500) {
+        if (elapsedTime < 1000) {
             // Wait until 1500ms has passed
             requestAnimationFrame(step);
             return;
         }
         if (currentPhase === "picking") {
-
             setArmBotActive(armBot.modelUuid, true);
             setArmBotState(armBot.modelUuid, "running");
             setCurrentPhase("start-to-end");
@@ -75,7 +74,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
             }
             logStatus(armBot.modelUuid, "Moving armBot from start point to end position.")
         } else if (currentPhase === "dropping") {
-
             setArmBotActive(armBot.modelUuid, true);
             setArmBotState(armBot.modelUuid, "running");
             setCurrentPhase("end-to-rest");
@@ -91,35 +89,26 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
             }
             logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.")
         }
-
     }
     useEffect(() => {
         isPausedRef.current = isPaused;
     }, [isPaused]);
 
     useEffect(() => {
-        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) {
-
+        if (isReset || !isPlaying) {
             logStatus(armBot.modelUuid, "Simulation Play Reset Successfully")
             removeCurrentAction(armBot.modelUuid)
-            setArmBotActive(armBot.modelUuid, true)
-            setArmBotState(armBot.modelUuid, "running")
-            setCurrentPhase("init-to-rest");
+            setArmBotActive(armBot.modelUuid, false)
+            setArmBotState(armBot.modelUuid, "idle")
+            setCurrentPhase("init");
             isPausedRef.current = false
             pauseTimeRef.current = null
-            isPausedRef.current = false
             startTime = 0
+            const targetBones = ikSolver?.mesh.skeleton.bones.find(
+                (b: any) => b.name === targetBone
+            );
             if (targetBones) {
-                let curve = createCurveBetweenTwoPoints(targetBones.position, targetBones.position)
+                let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
                 if (curve) {
                     setPath(curve.points.map(point => [point.x, point.y, point.z]));
                 }
@@ -127,6 +116,16 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
             setReset(false);
             logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.")
         }
+    }, [isReset, isPlaying])
+
+    useEffect(() => {
+        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 (isPlaying) {
 
             //Moving armBot from initial point to rest position.
@@ -136,7 +135,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
                 setArmBotState(armBot.modelUuid, "running")
                 setCurrentPhase("init-to-rest");
                 if (targetBones) {
-                    let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
+                    let curve = createCurveBetweenTwoPoints(targetBones.position, targetBones.position)
                     if (curve) {
                         setPath(curve.points.map(point => [point.x, point.y, point.z]));
                     }
@@ -147,8 +146,7 @@ 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, selectedAction?.actionId);
-                    console.log('selectedAction?.actionId: ', selectedAction?.actionId);
+                    addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid);
                 }, 3000);
                 return () => clearTimeout(timeoutId);
             }
@@ -158,11 +156,9 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
                     setArmBotActive(armBot.modelUuid, true);
                     setArmBotState(armBot.modelUuid, "running");
                     setCurrentPhase("rest-to-start");
-                    let actiondata = getActionByUuid(selectedProduct.productId, selectedAction.actionId)
-                    console.log('actiondata: ', actiondata);
                     const startPoint = armBot.point.actions[0].process.startPoint;
                     if (startPoint) {
-                        let curve = createCurveBetweenTwoPoints(restPosition, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
+                        let curve = createCurveBetweenTwoPoints(targetBones.position, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
                         if (curve) {
                             setPath(curve.points.map(point => [point.x, point.y, point.z]));
                         }
@@ -208,16 +204,19 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
                 // logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.")
             }
         } else {
-            logStatus(armBot.modelUuid, "Simulation Play Stopped")
+            logStatus(armBot.modelUuid, "Simulation Play Exited")
             setArmBotActive(armBot.modelUuid, false)
             setArmBotState(armBot.modelUuid, "idle")
             setCurrentPhase("init");
             setPath([])
+            isPausedRef.current = false
+            pauseTimeRef.current = null
+            isPausedRef.current = false
+            startTime = 0
             removeCurrentAction(armBot.modelUuid)
-
         }
 
-    }, [currentPhase, armBot, isPlaying, ikSolver, isReset])
+    }, [currentPhase, armBot, isPlaying, ikSolver])
 
 
     function createCurveBetweenTwoPoints(p1: any, p2: any) {
@@ -260,6 +259,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
         }
     }
     const logStatus = (id: string, status: string) => {
+        
         // 
 
     }
diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
index 3070c3a..bdfd1f9 100644
--- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
@@ -71,7 +71,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns
 
         setSelectedArm(OOI.Target_Bone);
 
-        // scene.add(helper);
+        scene.add(helper);
 
     }, [gltf]);
 
diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx
index efacd53..7fe3c50 100644
--- a/app/src/modules/simulation/simulation.tsx
+++ b/app/src/modules/simulation/simulation.tsx
@@ -23,7 +23,7 @@ function Simulation() {
     }, [events])
 
     useEffect(() => {
-        console.log('products: ', products);
+        // console.log('products: ', products);
     }, [products])
 
     return (
diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts
index e450bfd..ab89507 100644
--- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts
+++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts
@@ -1,10 +1,19 @@
 import { useRef, useState } from "react";
 import * as THREE from "three";
 import { ThreeEvent, useThree } from "@react-three/fiber";
+import { useProductStore } from "../../../../store/simulation/useProductStore";
+import {
+  useSelectedEventData,
+  useSelectedProduct,
+} from "../../../../store/simulation/useSimulationStore";
 
 type OnUpdateCallback = (object: THREE.Object3D) => void;
 
 export default function useDraggableGLTF(onUpdate: OnUpdateCallback) {
+  const { getEventByModelUuid, updateAction, getActionByUuid } =
+    useProductStore();
+  const { selectedEventData } = useSelectedEventData();
+  const { selectedProduct } = useSelectedProduct();
   const { camera, gl, controls } = useThree();
   const activeObjRef = useRef<THREE.Object3D | null>(null);
   const planeRef = useRef<THREE.Plane>(
@@ -34,7 +43,6 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) {
 
     activeObjRef.current = obj;
     initialPositionRef.current.copy(obj.position);
-    
 
     // Get world position
     setObjectWorldPos(obj.getWorldPosition(objectWorldPos));
@@ -62,57 +70,84 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) {
 
   const handlePointerMove = (e: PointerEvent) => {
     if (!activeObjRef.current) return;
+    if (selectedEventData?.data.type === "roboticArm") {
+      const selectedArmBot = getEventByModelUuid(
+        selectedProduct.productId,
+        selectedEventData.data.modelUuid
+      );
+      if (!selectedArmBot) return;
+      // Check if Shift key is pressed
+      const isShiftKeyPressed = e.shiftKey;
 
-    // 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;
 
-    // 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);
 
-    // 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
+      );
+      if (!intersects) return;
 
-    // Create a vector to store intersection point
-    const intersection = new THREE.Vector3();
-    const intersects = raycaster.ray.intersectPlane(
-      planeRef.current,
-      intersection
-    );
-    if (!intersects) return;
+      // Add offset for dragging
+      intersection.add(offsetRef.current);
 
-    // 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();
 
-    // Get the parent's world matrix if exists
-    const parent = activeObjRef.current.parent;
-    const targetPosition = new THREE.Vector3();
+      // OnPointerDown
+      initialPositionRef.current.copy(objectWorldPos);
 
-    // OnPointerDown
-    initialPositionRef.current.copy(objectWorldPos);
+      // OnPointerMove
+      if (isShiftKeyPressed) {
+        const { x: initialX, y: initialY } = initialPositionRef.current;
+        const { x: objectX, z: objectZ } = objectWorldPos;
 
-    // OnPointerMove
-    if (isShiftKeyPressed) {
-      const { x: initialX, y: initialY } = initialPositionRef.current;
-      const { x: objectX, z: objectZ } = objectWorldPos;
+        const deltaX = intersection.x - initialX;
 
-      const deltaX = intersection.x - initialX;
+        targetPosition.set(objectX, initialY + deltaX, objectZ);
+      } else {
+        // For free movement
+        targetPosition.copy(intersection);
+      }
 
-      targetPosition.set(objectX, initialY + deltaX, objectZ);
-    } else {
-      // For free movement
-      targetPosition.copy(intersection);
+      // CONSTRAIN MOVEMENT HERE:
+      const centerX = selectedArmBot.position[0];
+      const centerZ = selectedArmBot.position[2];
+      const minDistance = 1.2; // 1.4 radius
+      const maxDistance = 2; // 2 radius
+
+      const deltaX = targetPosition.x - centerX;
+      const deltaZ = targetPosition.z - centerZ;
+      const distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ);
+
+      if (distance < minDistance || distance > maxDistance) {
+        const angle = Math.atan2(deltaZ, deltaX);
+        const clampedDistance = Math.min(
+          Math.max(distance, minDistance),
+          maxDistance
+        );
+
+        targetPosition.x = centerX + Math.cos(angle) * clampedDistance;
+        targetPosition.z = centerZ + Math.sin(angle) * clampedDistance;
+      }
+      targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.5);
+      // 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);
     }
-
-    // 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);
   };
 
   const handlePointerUp = () => {