- Updated all instances of `simulationPaths` to `simulationStates` across multiple components including copyPasteControls, duplicationControls, moveControls, rotateControls, selectionControls, and others. - Adjusted related state management hooks in the store to reflect the change from `simulationPaths` to `simulationStates`. - Ensured that all references to simulation paths in the simulation logic and UI components are consistent with the new naming convention.
803 lines
24 KiB
TypeScript
803 lines
24 KiB
TypeScript
// import React, {
|
|
// useEffect,
|
|
// useMemo,
|
|
// useState,
|
|
// useCallback,
|
|
// useRef,
|
|
// } from "react";
|
|
// import { useSimulationStates } from "../../../store/store";
|
|
// import * as THREE from "three";
|
|
// import { useThree } from "@react-three/fiber";
|
|
// import {
|
|
// ConveyorEventsSchema,
|
|
// VehicleEventsSchema,
|
|
// } from "../../../types/world/worldTypes";
|
|
|
|
// // Type definitions
|
|
// export interface PointAction {
|
|
// uuid: string;
|
|
// name: string;
|
|
// type: string;
|
|
// material: string;
|
|
// delay: number | string;
|
|
// spawnInterval: string | number;
|
|
// isUsed: boolean;
|
|
// }
|
|
|
|
// export interface PathPoint {
|
|
// uuid: string;
|
|
// position: [number, number, number];
|
|
// actions: PointAction[];
|
|
// connections: {
|
|
// targets: Array<{ pathUUID: string }>;
|
|
// };
|
|
// }
|
|
|
|
// export interface SimulationPath {
|
|
// modeluuid: string;
|
|
// points: PathPoint[];
|
|
// pathPosition: [number, number, number];
|
|
// speed?: number;
|
|
// }
|
|
|
|
// export interface Process {
|
|
// id: string;
|
|
// paths: SimulationPath[];
|
|
// animationPath: THREE.Vector3[];
|
|
// pointActions: PointAction[][];
|
|
// speed: number;
|
|
// }
|
|
|
|
// interface ProcessCreatorProps {
|
|
// onProcessesCreated: (processes: Process[]) => void;
|
|
// }
|
|
|
|
// // Convert event schemas to SimulationPath
|
|
// function convertToSimulationPath(
|
|
// path: ConveyorEventsSchema | VehicleEventsSchema
|
|
// ): SimulationPath {
|
|
// const { modeluuid } = path;
|
|
|
|
// // Simplified normalizeAction function that preserves exact original properties
|
|
// const normalizeAction = (action: any): PointAction => {
|
|
// return { ...action }; // Return exact copy with no modifications
|
|
// };
|
|
|
|
// if (path.type === "Conveyor") {
|
|
// return {
|
|
// modeluuid,
|
|
// points: path.points.map((point) => ({
|
|
// uuid: point.uuid,
|
|
// position: point.position,
|
|
// actions: point.actions.map(normalizeAction), // Preserve exact actions
|
|
// connections: {
|
|
// targets: point.connections.targets.map((target) => ({
|
|
// pathUUID: target.pathUUID,
|
|
// })),
|
|
// },
|
|
// })),
|
|
// pathPosition: path.position,
|
|
// speed:
|
|
// typeof path.speed === "string"
|
|
// ? parseFloat(path.speed) || 1
|
|
// : path.speed || 1,
|
|
// };
|
|
// } else {
|
|
// return {
|
|
// modeluuid,
|
|
// points: [
|
|
// {
|
|
// uuid: path.point.uuid,
|
|
// position: path.point.position,
|
|
// actions: Array.isArray(path.point.actions)
|
|
// ? path.point.actions.map(normalizeAction)
|
|
// : [normalizeAction(path.point.actions)],
|
|
// connections: {
|
|
// targets: path.point.connections.targets.map((target) => ({
|
|
// pathUUID: target.pathUUID,
|
|
// })),
|
|
// },
|
|
// },
|
|
// ],
|
|
// pathPosition: path.position,
|
|
// speed: path.point.speed || 1,
|
|
// };
|
|
// }
|
|
// }
|
|
|
|
// // Custom shallow comparison for arrays
|
|
// const areArraysEqual = (a: any[], b: any[]) => {
|
|
// if (a.length !== b.length) return false;
|
|
// for (let i = 0; i < a.length; i++) {
|
|
// if (a[i] !== b[i]) return false;
|
|
// }
|
|
// return true;
|
|
// };
|
|
|
|
// // Helper function to create an empty process
|
|
// const createEmptyProcess = (): Process => ({
|
|
// id: `process-${Math.random().toString(36).substring(2, 11)}`,
|
|
// paths: [],
|
|
// animationPath: [],
|
|
// pointActions: [],
|
|
// speed: 1,
|
|
// });
|
|
|
|
// // Enhanced connection checking function
|
|
// function shouldReverseNextPath(
|
|
// currentPath: SimulationPath,
|
|
// nextPath: SimulationPath
|
|
// ): boolean {
|
|
// if (nextPath.points.length !== 3) return false;
|
|
|
|
// const currentLastPoint = currentPath.points[currentPath.points.length - 1];
|
|
// const nextFirstPoint = nextPath.points[0];
|
|
// const nextLastPoint = nextPath.points[nextPath.points.length - 1];
|
|
|
|
// // Check if current last connects to next last (requires reversal)
|
|
// const connectsToLast = currentLastPoint.connections.targets.some(
|
|
// (target) =>
|
|
// target.pathUUID === nextPath.modeluuid &&
|
|
// nextLastPoint.connections.targets.some(
|
|
// (t) => t.pathUUID === currentPath.modeluuid
|
|
// )
|
|
// );
|
|
|
|
// // Check if current last connects to next first (no reversal needed)
|
|
// const connectsToFirst = currentLastPoint.connections.targets.some(
|
|
// (target) =>
|
|
// target.pathUUID === nextPath.modeluuid &&
|
|
// nextFirstPoint.connections.targets.some(
|
|
// (t) => t.pathUUID === currentPath.modeluuid
|
|
// )
|
|
// );
|
|
|
|
// // Only reverse if connected to last point and not to first point
|
|
// return connectsToLast && !connectsToFirst;
|
|
// }
|
|
|
|
// // Updated path adjustment function
|
|
// function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] {
|
|
// if (paths.length < 2) return paths;
|
|
|
|
// const adjustedPaths = [...paths];
|
|
|
|
// for (let i = 0; i < adjustedPaths.length - 1; i++) {
|
|
// const currentPath = adjustedPaths[i];
|
|
// const nextPath = adjustedPaths[i + 1];
|
|
|
|
// if (shouldReverseNextPath(currentPath, nextPath)) {
|
|
// const reversedPoints = [
|
|
// nextPath.points[2],
|
|
// nextPath.points[1],
|
|
// nextPath.points[0],
|
|
// ];
|
|
|
|
// adjustedPaths[i + 1] = {
|
|
// ...nextPath,
|
|
// points: reversedPoints,
|
|
// };
|
|
// }
|
|
// }
|
|
|
|
// return adjustedPaths;
|
|
// }
|
|
|
|
// // Main hook for process creation
|
|
// export function useProcessCreation() {
|
|
// const { scene } = useThree();
|
|
// const [processes, setProcesses] = useState<Process[]>([]);
|
|
|
|
// const hasSpawnAction = useCallback((path: SimulationPath): boolean => {
|
|
// return path.points.some((point) =>
|
|
// point.actions.some((action) => action.type.toLowerCase() === "spawn")
|
|
// );
|
|
// }, []);
|
|
|
|
// const createProcess = useCallback(
|
|
// (paths: SimulationPath[]): Process => {
|
|
// if (!paths || paths.length === 0) {
|
|
// return createEmptyProcess();
|
|
// }
|
|
|
|
// const animationPath: THREE.Vector3[] = [];
|
|
// const pointActions: PointAction[][] = [];
|
|
// const processSpeed = paths[0]?.speed || 1;
|
|
|
|
// for (const path of paths) {
|
|
// for (const point of path.points) {
|
|
// const obj = scene.getObjectByProperty("uuid", point.uuid);
|
|
// if (!obj) {
|
|
// console.warn(`Object with UUID ${point.uuid} not found in scene`);
|
|
// continue;
|
|
// }
|
|
|
|
// const position = obj.getWorldPosition(new THREE.Vector3());
|
|
// animationPath.push(position.clone());
|
|
// pointActions.push(point.actions);
|
|
// }
|
|
// }
|
|
|
|
// return {
|
|
// id: `process-${Math.random().toString(36).substring(2, 11)}`,
|
|
// paths,
|
|
// animationPath,
|
|
// pointActions,
|
|
// speed: processSpeed,
|
|
// };
|
|
// },
|
|
// [scene]
|
|
// );
|
|
|
|
// const getAllConnectedPaths = useCallback(
|
|
// (
|
|
// initialPath: SimulationPath,
|
|
// allPaths: SimulationPath[],
|
|
// visited: Set<string> = new Set()
|
|
// ): SimulationPath[] => {
|
|
// const connectedPaths: SimulationPath[] = [];
|
|
// const queue: SimulationPath[] = [initialPath];
|
|
// visited.add(initialPath.modeluuid);
|
|
|
|
// const pathMap = new Map<string, SimulationPath>();
|
|
// allPaths.forEach((path) => pathMap.set(path.modeluuid, path));
|
|
|
|
// while (queue.length > 0) {
|
|
// const currentPath = queue.shift()!;
|
|
// connectedPaths.push(currentPath);
|
|
|
|
// // Process outgoing connections
|
|
// for (const point of currentPath.points) {
|
|
// for (const target of point.connections.targets) {
|
|
// if (!visited.has(target.pathUUID)) {
|
|
// const targetPath = pathMap.get(target.pathUUID);
|
|
// if (targetPath) {
|
|
// visited.add(target.pathUUID);
|
|
// queue.push(targetPath);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// // Process incoming connections
|
|
// for (const [uuid, path] of pathMap) {
|
|
// if (!visited.has(uuid)) {
|
|
// const hasConnectionToCurrent = path.points.some((point) =>
|
|
// point.connections.targets.some(
|
|
// (t) => t.pathUUID === currentPath.modeluuid
|
|
// )
|
|
// );
|
|
// if (hasConnectionToCurrent) {
|
|
// visited.add(uuid);
|
|
// queue.push(path);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// return connectedPaths;
|
|
// },
|
|
// []
|
|
// );
|
|
|
|
// const createProcessesFromPaths = useCallback(
|
|
// (paths: SimulationPath[]): Process[] => {
|
|
// if (!paths || paths.length === 0) return [];
|
|
|
|
// const visited = new Set<string>();
|
|
// const processes: Process[] = [];
|
|
// const pathMap = new Map<string, SimulationPath>();
|
|
// paths.forEach((path) => pathMap.set(path.modeluuid, path));
|
|
|
|
// for (const path of paths) {
|
|
// if (!visited.has(path.modeluuid) && hasSpawnAction(path)) {
|
|
// const connectedPaths = getAllConnectedPaths(path, paths, visited);
|
|
// const adjustedPaths = adjustPathPointsOrder(connectedPaths);
|
|
// const process = createProcess(adjustedPaths);
|
|
// processes.push(process);
|
|
// }
|
|
// }
|
|
|
|
// return processes;
|
|
// },
|
|
// [createProcess, getAllConnectedPaths, hasSpawnAction]
|
|
// );
|
|
|
|
// return {
|
|
// processes,
|
|
// createProcessesFromPaths,
|
|
// setProcesses,
|
|
// };
|
|
// }
|
|
|
|
// const ProcessCreator: React.FC<ProcessCreatorProps> = React.memo(
|
|
// ({ onProcessesCreated }) => {
|
|
// const { simulationStates } = useSimulationStates();
|
|
// const { createProcessesFromPaths } = useProcessCreation();
|
|
// const prevPathsRef = useRef<SimulationPath[]>([]);
|
|
// const prevProcessesRef = useRef<Process[]>([]);
|
|
|
|
// const convertedPaths = useMemo((): SimulationPath[] => {
|
|
// if (!simulationStates) return [];
|
|
// return simulationStates.map((path) =>
|
|
// convertToSimulationPath(
|
|
// path as ConveyorEventsSchema | VehicleEventsSchema
|
|
// )
|
|
// );
|
|
// }, [simulationStates]);
|
|
|
|
// const pathsDependency = useMemo(() => {
|
|
// if (!convertedPaths) return null;
|
|
// return convertedPaths.map((path) => ({
|
|
// id: path.modeluuid,
|
|
// hasSpawn: path.points.some((p: PathPoint) =>
|
|
// p.actions.some((a: PointAction) => a.type.toLowerCase() === "spawn")
|
|
// ),
|
|
// connections: path.points
|
|
// .flatMap((p: PathPoint) =>
|
|
// p.connections.targets.map((t: { pathUUID: string }) => t.pathUUID)
|
|
// )
|
|
// .join(","),
|
|
// }));
|
|
// }, [convertedPaths]);
|
|
|
|
// useEffect(() => {
|
|
// if (!convertedPaths || convertedPaths.length === 0) {
|
|
// if (prevProcessesRef.current.length > 0) {
|
|
// onProcessesCreated([]);
|
|
// prevProcessesRef.current = [];
|
|
// }
|
|
// return;
|
|
// }
|
|
|
|
// if (areArraysEqual(prevPathsRef.current, convertedPaths)) {
|
|
// return;
|
|
// }
|
|
|
|
// prevPathsRef.current = convertedPaths;
|
|
// const newProcesses = createProcessesFromPaths(convertedPaths);
|
|
|
|
// // console.log("--- Action Types in Paths ---");
|
|
// // convertedPaths.forEach((path) => {
|
|
// // path.points.forEach((point) => {
|
|
// // point.actions.forEach((action) => {
|
|
// // console.log(
|
|
// // `Path ${path.modeluuid}, Point ${point.uuid}: ${action.type}`
|
|
// // );
|
|
// // });
|
|
// // });
|
|
// // });
|
|
// // console.log("New processes:", newProcesses);
|
|
|
|
// if (
|
|
// newProcesses.length !== prevProcessesRef.current.length ||
|
|
// !newProcesses.every(
|
|
// (proc, i) =>
|
|
// proc.paths.length === prevProcessesRef.current[i]?.paths.length &&
|
|
// proc.paths.every(
|
|
// (path, j) =>
|
|
// path.modeluuid ===
|
|
// prevProcessesRef.current[i]?.paths[j]?.modeluuid
|
|
// )
|
|
// )
|
|
// ) {
|
|
// onProcessesCreated(newProcesses);
|
|
// // prevProcessesRef.current = newProcesses;
|
|
// }
|
|
// }, [
|
|
// pathsDependency,
|
|
// onProcessesCreated,
|
|
// convertedPaths,
|
|
// createProcessesFromPaths,
|
|
// ]);
|
|
|
|
// return null;
|
|
// }
|
|
// );
|
|
|
|
// export default ProcessCreator;
|
|
|
|
import React, {
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
useCallback,
|
|
useRef,
|
|
} from "react";
|
|
import { useSimulationStates } from "../../../store/store";
|
|
import * as THREE from "three";
|
|
import { useThree } from "@react-three/fiber";
|
|
import {
|
|
ConveyorEventsSchema,
|
|
VehicleEventsSchema,
|
|
} from "../../../types/world/worldTypes";
|
|
|
|
// Type definitions
|
|
export interface PointAction {
|
|
uuid: string;
|
|
name: string;
|
|
type: string;
|
|
material: string;
|
|
delay: number | string;
|
|
spawnInterval: string | number;
|
|
isUsed: boolean;
|
|
}
|
|
|
|
export interface PathPoint {
|
|
uuid: string;
|
|
position: [number, number, number];
|
|
actions: PointAction[];
|
|
connections: {
|
|
targets: Array<{ pathUUID: string }>;
|
|
};
|
|
}
|
|
|
|
export interface SimulationPath {
|
|
modeluuid: string;
|
|
points: PathPoint[];
|
|
pathPosition: [number, number, number];
|
|
speed?: number;
|
|
}
|
|
|
|
export interface Process {
|
|
id: string;
|
|
paths: SimulationPath[];
|
|
animationPath: THREE.Vector3[];
|
|
pointActions: PointAction[][];
|
|
speed: number;
|
|
}
|
|
|
|
interface ProcessCreatorProps {
|
|
onProcessesCreated: (processes: Process[]) => void;
|
|
}
|
|
|
|
// Convert event schemas to SimulationPath
|
|
function convertToSimulationPath(
|
|
path: ConveyorEventsSchema | VehicleEventsSchema
|
|
): SimulationPath {
|
|
const { modeluuid } = path;
|
|
|
|
// Simplified normalizeAction function that preserves exact original properties
|
|
const normalizeAction = (action: any): PointAction => {
|
|
return { ...action }; // Return exact copy with no modifications
|
|
};
|
|
|
|
if (path.type === "Conveyor") {
|
|
return {
|
|
modeluuid,
|
|
points: path.points.map((point) => ({
|
|
uuid: point.uuid,
|
|
position: point.position,
|
|
actions: point.actions.map(normalizeAction), // Preserve exact actions
|
|
connections: {
|
|
targets: point.connections.targets.map((target) => ({
|
|
pathUUID: target.pathUUID,
|
|
})),
|
|
},
|
|
})),
|
|
pathPosition: path.position,
|
|
speed:
|
|
typeof path.speed === "string"
|
|
? parseFloat(path.speed) || 1
|
|
: path.speed || 1,
|
|
};
|
|
} else {
|
|
return {
|
|
modeluuid,
|
|
points: [
|
|
{
|
|
uuid: path.points.uuid,
|
|
position: path.points.position,
|
|
actions: Array.isArray(path.points.actions)
|
|
? path.points.actions.map(normalizeAction)
|
|
: [normalizeAction(path.points.actions)],
|
|
connections: {
|
|
targets: path.points.connections.targets.map((target) => ({
|
|
pathUUID: target.pathUUID,
|
|
})),
|
|
},
|
|
},
|
|
],
|
|
pathPosition: path.position,
|
|
speed: path.points.speed || 1,
|
|
};
|
|
}
|
|
}
|
|
|
|
// Custom shallow comparison for arrays
|
|
const areArraysEqual = (a: any[], b: any[]) => {
|
|
if (a.length !== b.length) return false;
|
|
for (let i = 0; i < a.length; i++) {
|
|
if (a[i] !== b[i]) return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Helper function to create an empty process
|
|
const createEmptyProcess = (): Process => ({
|
|
id: `process-${Math.random().toString(36).substring(2, 11)}`,
|
|
paths: [],
|
|
animationPath: [],
|
|
pointActions: [],
|
|
speed: 1,
|
|
});
|
|
|
|
// Enhanced connection checking function
|
|
function shouldReverseNextPath(
|
|
currentPath: SimulationPath,
|
|
nextPath: SimulationPath
|
|
): boolean {
|
|
if (nextPath.points.length !== 3) return false;
|
|
|
|
const currentLastPoint = currentPath.points[currentPath.points.length - 1];
|
|
const nextFirstPoint = nextPath.points[0];
|
|
const nextLastPoint = nextPath.points[nextPath.points.length - 1];
|
|
|
|
// Check if current last connects to next last (requires reversal)
|
|
const connectsToLast = currentLastPoint.connections.targets.some(
|
|
(target) =>
|
|
target.pathUUID === nextPath.modeluuid &&
|
|
nextLastPoint.connections.targets.some(
|
|
(t) => t.pathUUID === currentPath.modeluuid
|
|
)
|
|
);
|
|
|
|
// Check if current last connects to next first (no reversal needed)
|
|
const connectsToFirst = currentLastPoint.connections.targets.some(
|
|
(target) =>
|
|
target.pathUUID === nextPath.modeluuid &&
|
|
nextFirstPoint.connections.targets.some(
|
|
(t) => t.pathUUID === currentPath.modeluuid
|
|
)
|
|
);
|
|
|
|
// Only reverse if connected to last point and not to first point
|
|
return connectsToLast && !connectsToFirst;
|
|
}
|
|
|
|
// Check if a point has a spawn action
|
|
function hasSpawnAction(point: PathPoint): boolean {
|
|
return point.actions.some((action) => action.type.toLowerCase() === "spawn");
|
|
}
|
|
|
|
// Ensure spawn point is always at the beginning of the path
|
|
function ensureSpawnPointIsFirst(path: SimulationPath): SimulationPath {
|
|
if (path.points.length !== 3) return path;
|
|
|
|
// If the third point has spawn action and first doesn't, reverse the array
|
|
if (hasSpawnAction(path.points[2]) && !hasSpawnAction(path.points[0])) {
|
|
return {
|
|
...path,
|
|
points: [...path.points].reverse(),
|
|
};
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
// Updated path adjustment function
|
|
function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] {
|
|
if (paths.length < 1) return paths;
|
|
|
|
const adjustedPaths = [...paths];
|
|
|
|
// First ensure all paths have spawn points at the beginning
|
|
for (let i = 0; i < adjustedPaths.length; i++) {
|
|
adjustedPaths[i] = ensureSpawnPointIsFirst(adjustedPaths[i]);
|
|
}
|
|
|
|
// Then handle connections between paths
|
|
for (let i = 0; i < adjustedPaths.length - 1; i++) {
|
|
const currentPath = adjustedPaths[i];
|
|
const nextPath = adjustedPaths[i + 1];
|
|
|
|
if (shouldReverseNextPath(currentPath, nextPath)) {
|
|
const reversedPoints = [
|
|
nextPath.points[2],
|
|
nextPath.points[1],
|
|
nextPath.points[0],
|
|
];
|
|
|
|
adjustedPaths[i + 1] = {
|
|
...nextPath,
|
|
points: reversedPoints,
|
|
};
|
|
}
|
|
}
|
|
|
|
return adjustedPaths;
|
|
}
|
|
|
|
// Main hook for process creation
|
|
export function useProcessCreation() {
|
|
const { scene } = useThree();
|
|
const [processes, setProcesses] = useState<Process[]>([]);
|
|
|
|
const hasSpawnAction = useCallback((path: SimulationPath): boolean => {
|
|
return path.points.some((point) =>
|
|
point.actions.some((action) => action.type.toLowerCase() === "spawn")
|
|
);
|
|
}, []);
|
|
|
|
const createProcess = useCallback(
|
|
(paths: SimulationPath[]): Process => {
|
|
if (!paths || paths.length === 0) {
|
|
return createEmptyProcess();
|
|
}
|
|
|
|
const animationPath: THREE.Vector3[] = [];
|
|
const pointActions: PointAction[][] = [];
|
|
const processSpeed = paths[0]?.speed || 1;
|
|
|
|
for (const path of paths) {
|
|
for (const point of path.points) {
|
|
const obj = scene.getObjectByProperty("uuid", point.uuid);
|
|
if (!obj) {
|
|
console.warn(`Object with UUID ${point.uuid} not found in scene`);
|
|
continue;
|
|
}
|
|
|
|
const position = obj.getWorldPosition(new THREE.Vector3());
|
|
animationPath.push(position.clone());
|
|
pointActions.push(point.actions);
|
|
}
|
|
}
|
|
|
|
return {
|
|
id: `process-${Math.random().toString(36).substring(2, 11)}`,
|
|
paths,
|
|
animationPath,
|
|
pointActions,
|
|
speed: processSpeed,
|
|
};
|
|
},
|
|
[scene]
|
|
);
|
|
|
|
const getAllConnectedPaths = useCallback(
|
|
(
|
|
initialPath: SimulationPath,
|
|
allPaths: SimulationPath[],
|
|
visited: Set<string> = new Set()
|
|
): SimulationPath[] => {
|
|
const connectedPaths: SimulationPath[] = [];
|
|
const queue: SimulationPath[] = [initialPath];
|
|
visited.add(initialPath.modeluuid);
|
|
|
|
const pathMap = new Map<string, SimulationPath>();
|
|
allPaths.forEach((path) => pathMap.set(path.modeluuid, path));
|
|
|
|
while (queue.length > 0) {
|
|
const currentPath = queue.shift()!;
|
|
connectedPaths.push(currentPath);
|
|
|
|
// Process outgoing connections
|
|
for (const point of currentPath.points) {
|
|
for (const target of point.connections.targets) {
|
|
if (!visited.has(target.pathUUID)) {
|
|
const targetPath = pathMap.get(target.pathUUID);
|
|
if (targetPath) {
|
|
visited.add(target.pathUUID);
|
|
queue.push(targetPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process incoming connections
|
|
for (const [uuid, path] of pathMap) {
|
|
if (!visited.has(uuid)) {
|
|
const hasConnectionToCurrent = path.points.some((point) =>
|
|
point.connections.targets.some(
|
|
(t) => t.pathUUID === currentPath.modeluuid
|
|
)
|
|
);
|
|
if (hasConnectionToCurrent) {
|
|
visited.add(uuid);
|
|
queue.push(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return connectedPaths;
|
|
},
|
|
[]
|
|
);
|
|
|
|
const createProcessesFromPaths = useCallback(
|
|
(paths: SimulationPath[]): Process[] => {
|
|
if (!paths || paths.length === 0) return [];
|
|
|
|
const visited = new Set<string>();
|
|
const processes: Process[] = [];
|
|
const pathMap = new Map<string, SimulationPath>();
|
|
paths.forEach((path) => pathMap.set(path.modeluuid, path));
|
|
|
|
for (const path of paths) {
|
|
if (!visited.has(path.modeluuid) && hasSpawnAction(path)) {
|
|
const connectedPaths = getAllConnectedPaths(path, paths, visited);
|
|
const adjustedPaths = adjustPathPointsOrder(connectedPaths);
|
|
const process = createProcess(adjustedPaths);
|
|
processes.push(process);
|
|
}
|
|
}
|
|
|
|
return processes;
|
|
},
|
|
[createProcess, getAllConnectedPaths, hasSpawnAction]
|
|
);
|
|
|
|
return {
|
|
processes,
|
|
createProcessesFromPaths,
|
|
setProcesses,
|
|
};
|
|
}
|
|
|
|
const ProcessCreator: React.FC<ProcessCreatorProps> = React.memo(
|
|
({ onProcessesCreated }) => {
|
|
const { simulationStates } = useSimulationStates();
|
|
const { createProcessesFromPaths } = useProcessCreation();
|
|
const prevPathsRef = useRef<SimulationPath[]>([]);
|
|
const prevProcessesRef = useRef<Process[]>([]);
|
|
|
|
const convertedPaths = useMemo((): SimulationPath[] => {
|
|
if (!simulationStates) return [];
|
|
return simulationStates.map((path) =>
|
|
convertToSimulationPath(
|
|
path as ConveyorEventsSchema | VehicleEventsSchema
|
|
)
|
|
);
|
|
}, [simulationStates]);
|
|
|
|
// Enhanced dependency tracking that includes action types
|
|
const pathsDependency = useMemo(() => {
|
|
if (!convertedPaths) return null;
|
|
return convertedPaths.map((path) => ({
|
|
id: path.modeluuid,
|
|
// Track all action types for each point
|
|
actionSignature: path.points
|
|
.map((point, index) =>
|
|
point.actions.map((action) => `${index}-${action.type}`).join("|")
|
|
)
|
|
.join(","),
|
|
connections: path.points
|
|
.flatMap((p: PathPoint) =>
|
|
p.connections.targets.map((t: { pathUUID: string }) => t.pathUUID)
|
|
)
|
|
.join(","),
|
|
}));
|
|
}, [convertedPaths]);
|
|
|
|
// Force process recreation when paths change
|
|
useEffect(() => {
|
|
if (!convertedPaths || convertedPaths.length === 0) {
|
|
if (prevProcessesRef.current.length > 0) {
|
|
onProcessesCreated([]);
|
|
prevProcessesRef.current = [];
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Always regenerate processes if the pathsDependency has changed
|
|
// This ensures action type changes will be detected
|
|
const newProcesses = createProcessesFromPaths(convertedPaths);
|
|
prevPathsRef.current = convertedPaths;
|
|
|
|
// Always update processes when action types change
|
|
onProcessesCreated(newProcesses);
|
|
prevProcessesRef.current = newProcesses;
|
|
}, [
|
|
pathsDependency, // This now includes action types
|
|
onProcessesCreated,
|
|
convertedPaths,
|
|
createProcessesFromPaths,
|
|
]);
|
|
|
|
return null;
|
|
}
|
|
);
|
|
|
|
export default ProcessCreator;
|