"updated single flow"

This commit is contained in:
SreeNath14
2025-04-10 17:46:11 +05:30
94 changed files with 6519 additions and 3372 deletions

View File

@@ -1,9 +1,9 @@
import { useFrame, useThree } from '@react-three/fiber';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { QuadraticBezierLine } from '@react-three/drei';
import { useIsConnecting, useSimulationStates, useSocketStore } from '../../../store/store';
import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore';
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt';
@@ -11,28 +11,23 @@ import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/
function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
const { activeModule } = useModuleStore();
const { gl, raycaster, scene, pointer, camera } = useThree();
const { deleteTool } = useDeleteTool();
const { renderDistance } = useRenderDistance();
const { setIsConnecting } = useIsConnecting();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { isPlaying } = usePlayButtonStore();
const { socket } = useSocketStore();
const groupRefs = useRef<{ [key: string]: any }>({});
const [firstSelected, setFirstSelected] = useState<{
modelUUID: string;
sphereUUID: string;
position: THREE.Vector3;
isCorner: boolean;
} | null>(null);
const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null);
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null);
const [helperlineColor, setHelperLineColor] = useState<string>('red');
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
const updatePathConnections = (
fromModelUUID: string,
fromPointUUID: string,
toModelUUID: string,
toPointUUID: string
) => {
const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => {
const updatedPaths = simulationStates.map(path => {
if (path.type === 'Conveyor') {
// Handle outgoing connections from Conveyor
if (path.modeluuid === fromModelUUID) {
return {
...path,
@@ -61,6 +56,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
})
};
}
// Handle incoming connections to Conveyor
else if (path.modeluuid === toModelUUID) {
return {
...path,
@@ -167,82 +163,170 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
return path;
}
// else if (path.type === 'StaticMachine') {
// if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
// const newTarget = {
// modelUUID: toModelUUID,
// pointUUID: toPointUUID
// };
// const existingTargets = path.points.connections.targets || [];
else if (path.type === 'StaticMachine') {
// Handle outgoing connections from StaticMachine
if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
const newTarget = {
modelUUID: toModelUUID,
pointUUID: toPointUUID
};
// // Check if target is an ArmBot
// const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
// if (toPath?.type !== 'ArmBot') {
// console.log("StaticMachine can only connect to ArmBot");
// return path;
// }
// Ensure target is an ArmBot
const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
if (toPath?.type !== 'ArmBot') {
console.log("StaticMachine can only connect to ArmBot");
return path;
}
// // Check if already has a connection
// if (existingTargets.length >= 1) {
// console.log("StaticMachine can have only one connection");
// return path;
// }
const existingTargets = path.points.connections.targets || [];
// if (!existingTargets.some(target =>
// target.modelUUID === newTarget.modelUUID &&
// target.pointUUID === newTarget.pointUUID
// )) {
// return {
// ...path,
// points: {
// ...path.points,
// connections: {
// ...path.points.connections,
// targets: [...existingTargets, newTarget]
// }
// }
// };
// }
// }
// // Handle incoming connections to StaticMachine
// else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
// const reverseTarget = {
// modelUUID: fromModelUUID,
// pointUUID: fromPointUUID
// };
// const existingTargets = path.points.connections.targets || [];
// Allow only one connection
if (existingTargets.length >= 1) {
console.log("StaticMachine can only have one connection");
return path;
}
// // Check if source is an ArmBot
// const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
// if (fromPath?.type !== 'ArmBot') {
// console.log("StaticMachine can only connect to ArmBot");
// return path;
// }
if (!existingTargets.some(target =>
target.modelUUID === newTarget.modelUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, newTarget]
}
}
};
}
}
// // Check if already has a connection
// if (existingTargets.length >= 1) {
// console.log("StaticMachine can have only one connection");
// return path;
// }
// Handle incoming connections to StaticMachine
else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
const reverseTarget = {
modelUUID: fromModelUUID,
pointUUID: fromPointUUID
};
const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
if (fromPath?.type !== 'ArmBot') {
console.log("StaticMachine can only be connected from ArmBot");
return path;
}
const existingTargets = path.points.connections.targets || [];
if (existingTargets.length >= 1) {
console.log("StaticMachine can only have one connection");
return path;
}
if (!existingTargets.some(target =>
target.modelUUID === reverseTarget.modelUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, reverseTarget]
}
}
};
}
}
return path;
}
else if (path.type === 'ArmBot') {
// Handle outgoing connections from ArmBot
if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
const newTarget = {
modelUUID: toModelUUID,
pointUUID: toPointUUID
};
const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
if (!toPath) return path;
const existingTargets = path.points.connections.targets || [];
// Check if connecting to a StaticMachine and already connected to one
const alreadyConnectedToStatic = existingTargets.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
if (toPath.type === 'StaticMachine') {
if (alreadyConnectedToStatic) {
console.log("ArmBot can only connect to one StaticMachine");
return path;
}
}
if (!existingTargets.some(target =>
target.modelUUID === newTarget.modelUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, newTarget]
}
}
};
}
}
// Handle incoming connections to ArmBot
else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
const reverseTarget = {
modelUUID: fromModelUUID,
pointUUID: fromPointUUID
};
const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
if (!fromPath) return path;
const existingTargets = path.points.connections.targets || [];
const alreadyConnectedFromStatic = existingTargets.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
if (fromPath.type === 'StaticMachine') {
if (alreadyConnectedFromStatic) {
console.log("ArmBot can only be connected from one StaticMachine");
return path;
}
}
if (!existingTargets.some(target =>
target.modelUUID === reverseTarget.modelUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, reverseTarget]
}
}
};
}
}
return path;
}
// if (!existingTargets.some(target =>
// target.modelUUID === reverseTarget.modelUUID &&
// target.pointUUID === reverseTarget.pointUUID
// )) {
// return {
// ...path,
// points: {
// ...path.points,
// connections: {
// ...path.points.connections,
// targets: [...existingTargets, reverseTarget]
// }
// }
// };
// }
// }
// return path;
// }
return path;
});
@@ -255,7 +339,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
updateBackend(updatedPathDetails);
};
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
if (updatedPaths.length === 0) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
@@ -293,6 +377,38 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
socket.emit('v2:model-asset:updateEventData', data);
} else if (updatedPath.type === 'StaticMachine') {
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "StaticMachine", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "StaticMachine", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
} else if (updatedPath.type === 'ArmBot') {
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "ArmBot", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "ArmBot", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
}
})
@@ -397,7 +513,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
// For Vehicles, check if they're already connected to anything
if (intersected.userData.path.type === 'Vehicle') {
console.log('intersected: ', intersected);
const vehicleConnections = intersected.userData.path.points.connections.targets.length;
if (vehicleConnections >= 1) {
console.log("Vehicle can only have one connection");
@@ -437,6 +552,69 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return;
}
// Check if StaticMachine is involved in the connection
if ((firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') ||
(secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot')) {
console.log("StaticMachine can only connect to ArmBot");
return;
}
// Check if StaticMachine already has a connection
if (firstPath?.type === 'StaticMachine') {
const staticConnections = firstPath.points.connections.targets.length;
if (staticConnections >= 1) {
console.log("StaticMachine can only have one connection");
return;
}
}
if (secondPath?.type === 'StaticMachine') {
const staticConnections = secondPath.points.connections.targets.length;
if (staticConnections >= 1) {
console.log("StaticMachine can only have one connection");
return;
}
}
// Check if ArmBot is involved
if ((firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') ||
(secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine')) {
const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath;
const staticPath = firstPath?.type === 'StaticMachine' ? firstPath : secondPath;
const armBotConnections = armBotPath.points.connections.targets || [];
const alreadyConnectedToStatic = armBotConnections.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
if (alreadyConnectedToStatic) {
console.log("ArmBot can only connect to one StaticMachine");
return;
}
const staticConnections = staticPath.points.connections.targets.length;
if (staticConnections >= 1) {
console.log("StaticMachine can only have one connection");
return;
}
}
// Prevent ArmBot ↔ ArmBot
if (firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot') {
console.log("Cannot connect two ArmBots together");
return;
}
// If one is ArmBot, ensure the other is StaticMachine or Conveyor
if (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') {
const otherType = firstPath?.type === 'ArmBot' ? secondPath?.type : firstPath?.type;
if (otherType !== 'StaticMachine' && otherType !== 'Conveyor') {
console.log("ArmBot can only connect to Conveyors or one StaticMachine");
return;
}
}
// At least one must be start/end point
if (!firstSelected.isCorner && !isStartOrEnd) {
console.log("At least one of the selected spheres must be a start or end point.");
@@ -444,20 +622,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
// All checks passed - make the connection
handleAddConnection(
firstSelected.modelUUID,
firstSelected.sphereUUID,
modelUUID,
sphereUUID
);
handleAddConnection(firstSelected.modelUUID, firstSelected.sphereUUID, modelUUID, sphereUUID);
} else {
// First selection - just store it
setFirstSelected({
modelUUID,
sphereUUID,
position: worldPosition,
isCorner: isStartOrEnd
});
setFirstSelected({ modelUUID, sphereUUID, position: worldPosition, isCorner: isStartOrEnd });
setIsConnecting(true);
}
}
@@ -470,7 +638,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
};
if (activeModule === 'simulation') {
if (activeModule === 'simulation' && !deleteTool) {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
@@ -487,7 +655,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("contextmenu", onContextMenu);
};
}, [camera, scene, raycaster, firstSelected, simulationStates]);
}, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]);
useFrame(() => {
Object.values(groupRefs.current).forEach((group) => {
if (group) {
const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position);
group.visible = ((distance <= renderDistance) && !isPlaying);
}
});
});
useFrame(() => {
if (firstSelected) {
@@ -511,9 +688,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
}
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) =>
obj.object.name.includes("events-sphere")
);
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => obj.object.name.includes("events-sphere"));
if (sphereIntersects.length > 0) {
const sphere = sphereIntersects[0].object;
@@ -528,15 +703,21 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle';
// Inside the useFrame hook, where we check for snapped spheres:
const isConnectable = (pathData.type === 'Vehicle' ||
const isConnectable = (
pathData.type === 'Vehicle' ||
pathData.type === 'ArmBot' ||
(pathData.points.length > 0 && (
sphereUUID === pathData.points[0].uuid ||
sphereUUID === pathData.points[pathData.points.length - 1].uuid
))) &&
sphereUUID === pathData.points[pathData.points.length - 1].uuid ||
(pathData.type === 'Conveyor' && firstPath?.type === 'ArmBot') // Allow ArmBot to connect to middle points
))
) &&
!isVehicleToVehicle &&
!(firstPath?.type === 'Conveyor' &&
!(
firstPath?.type === 'Conveyor' &&
pathData.type === 'Conveyor' &&
!firstSelected.isCorner);
!firstSelected.isCorner
);
// Check for duplicate connection (regardless of path type)
const isDuplicateConnection = simulationStates.some(path => {
@@ -574,12 +755,50 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
(firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') ||
(secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor');
// Check if StaticMachine is connecting to non-ArmBot
const isStaticMachineToNonArmBot =
(firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') ||
(secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot');
// Check if StaticMachine already has a connection
const isStaticMachineAtMaxConnections =
(firstPath?.type === 'StaticMachine' && firstPath.points.connections.targets.length >= 1) ||
(secondPath?.type === 'StaticMachine' && secondPath.points.connections.targets.length >= 1);
// Check if ArmBot is connecting to StaticMachine
const isArmBotToStaticMachine =
(firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') ||
(secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine');
// Prevent multiple StaticMachine connections to ArmBot
let isArmBotAlreadyConnectedToStatic = false;
if (isArmBotToStaticMachine) {
const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath;
isArmBotAlreadyConnectedToStatic = armBotPath.points.connections.targets.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
}
// Prevent ArmBot to ArmBot
const isArmBotToArmBot = firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot';
// If ArmBot is involved, other must be Conveyor or StaticMachine
const isArmBotToInvalidType = (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') &&
!(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' ||
secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine');
if (
!isDuplicateConnection &&
!isVehicleToVehicle &&
!isNonVehicleAlreadyConnected &&
!isVehicleAtMaxConnections &&
!isVehicleConnectingToNonConveyor &&
!isStaticMachineToNonArmBot &&
!isStaticMachineAtMaxConnections &&
!isArmBotToArmBot &&
!isArmBotToInvalidType &&
!isArmBotAlreadyConnectedToStatic &&
firstSelected.sphereUUID !== sphereUUID &&
firstSelected.modelUUID !== modelUUID &&
(firstSelected.isCorner || isConnectable) &&
@@ -596,6 +815,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
} else {
isInvalidConnection = true;
}
}
if (snappedSphere) {
@@ -632,14 +852,144 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
});
const removeConnections = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => {
const updatedStates = simulationStates.map(state => {
// Handle Conveyor (which has multiple points)
if (state.type === 'Conveyor') {
const updatedConveyor: Types.ConveyorEventsSchema = {
...state,
points: state.points.map(point => {
// Check if this point is either connection1 or connection2
if ((state.modeluuid === connection1.model && point.uuid === connection1.point) ||
(state.modeluuid === connection2.model && point.uuid === connection2.point)) {
return {
...point,
connections: {
...point.connections,
targets: point.connections.targets.filter(target => {
// Remove the target that matches the other connection
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
}
};
}
return point;
})
};
return updatedConveyor;
}
// Handle Vehicle
else if (state.type === 'Vehicle') {
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
const updatedVehicle: Types.VehicleEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(target => {
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
},
// Ensure all required Vehicle point properties are included
speed: state.points.speed,
actions: state.points.actions
}
};
return updatedVehicle;
}
}
// Handle StaticMachine
else if (state.type === 'StaticMachine') {
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
const updatedStaticMachine: Types.StaticMachineEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(target => {
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
},
// Ensure all required StaticMachine point properties are included
actions: state.points.actions,
triggers: state.points.triggers
}
};
return updatedStaticMachine;
}
}
// Handle ArmBot
else if (state.type === 'ArmBot') {
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
const updatedArmBot: Types.ArmBotEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(target => {
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
},
actions: {
...state.points.actions,
processes: state.points.actions.processes?.filter(process => {
return !(
process.startPoint === connection1.point ||
process.endPoint === connection1.point ||
process.startPoint === connection2.point ||
process.endPoint === connection2.point
);
}) || []
},
triggers: state.points.triggers
}
};
return updatedArmBot;
}
}
return state;
});
const updatedPaths = updatedStates.filter(state =>
state.modeluuid === connection1.model || state.modeluuid === connection2.model
);
updateBackend(updatedPaths);
setSimulationStates(updatedStates);
};
return (
<group name='simulationConnectionGroup' visible={!isPlaying} >
<group name='simulationConnectionGroup' visible={!isPlaying}>
{simulationStates.flatMap(path => {
if (path.type === 'Conveyor') {
return path.points.flatMap(point =>
point.connections.targets.map((target, index) => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
if (targetPath?.type === 'Vehicle') return null;
if (targetPath?.type !== 'Conveyor' && targetPath?.type !== 'ArmBot') return null;
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
@@ -652,31 +1002,48 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2);
return (
<QuadraticBezierLine
key={`${point.uuid}-${target.pointUUID}-${index}`}
ref={(el) => (groupRefs.current[`${point.uuid}-${target.pointUUID}-${index}`] = el!)}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="white"
color={
deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}`
? 'red'
: targetPath?.type === 'ArmBot'
? '#42a5f5'
: 'white'
}
lineWidth={4}
dashed
dashed={(deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}`) ? false : true}
dashSize={0.75}
dashScale={20}
onPointerOver={() => setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)}
onPointerOut={() => setHoveredLineKey(null)}
onClick={() => {
if (deleteTool) {
const connection1 = { model: path.modeluuid, point: point.uuid }
const connection2 = { model: target.modelUUID, point: target.pointUUID }
removeConnections(connection1, connection2)
}
}}
userData={target}
/>
);
}
return null;
})
);
} else if (path.type === 'Vehicle') {
}
if (path.type === 'Vehicle') {
return path.points.connections.targets.map((target, index) => {
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
@@ -689,30 +1056,97 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2);
return (
<QuadraticBezierLine
key={`${path.points.uuid}-${target.pointUUID}-${index}`}
ref={(el) => (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="orange"
color={
deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`
? 'red'
: 'orange'
}
lineWidth={4}
dashed
dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true}
dashSize={0.75}
dashScale={20}
onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)}
onPointerOut={() => setHoveredLineKey(null)}
onClick={() => {
if (deleteTool) {
const connection1 = { model: path.modeluuid, point: path.points.uuid }
const connection2 = { model: target.modelUUID, point: target.pointUUID }
removeConnections(connection1, connection2)
}
}}
userData={target}
/>
);
}
return null;
});
}
if (path.type === 'StaticMachine') {
return path.points.connections.targets.map((target, index) => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
if (targetPath?.type !== 'ArmBot') return null;
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
if (fromSphere && toSphere) {
const fromWorldPosition = new THREE.Vector3();
const toWorldPosition = new THREE.Vector3();
fromSphere.getWorldPosition(fromWorldPosition);
toSphere.getWorldPosition(toWorldPosition);
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2);
return (
<QuadraticBezierLine
key={`${path.points.uuid}-${target.pointUUID}-${index}`}
ref={(el) => (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color={
deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`
? 'red'
: '#42a5f5'
}
lineWidth={4}
dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true}
dashSize={0.75}
dashScale={20}
onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)}
onPointerOut={() => setHoveredLineKey(null)}
onClick={() => {
if (deleteTool) {
const connection1 = { model: path.modeluuid, point: path.points.uuid }
const connection2 = { model: target.modelUUID, point: target.pointUUID }
removeConnections(connection1, connection2)
}
}}
userData={target}
/>
);
}
return null;
});
}
return [];
})}
@@ -730,6 +1164,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
)}
</group>
);
}
export default PathConnector;

