Refactor Simulations, RenameTooltip, EditWidgetOption, and RoboticArmAnimator components: streamline imports, enhance UI elements, and improve event handling logic.

This commit is contained in:
2025-05-03 11:17:14 +05:30
parent 6a79ef563c
commit b4e4bf7fb3
5 changed files with 452 additions and 416 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from "react"; import React, { useRef, useState } from "react";
import { import {
AddIcon, AddIcon,
ArrowIcon, ArrowIcon,
@@ -39,19 +39,30 @@ const List: React.FC<ListProps> = ({ val }) => {
const Simulations: React.FC = () => { const Simulations: React.FC = () => {
const productsContainerRef = useRef<HTMLDivElement>(null); const productsContainerRef = useRef<HTMLDivElement>(null);
const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent, } = useProductStore(); const {
products,
addProduct,
removeProduct,
renameProduct,
addEvent,
removeEvent,
} = useProductStore();
const { selectedProduct, setSelectedProduct } = useSelectedProduct(); const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const { getEventByModelUuid } = useEventsStore(); const { getEventByModelUuid } = useEventsStore();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const email = localStorage.getItem('email') const email = localStorage.getItem("email");
const organization = (email!.split("@")[1]).split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
const [openObjects, setOpenObjects] = useState(true); const [openObjects, setOpenObjects] = useState(true);
const handleAddProduct = () => { const handleAddProduct = () => {
const id = generateUUID(); const id = generateUUID();
const name = `Product ${products.length + 1}`; const name = `Product ${products.length + 1}`;
addProduct(name, id); addProduct(name, id);
upsertProductOrEventApi({ productName: name, productId: id, organization: organization }); upsertProductOrEventApi({
productName: name,
productId: id,
organization: organization,
});
}; };
const handleRemoveProduct = (productId: string) => { const handleRemoveProduct = (productId: string) => {
@@ -88,12 +99,12 @@ const Simulations: React.FC = () => {
const handleRemoveEventFromProduct = () => { const handleRemoveEventFromProduct = () => {
if (selectedAsset) { if (selectedAsset) {
const email = localStorage.getItem('email') const email = localStorage.getItem("email");
const organization = (email!.split("@")[1]).split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
deleteEventDataApi({ deleteEventDataApi({
productId: selectedProduct.productId, productId: selectedProduct.productId,
modelUuid: selectedAsset.modelUuid, modelUuid: selectedAsset.modelUuid,
organization: organization organization: organization,
}); });
removeEvent(selectedProduct.productId, selectedAsset.modelUuid); removeEvent(selectedProduct.productId, selectedAsset.modelUuid);
clearSelectedAsset(); clearSelectedAsset();
@@ -104,7 +115,8 @@ const Simulations: React.FC = () => {
(product) => product.productId === selectedProduct.productId (product) => product.productId === selectedProduct.productId
); );
const events: Event[] = selectedProductData?.eventDatas.map((event) => ({ const events: Event[] =
selectedProductData?.eventDatas.map((event) => ({
pathName: event.modelName, pathName: event.modelName,
})) || []; })) || [];
@@ -115,9 +127,9 @@ const Simulations: React.FC = () => {
<div className="actions section"> <div className="actions section">
<div className="header"> <div className="header">
<div className="header-value">Products</div> <div className="header-value">Products</div>
<div className="add-button" onClick={handleAddProduct}> <button className="add-button" onClick={handleAddProduct}>
<AddIcon /> Add <AddIcon /> Add
</div> </button>
</div> </div>
<div <div
className="lists-main-container" className="lists-main-container"
@@ -128,11 +140,13 @@ const Simulations: React.FC = () => {
{products.map((product, index) => ( {products.map((product, index) => (
<div <div
key={product.productId} key={product.productId}
className={`list-item ${selectedProduct.productId === product.productId className={`list-item ${
selectedProduct.productId === product.productId
? "active" ? "active"
: "" : ""
}`} }`}
> >
{/* eslint-disable-next-line */}
<div <div
className="value" className="value"
onClick={() => onClick={() =>
@@ -153,23 +167,23 @@ const Simulations: React.FC = () => {
/> />
</div> </div>
{products.length > 1 && ( {products.length > 1 && (
<div <button
className="remove-button" className="remove-button"
onClick={() => handleRemoveProduct(product.productId)} onClick={() => handleRemoveProduct(product.productId)}
> >
<RemoveIcon /> <RemoveIcon />
</div> </button>
)} )}
</div> </div>
))} ))}
</div> </div>
<div <button
className="resize-icon" className="resize-icon"
id="action-resize" id="action-resize"
onMouseDown={(e) => handleResize(e, productsContainerRef)} onMouseDown={(e: any) => handleResize(e, productsContainerRef)}
> >
<ResizeHeightIcon /> <ResizeHeightIcon />
</div> </button>
</div> </div>
</div> </div>
@@ -184,7 +198,9 @@ const Simulations: React.FC = () => {
</div> </div>
</button> </button>
{openObjects && {openObjects &&
events.map((event, index) => <List key={index} val={event} />)} events.map((event, index) => (
<List key={`${index}-${event.pathName}`} val={event} />
))}
</div> </div>
<div className="compare-simulations-container"> <div className="compare-simulations-container">
@@ -192,7 +208,7 @@ const Simulations: React.FC = () => {
Need to Compare Layout? Need to Compare Layout?
</div> </div>
<div className="content"> <div className="content">
Click <span>'Compare'</span> to review and analyze the layout Click '<span>Compare</span>' to review and analyze the layout
differences between them. differences between them.
</div> </div>
<div className="input"> <div className="input">
@@ -211,7 +227,7 @@ const Simulations: React.FC = () => {
event: getEventByModelUuid(selectedAsset.modelUuid), event: getEventByModelUuid(selectedAsset.modelUuid),
addEvent, addEvent,
selectedProduct, selectedProduct,
clearSelectedAsset clearSelectedAsset,
}); });
} else { } else {
handleRemoveEventFromProduct(); handleRemoveEventFromProduct();
@@ -221,7 +237,7 @@ const Simulations: React.FC = () => {
</RenderOverlay> </RenderOverlay>
)} )}
</div> </div>
) );
}; };
export default Simulations; export default Simulations;

View File

@@ -25,10 +25,8 @@ const RenameTooltip: React.FC<RenameTooltipProps> = ({ name, onSubmit }) => {
<div <div
className="rename-tool-tip" className="rename-tool-tip"
style={{ style={{
position: "absolute",
top: `${top}px`, top: `${top}px`,
left: `${left}px`, left: `${left}px`,
zIndex: 100,
}} }}
> >
<div className="header"> <div className="header">

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react"; import React from "react";
import { import {
useLeftData, useLeftData,
useTopData, useTopData,
@@ -28,13 +28,13 @@ const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
> >
<div className="context-menu-options"> <div className="context-menu-options">
{options.map((option, index) => ( {options.map((option, index) => (
<div <button
className="option" className="option"
key={index} key={`${index}-${option}`}
onClick={() => onClick(option)} onClick={() => onClick(option)}
> >
{option} {option}
</div> </button>
))} ))}
</div> </div>
</div> </div>

View File

@@ -64,8 +64,6 @@ const SimulationPlayer: React.FC = () => {
const handleMouseDown = () => { const handleMouseDown = () => {
isDragging.current = true; isDragging.current = true;
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
}; };
const handleMouseMove = (e: MouseEvent) => { const handleMouseMove = (e: MouseEvent) => {
@@ -80,11 +78,11 @@ const SimulationPlayer: React.FC = () => {
const handleMouseUp = () => { const handleMouseUp = () => {
isDragging.current = false; isDragging.current = false;
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
}; };
useEffect(() => { useEffect(() => {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
return () => { return () => {
document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp); document.removeEventListener("mouseup", handleMouseUp);
@@ -109,24 +107,6 @@ const SimulationPlayer: React.FC = () => {
{ name: "process 9", completed: 90 }, // 90% completed { name: "process 9", completed: 90 }, // 90% completed
{ name: "process 10", completed: 30 }, // 30% 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<string[]>([]);
// 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 intervals = [10, 20, 30, 40, 50, 60]; // in minutes
const totalSegments = intervals.length; const totalSegments = intervals.length;
@@ -218,7 +198,7 @@ const SimulationPlayer: React.FC = () => {
</div> </div>
</div> </div>
)} )}
{subModule === "simulations" && ( {subModule !== "analysis" && (
<div className="header"> <div className="header">
<InfoIcon /> <InfoIcon />
{playSimulation {playSimulation
@@ -281,7 +261,7 @@ const SimulationPlayer: React.FC = () => {
const segmentProgress = (index / totalSegments) * 100; const segmentProgress = (index / totalSegments) * 100;
const isFilled = progress >= segmentProgress; const isFilled = progress >= segmentProgress;
return ( return (
<React.Fragment key={index}> <React.Fragment key={`${index}-${label}`}>
<div className="label-dot-wrapper"> <div className="label-dot-wrapper">
<div className="label">{label} mins</div> <div className="label">{label} mins</div>
<div <div
@@ -360,6 +340,7 @@ const SimulationPlayer: React.FC = () => {
className="process-wrapper" className="process-wrapper"
style={{ padding: expand ? "0px" : "5px 35px" }} style={{ padding: expand ? "0px" : "5px 35px" }}
> >
{/* eslint-disable-next-line */}
<div <div
className="process-container" className="process-container"
ref={processWrapperRef} ref={processWrapperRef}
@@ -367,7 +348,7 @@ const SimulationPlayer: React.FC = () => {
> >
{process.map((item, index) => ( {process.map((item, index) => (
<div <div
key={index} key={`${index}-${item.name}`}
className="process" className="process"
style={{ style={{
width: `${item.completed}%`, width: `${item.completed}%`,

View File

@@ -1,13 +1,13 @@
import React, { useEffect, useMemo, 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 { Line } from "@react-three/drei";
import { import {
useAnimationPlaySpeed, useAnimationPlaySpeed,
usePauseButtonStore, usePauseButtonStore,
usePlayButtonStore, usePlayButtonStore,
useResetButtonStore useResetButtonStore,
} from '../../../../../store/usePlayButtonStore'; } from "../../../../../store/usePlayButtonStore";
function RoboticArmAnimator({ function RoboticArmAnimator({
HandleCallback, HandleCallback,
@@ -16,13 +16,19 @@ function RoboticArmAnimator({
targetBone, targetBone,
armBot, armBot,
logStatus, logStatus,
path path,
}: any) { }: any) {
const progressRef = useRef(0); const progressRef = useRef(0);
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 [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); []
const [customCurvePoints, setCustomCurvePoints] = useState<THREE.Vector3[] | null>(null); );
const [circlePoints, setCirclePoints] = useState<[number, number, number][]>(
[]
);
const [customCurvePoints, setCustomCurvePoints] = useState<
THREE.Vector3[] | null
>(null);
// Zustand stores // Zustand stores
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
@@ -45,11 +51,10 @@ function RoboticArmAnimator({
// Handle circle points based on armBot position // Handle circle points based on armBot position
useEffect(() => { useEffect(() => {
const points = generateRingPoints(1.6, 64) const points = generateRingPoints(1.6, 64);
setCirclePoints(points); setCirclePoints(points);
}, [armBot.position]); }, [armBot.position]);
function generateRingPoints(radius: any, segments: any) { function generateRingPoints(radius: any, segments: any) {
const points: [number, number, number][] = []; const points: [number, number, number][] = [];
for (let i = 0; i < segments; i++) { for (let i = 0; i < segments; i++) {
@@ -63,7 +68,11 @@ function RoboticArmAnimator({
return points; return points;
} }
const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { const findNearestIndex = (
nearestPoint: [number, number, number],
points: [number, number, number][],
epsilon = 1e-6
) => {
for (let i = 0; i < points.length; i++) { for (let i = 0; i < points.length; i++) {
const [x, y, z] = points[i]; const [x, y, z] = points[i];
if ( if (
@@ -77,15 +86,22 @@ function RoboticArmAnimator({
return -1; // Not found return -1; // Not found
}; };
// Handle nearest points and final path (including arc points) // Handle nearest points and final path (including arc points)
useEffect(() => { useEffect(() => {
if (circlePoints.length > 0 && currentPath.length > 0) { if (circlePoints.length > 0 && currentPath.length > 0) {
const start = currentPath[0]; const start = currentPath[0];
const end = currentPath[currentPath.length - 1]; const end = currentPath[currentPath.length - 1];
const raisedStart = [start[0], start[1] + 0.5, start[2]] as [number, number, number]; const raisedStart = [start[0], start[1] + 0.5, start[2]] as [
const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [number, number, number]; number,
number,
number
];
const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [
number,
number,
number
];
const findNearest = (target: [number, number, number]) => { const findNearest = (target: [number, number, number]) => {
return circlePoints.reduce((nearest, point) => { return circlePoints.reduce((nearest, point) => {
@@ -107,14 +123,19 @@ function RoboticArmAnimator({
const nearestToEnd = findNearest(raisedEnd); const nearestToEnd = findNearest(raisedEnd);
const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); const indexOfNearestStart = findNearestIndex(
nearestToStart,
circlePoints
);
const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints);
// Find clockwise and counter-clockwise distances // Find clockwise and counter-clockwise distances
const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; const clockwiseDistance =
(indexOfNearestEnd - indexOfNearestStart + 64) % 64;
const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; const counterClockwiseDistance =
(indexOfNearestStart - indexOfNearestEnd + 64) % 64;
const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance;
@@ -123,24 +144,25 @@ function RoboticArmAnimator({
if (clockwiseIsShorter) { if (clockwiseIsShorter) {
if (indexOfNearestStart <= indexOfNearestEnd) { if (indexOfNearestStart <= indexOfNearestEnd) {
arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1); arcPoints = circlePoints.slice(
indexOfNearestStart,
indexOfNearestEnd + 1
);
} else { } else {
// Wrap around // Wrap around
arcPoints = [ arcPoints = [
...circlePoints.slice(indexOfNearestStart, 64), ...circlePoints.slice(indexOfNearestStart, 64),
...circlePoints.slice(0, indexOfNearestEnd + 1) ...circlePoints.slice(0, indexOfNearestEnd + 1),
]; ];
} }
} else { } else if (indexOfNearestStart >= indexOfNearestEnd) {
if (indexOfNearestStart >= indexOfNearestEnd) { for (
for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { let i = indexOfNearestStart;
i !== (indexOfNearestEnd - 1 + 64) % 64;
i = (i - 1 + 64) % 64
) {
arcPoints.push(circlePoints[i]); 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 // Continue your custom path logic
@@ -148,13 +170,20 @@ function RoboticArmAnimator({
new THREE.Vector3(start[0], start[1], start[2]), // start new THREE.Vector3(start[0], start[1], start[2]), // start
new THREE.Vector3(raisedStart[0], raisedStart[1], raisedStart[2]), // lift up new THREE.Vector3(raisedStart[0], raisedStart[1], raisedStart[2]), // lift up
new THREE.Vector3(nearestToStart[0], raisedStart[1], nearestToStart[2]), // move to arc start 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])), ...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(nearestToEnd[0], raisedEnd[1], nearestToEnd[2]), // move from arc end
new THREE.Vector3(raisedEnd[0], raisedEnd[1], raisedEnd[2]), // lowered 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(end[0], end[1], end[2]), // end
]; ];
const customCurve = new THREE.CatmullRomCurve3(pathVectors, false, 'centripetal', 1); const customCurve = new THREE.CatmullRomCurve3(
pathVectors,
false,
"centripetal",
1
);
const generatedPoints = customCurve.getPoints(100); const generatedPoints = customCurve.getPoints(100);
setCustomCurvePoints(generatedPoints); setCustomCurvePoints(generatedPoints);
} }
@@ -164,13 +193,16 @@ function RoboticArmAnimator({
useFrame((_, delta) => { useFrame((_, delta) => {
if (!ikSolver) return; if (!ikSolver) return;
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); const bone = ikSolver.mesh.skeleton.bones.find(
(b: any) => b.name === targetBone
);
if (!bone) return; if (!bone) return;
if (isPlaying) { if (isPlaying) {
if (!isPaused && customCurvePoints && currentPath.length > 0) { if (!isPaused && customCurvePoints && currentPath.length > 0) {
const curvePoints = customCurvePoints; const curvePoints = customCurvePoints;
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) { if (index >= curvePoints.length) {
@@ -185,7 +217,7 @@ function RoboticArmAnimator({
progressRef.current = speedAdjustedProgress; progressRef.current = speedAdjustedProgress;
} }
} else if (isPaused) { } else if (isPaused) {
logStatus(armBot.modelUuid, 'Simulation Paused'); logStatus(armBot.modelUuid, "Simulation Paused");
} }
ikSolver.update(); ikSolver.update();
@@ -196,20 +228,29 @@ function RoboticArmAnimator({
} }
}); });
return ( return (
<> <>
{customCurvePoints && currentPath && isPlaying && ( {customCurvePoints && currentPath && isPlaying && (
<mesh rotation={armBot.rotation} position={armBot.position}> <mesh rotation={armBot.rotation} position={armBot.position}>
<Line <Line
points={customCurvePoints.map((p) => [p.x, p.y, p.z] as [number, number, number])} points={customCurvePoints.map(
(p) => [p.x, p.y, p.z] as [number, number, number]
)}
color="green" color="green"
lineWidth={5} lineWidth={5}
dashed={false} dashed={false}
/> />
</mesh> </mesh>
)} )}
<mesh position={[armBot.position[0], armBot.position[1] + 1.5, armBot.position[2]]} rotation={[-Math.PI / 2, 0, 0]}> <mesh
position={[
armBot.position[0],
armBot.position[1] + 1.5,
armBot.position[2],
]}
rotation={[-Math.PI / 2, 0, 0]}
visible={false}
>
<ringGeometry args={[1.59, 1.61, 64]} /> <ringGeometry args={[1.59, 1.61, 64]} />
<meshBasicMaterial color="green" side={THREE.DoubleSide} /> <meshBasicMaterial color="green" side={THREE.DoubleSide} />
</mesh> </mesh>