From b4e4bf7fb33e1f1e65da5208fd2489f236b47309 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 3 May 2025 11:17:14 +0530 Subject: [PATCH] Refactor Simulations, RenameTooltip, EditWidgetOption, and RoboticArmAnimator components: streamline imports, enhance UI elements, and improve event handling logic. --- .../sidebarRight/simulation/Simulations.tsx | 384 +++++++-------- .../components/ui/features/RenameTooltip.tsx | 2 - .../components/ui/menu/EditWidgetOption.tsx | 8 +- .../ui/simulation/simulationPlayer.tsx | 31 +- .../instances/animator/roboticArmAnimator.tsx | 443 ++++++++++-------- 5 files changed, 452 insertions(+), 416 deletions(-) diff --git a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx index 1280693..7799858 100644 --- a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx +++ b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx @@ -1,15 +1,15 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useRef, useState } from "react"; import { - AddIcon, - ArrowIcon, - RemoveIcon, - ResizeHeightIcon, + AddIcon, + ArrowIcon, + RemoveIcon, + ResizeHeightIcon, } from "../../../icons/ExportCommonIcons"; import RenameInput from "../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../functions/handleResizePannel"; import { - useSelectedAsset, - useSelectedProduct, + useSelectedAsset, + useSelectedProduct, } from "../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { generateUUID } from "three/src/math/MathUtils"; @@ -22,206 +22,222 @@ import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertP import { deleteProductApi } from "../../../../services/simulation/deleteProductApi"; interface Event { - pathName: string; + pathName: string; } interface ListProps { - val: Event; + val: Event; } const List: React.FC = ({ val }) => { - return ( -
-
{val.pathName}
-
- ); + return ( +
+
{val.pathName}
+
+ ); }; const Simulations: React.FC = () => { - const productsContainerRef = useRef(null); - const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent, } = useProductStore(); - const { selectedProduct, setSelectedProduct } = useSelectedProduct(); - const { getEventByModelUuid } = useEventsStore(); - const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - const [openObjects, setOpenObjects] = useState(true); + const productsContainerRef = useRef(null); + const { + products, + addProduct, + removeProduct, + renameProduct, + addEvent, + removeEvent, + } = useProductStore(); + const { selectedProduct, setSelectedProduct } = useSelectedProduct(); + const { getEventByModelUuid } = useEventsStore(); + const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + const [openObjects, setOpenObjects] = useState(true); - const handleAddProduct = () => { - const id = generateUUID(); - const name = `Product ${products.length + 1}`; - addProduct(name, id); - upsertProductOrEventApi({ productName: name, productId: id, organization: organization }); - }; + const handleAddProduct = () => { + const id = generateUUID(); + const name = `Product ${products.length + 1}`; + addProduct(name, id); + upsertProductOrEventApi({ + productName: name, + productId: id, + organization: organization, + }); + }; - const handleRemoveProduct = (productId: string) => { - const currentIndex = products.findIndex((p) => p.productId === productId); - const isSelected = selectedProduct.productId === productId; + const handleRemoveProduct = (productId: string) => { + const currentIndex = products.findIndex((p) => p.productId === productId); + const isSelected = selectedProduct.productId === productId; - const updatedProducts = products.filter((p) => p.productId !== productId); + const updatedProducts = products.filter((p) => p.productId !== productId); - if (isSelected) { - if (updatedProducts.length > 0) { - let newSelectedIndex = currentIndex; - if (currentIndex >= updatedProducts.length) { - newSelectedIndex = updatedProducts.length - 1; - } - setSelectedProduct( - updatedProducts[newSelectedIndex].productId, - updatedProducts[newSelectedIndex].productName - ); - } else { - setSelectedProduct("", ""); - } + if (isSelected) { + if (updatedProducts.length > 0) { + let newSelectedIndex = currentIndex; + if (currentIndex >= updatedProducts.length) { + newSelectedIndex = updatedProducts.length - 1; } + setSelectedProduct( + updatedProducts[newSelectedIndex].productId, + updatedProducts[newSelectedIndex].productName + ); + } else { + setSelectedProduct("", ""); + } + } - removeProduct(productId); - deleteProductApi(productId, organization); - }; + removeProduct(productId); + deleteProductApi(productId, organization); + }; - const handleRenameProduct = (productId: string, newName: string) => { - renameProduct(productId, newName); - if (selectedProduct.productId === productId) { - setSelectedProduct(productId, newName); - } - }; + const handleRenameProduct = (productId: string, newName: string) => { + renameProduct(productId, newName); + if (selectedProduct.productId === productId) { + setSelectedProduct(productId, newName); + } + }; - const handleRemoveEventFromProduct = () => { - if (selectedAsset) { - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - deleteEventDataApi({ - productId: selectedProduct.productId, - modelUuid: selectedAsset.modelUuid, - organization: organization - }); - removeEvent(selectedProduct.productId, selectedAsset.modelUuid); - clearSelectedAsset(); - } - }; + const handleRemoveEventFromProduct = () => { + if (selectedAsset) { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + deleteEventDataApi({ + productId: selectedProduct.productId, + modelUuid: selectedAsset.modelUuid, + organization: organization, + }); + removeEvent(selectedProduct.productId, selectedAsset.modelUuid); + clearSelectedAsset(); + } + }; - const selectedProductData = products.find( - (product) => product.productId === selectedProduct.productId - ); + const selectedProductData = products.find( + (product) => product.productId === selectedProduct.productId + ); - const events: Event[] = selectedProductData?.eventDatas.map((event) => ({ - pathName: event.modelName, + const events: Event[] = + selectedProductData?.eventDatas.map((event) => ({ + pathName: event.modelName, })) || []; - return ( -
-
Simulations
-
-
-
-
Products
-
- Add -
-
-
-
- {products.map((product, index) => ( -
-
- setSelectedProduct(product.productId, product.productName) - } - > - - - handleRenameProduct(product.productId, newName) - } - /> -
- {products.length > 1 && ( -
handleRemoveProduct(product.productId)} - > - -
- )} -
- ))} -
-
handleResize(e, productsContainerRef)} - > - -
-
-
- -
- - {openObjects && - events.map((event, index) => )} -
- -
-
- Need to Compare Layout? -
-
- Click 'Compare' to review and analyze the layout - differences between them. -
-
- -
-
-
- - {selectedAsset && ( - - { - if (option === "Add to Product") { - handleAddEventToProduct({ - event: getEventByModelUuid(selectedAsset.modelUuid), - addEvent, - selectedProduct, - clearSelectedAsset - }); - } else { - handleRemoveEventFromProduct(); - } - }} + return ( +
+
Simulations
+
+
+
+
Products
+ +
+
+
+ {products.map((product, index) => ( +
+ {/* eslint-disable-next-line */} +
+ setSelectedProduct(product.productId, product.productName) + } + > + - - )} + + handleRenameProduct(product.productId, newName) + } + /> +
+ {products.length > 1 && ( + + )} +
+ ))} +
+ +
- ) + +
+ + {openObjects && + events.map((event, index) => ( + + ))} +
+ +
+
+ Need to Compare Layout? +
+
+ Click 'Compare' to review and analyze the layout + differences between them. +
+
+ +
+
+
+ + {selectedAsset && ( + + { + if (option === "Add to Product") { + handleAddEventToProduct({ + event: getEventByModelUuid(selectedAsset.modelUuid), + addEvent, + selectedProduct, + clearSelectedAsset, + }); + } else { + handleRemoveEventFromProduct(); + } + }} + /> + + )} +
+ ); }; export default Simulations; diff --git a/app/src/components/ui/features/RenameTooltip.tsx b/app/src/components/ui/features/RenameTooltip.tsx index ae64bf0..180ba85 100644 --- a/app/src/components/ui/features/RenameTooltip.tsx +++ b/app/src/components/ui/features/RenameTooltip.tsx @@ -25,10 +25,8 @@ const RenameTooltip: React.FC = ({ name, onSubmit }) => {
diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index 035b26b..558957b 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React from "react"; import { useLeftData, useTopData, @@ -28,13 +28,13 @@ const EditWidgetOption: React.FC = ({ >
{options.map((option, index) => ( -
onClick(option)} > {option} -
+ ))}
diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index e569250..2b8dcff 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -64,8 +64,6 @@ const SimulationPlayer: React.FC = () => { const handleMouseDown = () => { isDragging.current = true; - document.addEventListener("mousemove", handleMouseMove); - document.addEventListener("mouseup", handleMouseUp); }; const handleMouseMove = (e: MouseEvent) => { @@ -80,11 +78,11 @@ const SimulationPlayer: React.FC = () => { const handleMouseUp = () => { isDragging.current = false; - document.removeEventListener("mousemove", handleMouseMove); - document.removeEventListener("mouseup", handleMouseUp); }; useEffect(() => { + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); return () => { document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); @@ -109,24 +107,6 @@ const SimulationPlayer: React.FC = () => { { name: "process 9", completed: 90 }, // 90% completed { name: "process 10", completed: 30 }, // 30% completed ]; - // Move getRandomColor out of render - const getRandomColor = () => { - const letters = "0123456789ABCDEF"; - let color = "#"; - for (let i = 0; i < 6; i++) { - color += letters[Math.floor(Math.random() * 16)]; - } - return color; - }; - - // Store colors for each process item - const [_, setProcessColors] = useState([]); - - // Generate colors on mount or when process changes - useEffect(() => { - const generatedColors = process.map(() => getRandomColor()); - setProcessColors(generatedColors); - }, []); const intervals = [10, 20, 30, 40, 50, 60]; // in minutes const totalSegments = intervals.length; @@ -218,7 +198,7 @@ const SimulationPlayer: React.FC = () => {
)} - {subModule === "simulations" && ( + {subModule !== "analysis" && (
{playSimulation @@ -281,7 +261,7 @@ const SimulationPlayer: React.FC = () => { const segmentProgress = (index / totalSegments) * 100; const isFilled = progress >= segmentProgress; return ( - +
{label} mins
{ className="process-wrapper" style={{ padding: expand ? "0px" : "5px 35px" }} > + {/* eslint-disable-next-line */}
{ > {process.map((item, index) => (
(null); - const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); - const [customCurvePoints, setCustomCurvePoints] = useState(null); + const progressRef = useRef(0); + const curveRef = useRef(null); + const [currentPath, setCurrentPath] = useState<[number, number, number][]>( + [] + ); + const [circlePoints, setCirclePoints] = useState<[number, number, number][]>( + [] + ); + const [customCurvePoints, setCustomCurvePoints] = useState< + THREE.Vector3[] | null + >(null); - // Zustand stores - const { isPlaying } = usePlayButtonStore(); - const { isPaused } = usePauseButtonStore(); - const { isReset } = useResetButtonStore(); - const { speed } = useAnimationPlaySpeed(); + // Zustand stores + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { isReset } = 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]); - // 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]); - - - 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; + // Reset logic when `isPlaying` changes + useEffect(() => { + if (!isPlaying) { + setCurrentPath([]); + curveRef.current = null; } + }, [isPlaying]); - 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 - } + // Handle circle points based on armBot position + useEffect(() => { + 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 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]) => { + 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]); + }; + + 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), + ]; } - 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]) => { - 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]); - }; - - 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) - ]; - } - } 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(pathVectors, false, 'centripetal', 1); - const generatedPoints = customCurve.getPoints(100); - setCustomCurvePoints(generatedPoints); + } else if (indexOfNearestStart >= indexOfNearestEnd) { + 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((_, delta) => { - if (!ikSolver) return; + // 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 bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); - if (!bone) return; + const customCurve = new THREE.CatmullRomCurve3( + pathVectors, + false, + "centripetal", + 1 + ); + const generatedPoints = customCurve.getPoints(100); + setCustomCurvePoints(generatedPoints); + } + }, [circlePoints, currentPath]); - if (isPlaying) { - if (!isPaused && customCurvePoints && currentPath.length > 0) { - const curvePoints = customCurvePoints; - const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); - const index = Math.floor(speedAdjustedProgress); + // Frame update for animation + useFrame((_, delta) => { + if (!ikSolver) return; - if (index >= curvePoints.length) { - // Reached the end of the curve - HandleCallback(); - setCurrentPath([]); - curveRef.current = null; - progressRef.current = 0; - } else { - const point = curvePoints[index]; - bone.position.copy(point); - progressRef.current = speedAdjustedProgress; - } - } else if (isPaused) { - logStatus(armBot.modelUuid, 'Simulation Paused'); - } - - ikSolver.update(); - } else if (!isPlaying && currentPath.length === 0) { - // Not playing anymore, reset to rest - bone.position.copy(restPosition); - ikSolver.update(); - } - }); - - - return ( - <> - {customCurvePoints && currentPath && isPlaying && ( - - [p.x, p.y, p.z] as [number, number, number])} - color="green" - lineWidth={5} - dashed={false} - /> - - )} - - - - - + 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 (index >= curvePoints.length) { + // Reached the end of the curve + HandleCallback(); + setCurrentPath([]); + curveRef.current = null; + progressRef.current = 0; + } else { + const point = curvePoints[index]; + bone.position.copy(point); + progressRef.current = speedAdjustedProgress; + } + } else if (isPaused) { + logStatus(armBot.modelUuid, "Simulation Paused"); + } + + ikSolver.update(); + } else if (!isPlaying && currentPath.length === 0) { + // Not playing anymore, reset to rest + bone.position.copy(restPosition); + ikSolver.update(); + } + }); + + return ( + <> + {customCurvePoints && currentPath && isPlaying && ( + + [p.x, p.y, p.z] as [number, number, number] + )} + color="green" + lineWidth={5} + dashed={false} + /> + + )} + + + + + + ); } export default RoboticArmAnimator;