View File

@@ -206,6 +206,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
@@ -271,10 +272,11 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
})}
</group>
);
} else if (path.type === "Vehicle" || path.type === "StaticMachine") {
} else if (path.type === "Vehicle") {
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
@@ -323,6 +325,112 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
</Sphere>
</group>
);
} else if (path.type === "StaticMachine") {
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
<Sphere
key={path.points.uuid}
uuid={path.points.uuid}
position={path.points.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
points: sphereRefs.current[path.points.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ points: path.points, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="yellow" />
</Sphere>
</group>
);
} else if (path.type === "ArmBot") {
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
<Sphere
key={path.points.uuid}
uuid={path.points.uuid}
position={path.points.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
points: sphereRefs.current[path.points.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ points: path.points, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="pink" />
</Sphere>
</group>
);
}
return null;
})}

View File

@@ -9,17 +9,20 @@ import { useProcessAnimation } from "./useProcessAnimations";
import ProcessObject from "./processObject";
import { ProcessData } from "./types";
import { useSimulationStates } from "../../../store/store";
import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
interface ProcessContainerProps {
processes: ProcessData[];
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
agvRef: any;
MaterialRef: any;
}
const ProcessAnimator: React.FC<ProcessContainerProps> = ({
processes,
setProcesses,
agvRef,
MaterialRef,
}) => {
const gltf = useLoader(GLTFLoader, crate) as GLTF;
const groupRef = useRef<THREE.Group>(null);
@@ -44,11 +47,57 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
() => ({
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
// Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
Default: new THREE.MeshStandardMaterial(),
}),
[]
);
useEffect(() => {
// Update material references for all spawned objects
Object.entries(animationStates).forEach(([processId, processState]) => {
Object.keys(processState.spawnedObjects).forEach((objectId) => {
const entry = {
processId,
objectId,
};
const materialType =
processState.spawnedObjects[objectId]?.currentMaterialType;
if (!materialType) {
return;
}
const matRefArray = MaterialRef.current;
// Find existing material group
const existing = matRefArray.find(
(entryGroup: { material: string; objects: any[] }) =>
entryGroup.material === materialType
);
if (existing) {
// Check if this processId + objectId already exists
const alreadyExists = existing.objects.some(
(o: any) =>
o.processId === entry.processId && o.objectId === entry.objectId
);
if (!alreadyExists) {
existing.objects.push(entry);
}
} else {
// Create new group for this material type
matRefArray.push({
material: materialType,
objects: [entry],
});
}
});
});
}, [animationStates, MaterialRef, agvRef]);
// In processAnimator.tsx - only the relevant spawn logic part that needs fixes
useFrame(() => {
@@ -252,14 +301,39 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
const nextPointIdx = stateRef.currentIndex + 1;
const isLastPoint = nextPointIdx >= path.length;
// if (isLastPoint) {
// if (currentPointData?.actions) {
// const shouldStop = !hasNonInheritActions(
// currentPointData.actions
// );
// if (shouldStop) {
// return;
// }
// }
// }
if (isLastPoint) {
if (currentPointData?.actions) {
const shouldStop = !hasNonInheritActions(
const hasNonInherit = hasNonInheritActions(
currentPointData.actions
);
if (shouldStop) {
if (!hasNonInherit) {
// Remove the object if all actions are inherit
updatedObjects[objectId] = {
...obj,
visible: false,
state: { ...stateRef, isAnimating: false },
};
return;
}
} else {
// No actions at last point - remove the object
updatedObjects[objectId] = {
...obj,
visible: false,
state: { ...stateRef, isAnimating: false },
};
return;
}
}
@@ -329,6 +403,7 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
.filter(([_, obj]) => obj.visible)
.map(([objectId, obj]) => {
const process = processedProcesses.find((p) => p.id === processId);
const renderAs = process?.renderAs || "custom";
return (

View File

@@ -6,18 +6,24 @@ interface ProcessContainerProps {
processes: any[];
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
agvRef: any;
MaterialRef: any;
}
const ProcessContainer: React.FC<ProcessContainerProps> = ({
processes,
setProcesses,
agvRef
agvRef,
MaterialRef,
}) => {
console.log("processes: ", processes);
return (
<>
<ProcessCreator onProcessesCreated={setProcesses} />
<ProcessAnimator processes={processes} setProcesses={setProcesses} agvRef={agvRef}/>
<ProcessAnimator
processes={processes}
setProcesses={setProcesses}
agvRef={agvRef}
MaterialRef={MaterialRef}
/>
</>
);
};

View File

@@ -25,7 +25,6 @@ interface EnhancedProcessAnimationState extends ProcessAnimationState {
pointId: string;
objectId: string;
triggerId: string;
hasSpawnedZeroIntervalObject?: boolean;
}>;
}
@@ -139,7 +138,6 @@ export const useProcessAnimation = (
processDelayDuration: 0,
triggerCounts,
triggerLogs: [],
hasSpawnedZeroIntervalObject: false, // Initialize the new property
};
});
@@ -453,7 +451,7 @@ export const useProcessAnimation = (
// Increment expectedHitCount if not playing
if (!vehicleEntry.isplaying) {
vehicleEntry.expectedHitCount = processTotalHits + 1;
vehicleEntry.expectedHitCount = processTotalHits;
}
// Set vehicle's hitCount to the processTotalHits

View File

@@ -18,6 +18,7 @@ function Simulation() {
const { simulationStates, setSimulationStates } = useSimulationStates();
const [processes, setProcesses] = useState<any[]>([]);
const agvRef = useRef([]);
const MaterialRef = useRef([]);
return (
<>
@@ -26,8 +27,17 @@ function Simulation() {
<>
<PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer processes={processes} setProcesses={setProcesses} agvRef={agvRef} />
<Agv processes={processes} agvRef={agvRef} />
<ProcessContainer
processes={processes}
setProcesses={setProcesses}
agvRef={agvRef}
MaterialRef={MaterialRef}
/>
<Agv
processes={processes}
agvRef={agvRef}
MaterialRef={MaterialRef}
/>
</>
)}
</>