From f8bcc5aa1c221e44e7dce8871c7c14d5872fd48b Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 13 Jun 2025 17:40:55 +0530 Subject: [PATCH] feat: Implement duplicate event and product stores using Zustand with immer middleware - Created `duplicateEventStore` to manage events with actions for adding, removing, updating events, points, actions, and triggers. - Implemented helper functions to retrieve events, points, actions, and triggers by their unique identifiers. - Created `duplicateProductStore` to manage products and their associated events, including actions for adding, removing, updating products, events, points, actions, and triggers. - Added renaming functions for products, actions, and triggers. - Included comprehensive helper functions to retrieve products and their related data by various identifiers. --- .../duplicateAsset/assetsGroupDuplicate.tsx | 142 +++- .../duplicateWall/wallMeshDuplicate.tsx | 16 +- .../duplicateSetup/controlsDuplicate.tsx | 35 +- .../duplicatePoint/duplicatePoint.tsx | 142 ++++ .../duplicateProduct/duplicateProduct.tsx | 149 ++++ .../duplicateSimulation.tsx | 20 + .../duplicateTrigger/duplicateTrigger.tsx | 165 ++++ .../duplicateStores/duplicateEventStore.ts | 357 ++++++++ .../duplicateStores/duplicateProductStore.ts | 798 ++++++++++++++++++ app/src/modules/duplicate/sceneDuplicate.tsx | 49 +- app/src/modules/scene/controls/controls.tsx | 17 +- app/src/store/builder/store.ts | 34 +- app/src/styles/layout/compareLayout.scss | 41 +- 13 files changed, 1888 insertions(+), 77 deletions(-) create mode 100644 app/src/modules/duplicate/duplicateSimulation/duplicatePoint/duplicatePoint.tsx create mode 100644 app/src/modules/duplicate/duplicateSimulation/duplicateProduct/duplicateProduct.tsx create mode 100644 app/src/modules/duplicate/duplicateSimulation/duplicateSimulation.tsx create mode 100644 app/src/modules/duplicate/duplicateSimulation/duplicateTrigger/duplicateTrigger.tsx create mode 100644 app/src/modules/duplicate/duplicateStores/duplicateEventStore.ts create mode 100644 app/src/modules/duplicate/duplicateStores/duplicateProductStore.ts diff --git a/app/src/modules/duplicate/duplicateBuilder/duplicateAsset/assetsGroupDuplicate.tsx b/app/src/modules/duplicate/duplicateBuilder/duplicateAsset/assetsGroupDuplicate.tsx index fd639a9..6759b2d 100644 --- a/app/src/modules/duplicate/duplicateBuilder/duplicateAsset/assetsGroupDuplicate.tsx +++ b/app/src/modules/duplicate/duplicateBuilder/duplicateAsset/assetsGroupDuplicate.tsx @@ -6,6 +6,7 @@ import ModelDuplicate from "./modelDuplicate"; import { useLoadingProgress } from "../../../../store/builder/store"; import { getFloorAssets } from "../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import { FloorItems } from "../../../../types/world/worldTypes"; +import { useDuplicateEventsStore } from "../../duplicateStores/duplicateEventStore"; const gltfLoaderWorker = new Worker( new URL( @@ -19,6 +20,7 @@ function AssetsGroupDuplicate({ projectId }: { projectId: string }) { const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; const [assetsDuplicates, setAssetsDuplicates] = useState([]); + const { addEvent } = useDuplicateEventsStore(); const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); @@ -73,10 +75,11 @@ function AssetsGroupDuplicate({ projectId }: { projectId: string }) { updateLoadingProgress(progress); if (loadedAssets === totalAssets) { + const assets: Asset[] = []; getFloorAssets(organization, projectId).then((data: FloorItems) => { - const newAssets: Assets = data.map((item) => { + data.map((item) => { if (item.eventData) { - return { + assets.push({ modelUuid: item.modelUuid, modelName: item.modelName, assetId: item.assetId, @@ -87,9 +90,136 @@ function AssetsGroupDuplicate({ projectId }: { projectId: string }) { isVisible: item.isVisible, opacity: 1, eventData: item.eventData - }; + }) + + if (item.eventData.type === "Vehicle") { + const vehicleEvent: VehicleEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "travel", + unLoadDuration: 5, + loadCapacity: 1, + steeringAngle: 0, + pickUpPoint: null, + unLoadPoint: null, + triggers: [] + } + } + }; + addEvent(vehicleEvent); + } else if (item.eventData.type === "Conveyor") { + const ConveyorEvent: ConveyorEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "transfer", + speed: 1, + points: item.eventData.points?.map((point: any, index: number) => ({ + uuid: point.uuid || THREE.MathUtils.generateUUID(), + position: [point.position[0], point.position[1], point.position[2]], + rotation: [point.rotation[0], point.rotation[1], point.rotation[2]], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action 1`, + actionType: 'default', + material: 'Default material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: [] + } + })) || [], + }; + addEvent(ConveyorEvent); + } else if (item.eventData.type === "StaticMachine") { + const machineEvent: MachineEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "machine", + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "process", + processTime: 10, + swapMaterial: "Default Material", + triggers: [] + } + } + }; + addEvent(machineEvent); + } else if (item.eventData.type === "ArmBot") { + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "pickAndPlace", + process: { + startPoint: null, + endPoint: null + }, + triggers: [] + } + ] + } + }; + addEvent(roboticArmEvent); + } else if (item.eventData.type === 'Storage') { + const storageEvent: StorageEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle", + type: "storageUnit", + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "store", + storageCapacity: 10, + triggers: [] + } + } + }; + addEvent(storageEvent); + } } else { - return { + assets.push({ modelUuid: item.modelUuid, modelName: item.modelName, assetId: item.assetId, @@ -99,10 +229,10 @@ function AssetsGroupDuplicate({ projectId }: { projectId: string }) { isCollidable: false, isVisible: item.isVisible, opacity: 1, - }; + }); } }); - setAssetsDuplicates(newAssets); + setAssetsDuplicates(assets); }); updateLoadingProgress(100); } diff --git a/app/src/modules/duplicate/duplicateBuilder/duplicateWall/wallMeshDuplicate.tsx b/app/src/modules/duplicate/duplicateBuilder/duplicateWall/wallMeshDuplicate.tsx index 6a3adb5..d819d57 100644 --- a/app/src/modules/duplicate/duplicateBuilder/duplicateWall/wallMeshDuplicate.tsx +++ b/app/src/modules/duplicate/duplicateBuilder/duplicateWall/wallMeshDuplicate.tsx @@ -3,7 +3,7 @@ import * as Types from "../../../../types/world/worldTypes"; import * as CONSTANTS from "../../../../types/world/worldConstants"; import { Base } from "@react-three/csg"; import { MeshDiscardMaterial } from "@react-three/drei"; -import { useEffect } from "react"; +import React, { useEffect } from "react"; import objectLinesToArray from "../../../builder/geomentries/lines/lineConvertions/objectLinesToArray"; import loadWalls from "../../../builder/geomentries/walls/loadWalls"; import texturePath from "../../../../assets/textures/floor/wall-tex.png"; @@ -24,12 +24,14 @@ const WallsMeshDuplicate = ({ projectId, walls, setWalls, lines }: any) => { }); }, []); - const textureLoader = new THREE.TextureLoader(); - const wallTexture = textureLoader.load(texturePath); - - wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping; - wallTexture.repeat.set(0.1, 0.1); - wallTexture.colorSpace = THREE.SRGBColorSpace; + const wallTexture = React.useMemo(() => { + const textureLoader = new THREE.TextureLoader(); + const texture = textureLoader.load(texturePath); + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.repeat.set(0.1, 0.1); + texture.colorSpace = THREE.SRGBColorSpace; + return texture; + }, []); return ( <> diff --git a/app/src/modules/duplicate/duplicateSetup/controlsDuplicate.tsx b/app/src/modules/duplicate/duplicateSetup/controlsDuplicate.tsx index 173f4e8..18b4259 100644 --- a/app/src/modules/duplicate/duplicateSetup/controlsDuplicate.tsx +++ b/app/src/modules/duplicate/duplicateSetup/controlsDuplicate.tsx @@ -1,37 +1,20 @@ import { CameraControls } from "@react-three/drei"; -import { useRef, useEffect } from "react"; -import { useThree } from "@react-three/fiber"; +import { useRef } from "react"; +import { useFrame, useThree } from "@react-three/fiber"; import * as CONSTANTS from '../../../types/world/worldConstants'; -import { useToggleView } from "../../../store/builder/store"; -import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; +import { useDuplicatedCamData, useToggleView } from "../../../store/builder/store"; -export default function ControlsDuplicate({ projectId }: { projectId: string }) { +export default function ControlsDuplicate() { const controlsRef = useRef(null); - const { toggleView } = useToggleView(); const state = useThree(); + const { duplicatedCamData } = useDuplicatedCamData(); - useEffect(() => { - if (controlsRef.current) { - (controlsRef.current as any).mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; - (controlsRef.current as any).mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; - } - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - const userId = localStorage.getItem("userId")!; - - getCamera(organization, userId, projectId).then((data) => { - if (data && data.position && data.target) { - controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z); - controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z); - } else { - controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition); - controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget); - } - }) - .catch((error) => console.error("Failed to fetch camera data:", error)); - }, []); + useFrame(() => { + controlsRef.current?.setPosition(...duplicatedCamData.camPosition, true); + controlsRef.current?.setTarget(...duplicatedCamData.camTarget, true); + }) return ( <> diff --git a/app/src/modules/duplicate/duplicateSimulation/duplicatePoint/duplicatePoint.tsx b/app/src/modules/duplicate/duplicateSimulation/duplicatePoint/duplicatePoint.tsx new file mode 100644 index 0000000..07e8ad6 --- /dev/null +++ b/app/src/modules/duplicate/duplicateSimulation/duplicatePoint/duplicatePoint.tsx @@ -0,0 +1,142 @@ +import * as THREE from "three"; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import useModuleStore from "../../../../store/useModuleStore"; +import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; + +function PointsDuplicate() { + const { events } = useEventsStore(); + const { activeModule } = useModuleStore(); + const { isPlaying } = usePlayButtonStore(); + + return ( + <> + {activeModule === "simulation" && ( + <> + + {events.map((event, index) => { + const usedEvent = event; + + if (usedEvent.type === "transfer") { + return ( + + {usedEvent.points.map((point, j) => ( + + + + + ))} + + ); + } else if (usedEvent.type === "vehicle") { + const point = usedEvent.point; + return ( + + + + + + + ); + } else if (usedEvent.type === "roboticArm") { + const point = usedEvent.point; + return ( + + + + + + + ); + } else if (usedEvent.type === "machine") { + const point = usedEvent.point; + return ( + + + + + + + ); + } else if (usedEvent.type === "storageUnit") { + const point = usedEvent.point; + return ( + + + + + + + ); + } else { + return null; + } + })} + + + )} + + ); +} + +export default PointsDuplicate; diff --git a/app/src/modules/duplicate/duplicateSimulation/duplicateProduct/duplicateProduct.tsx b/app/src/modules/duplicate/duplicateSimulation/duplicateProduct/duplicateProduct.tsx new file mode 100644 index 0000000..92126c5 --- /dev/null +++ b/app/src/modules/duplicate/duplicateSimulation/duplicateProduct/duplicateProduct.tsx @@ -0,0 +1,149 @@ +import * as THREE from 'three'; +import { useEffect } from 'react'; +import { upsertProductOrEventApi } from '../../../../services/simulation/products/UpsertProductOrEventApi'; +import { getAllProductsApi } from '../../../../services/simulation/products/getallProductsApi'; +import { usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useProductContext } from '../../../simulation/products/productContext'; +import { useComparisonProduct, useMainProduct } from '../../../../store/simulation/useSimulationStore'; +import { useDuplicateProductStore } from '../../duplicateStores/duplicateProductStore'; + +function ProductsDuplicate({ projectId }: { projectId: string }) { + const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, layout } = useSceneContext(); + const { products, getProductById, addProduct, setProducts } = useDuplicateProductStore(); + const { selectedProductStore } = useProductContext(); + const { setMainProduct } = useMainProduct(); + const { selectedProduct, setSelectedProduct } = selectedProductStore(); + const { addVehicle, clearvehicles } = vehicleStore(); + const { addArmBot, clearArmBots } = armBotStore(); + const { addMachine, clearMachines } = machineStore(); + const { addConveyor, clearConveyors } = conveyorStore(); + const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore(); + const { isReset } = useResetButtonStore(); + const { isPlaying } = usePlayButtonStore(); + const { comparisonProduct } = useComparisonProduct(); + + useEffect(() => { + if (layout === 'Comparison Layout' && comparisonProduct) { + setSelectedProduct(comparisonProduct.productUuid, comparisonProduct.productName); + } + }, [comparisonProduct]) + + useEffect(() => { + getAllProductsApi(projectId || '').then((data) => { + if (data.length === 0) { + const id = THREE.MathUtils.generateUUID(); + const name = 'Product 1'; + addProduct(name, id); + console.log(name, id, projectId); + upsertProductOrEventApi({ + productName: name, + productUuid: id, + projectId: projectId || '' + }) + if (layout === 'Main Layout') { + setSelectedProduct(id, name); + setMainProduct(id, name); + } + } else { + setProducts(data); + if (layout === 'Main Layout') { + setSelectedProduct(data[0].productUuid, data[0].productName); + setMainProduct(data[0].productUuid, data[0].productName); + } + } + }) + }, []) + + useEffect(() => { + if (selectedProduct.productUuid) { + const product = getProductById(selectedProduct.productUuid); + if (product) { + clearvehicles(); + product.eventDatas.forEach(events => { + if (events.type === 'vehicle') { + addVehicle(selectedProduct.productUuid, events); + } + }); + } + } + }, [selectedProduct, products, isReset, isPlaying]); + + useEffect(() => { + if (selectedProduct.productUuid) { + const product = getProductById(selectedProduct.productUuid); + if (product) { + clearArmBots(); + product.eventDatas.forEach(events => { + if (events.type === 'roboticArm') { + addArmBot(selectedProduct.productUuid, events); + } + }); + } + } + }, [selectedProduct, products, isReset, isPlaying]); + + useEffect(() => { + if (selectedProduct.productUuid) { + const product = getProductById(selectedProduct.productUuid); + if (product) { + clearConveyors(); + product.eventDatas.forEach(events => { + if (events.type === 'transfer') { + addConveyor(selectedProduct.productUuid, events); + } + }); + } + } + }, [selectedProduct, products, isReset, isPlaying]); + + useEffect(() => { + if (selectedProduct.productUuid) { + const product = getProductById(selectedProduct.productUuid); + if (product) { + clearMachines(); + product.eventDatas.forEach(events => { + if (events.type === 'machine') { + addMachine(selectedProduct.productUuid, events); + } + }); + } + } + }, [selectedProduct, products, isReset, isPlaying]); + + useEffect(() => { + if (selectedProduct.productUuid) { + const product = getProductById(selectedProduct.productUuid); + if (product) { + clearStorageUnits(); + product.eventDatas.forEach(event => { + if (event.type === 'storageUnit') { + addStorageUnit(selectedProduct.productUuid, event); + + if (event.point.action.actionType === 'retrieve') { + const storageAction = event.point.action; + const materials = Array.from({ length: storageAction.storageCapacity }, () => ({ + materialType: storageAction.materialType || 'Default material', + materialId: THREE.MathUtils.generateUUID() + })); + + setCurrentMaterials(event.modelUuid, materials); + updateCurrentLoad(event.modelUuid, storageAction.storageCapacity); + } else { + setCurrentMaterials(event.modelUuid, []); + updateCurrentLoad(event.modelUuid, 0); + } + } + }); + } + } + }, [selectedProduct, products, isReset, isPlaying]); + + return ( + <> + + + ) +} + +export default ProductsDuplicate \ No newline at end of file diff --git a/app/src/modules/duplicate/duplicateSimulation/duplicateSimulation.tsx b/app/src/modules/duplicate/duplicateSimulation/duplicateSimulation.tsx new file mode 100644 index 0000000..ced7d2f --- /dev/null +++ b/app/src/modules/duplicate/duplicateSimulation/duplicateSimulation.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import ProductsDuplicate from './duplicateProduct/duplicateProduct' +import PointsDuplicate from './duplicatePoint/duplicatePoint' +import TriggerDuplicate from './duplicateTrigger/duplicateTrigger' + +function SimulationDuplicate({ projectId }: { projectId: string }) { + return ( + <> + + + + + + + + + ) +} + +export default SimulationDuplicate \ No newline at end of file diff --git a/app/src/modules/duplicate/duplicateSimulation/duplicateTrigger/duplicateTrigger.tsx b/app/src/modules/duplicate/duplicateSimulation/duplicateTrigger/duplicateTrigger.tsx new file mode 100644 index 0000000..d9e70e4 --- /dev/null +++ b/app/src/modules/duplicate/duplicateSimulation/duplicateTrigger/duplicateTrigger.tsx @@ -0,0 +1,165 @@ +import { useEffect, useState } from "react"; +import { useThree } from "@react-three/fiber"; +import * as THREE from "three"; +import { QuadraticBezierLine } from "@react-three/drei"; +import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; +import { Arrows } from "../../../simulation/events/arrows/arrows"; +import { useProductContext } from "../../../simulation/products/productContext"; +import { useDuplicateProductStore } from "../../duplicateStores/duplicateProductStore"; + +interface ConnectionLine { + id: string; + startPointUuid: string; + endPointUuid: string; + trigger: TriggerSchema; +} + +function TriggerDuplicate() { + const { scene } = useThree(); + const { selectedProductStore } = useProductContext(); + const { products, getProductById } = useDuplicateProductStore(); + const { selectedProduct } = selectedProductStore(); + const { isPlaying } = usePlayButtonStore(); + + const [connections, setConnections] = useState([]); + + useEffect(() => { + const newConnections: ConnectionLine[] = []; + + const product = getProductById(selectedProduct.productUuid); + if (!product || products.length === 0) return; + + product.eventDatas.forEach(event => { + // Handle Conveyor points + if (event.type === "transfer" && 'points' in event) { + event.points.forEach(point => { + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + }); + } + // Handle Vehicle point + else if (event.type === "vehicle" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + } + // Handle Robotic Arm points + else if (event.type === "roboticArm" && 'point' in event) { + const point = event.point; + point.actions?.forEach(action => { + action.triggers?.forEach(trigger => { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + }); + } + // Handle Machine point + else if (event.type === "machine" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + } + // Handle StorageUnit point + else if (event.type === "storageUnit" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + } + }); + + setConnections(newConnections); + }, [products, selectedProduct.productUuid]); + + const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => { + const pointObj = scene.getObjectByProperty("uuid", pointUuid); + if (!pointObj) return null; + + const worldPosition = new THREE.Vector3(); + pointObj.getWorldPosition(worldPosition); + return worldPosition; + }; + + return ( + + {connections.map((connection) => { + const startPoint = getWorldPositionFromScene(connection.startPointUuid); + const endPoint = getWorldPositionFromScene(connection.endPointUuid); + + if (!startPoint || !endPoint) return null; + + const distance = startPoint.distanceTo(endPoint); + const heightFactor = Math.max(0.5, distance * 0.2); + const midPoint = new THREE.Vector3( + (startPoint.x + endPoint.x) / 2, + Math.max(startPoint.y, endPoint.y) + heightFactor, + (startPoint.z + endPoint.z) / 2 + ); + + return ( + + ); + })} + + + + ); +} + +export default TriggerDuplicate; \ No newline at end of file diff --git a/app/src/modules/duplicate/duplicateStores/duplicateEventStore.ts b/app/src/modules/duplicate/duplicateStores/duplicateEventStore.ts new file mode 100644 index 0000000..81594ea --- /dev/null +++ b/app/src/modules/duplicate/duplicateStores/duplicateEventStore.ts @@ -0,0 +1,357 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +type EventsStore = { + events: EventsSchema[]; + + // Event-level actions + addEvent: (event: EventsSchema) => void; + removeEvent: (modelUuid: string) => void; + clearEvents: () => void; + updateEvent: (modelUuid: string, updates: Partial) => EventsSchema | undefined; + + // Point-level actions + addPoint: (modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; + removePoint: (modelUuid: string, pointUuid: string) => void; + updatePoint: ( + modelUuid: string, + pointUuid: string, + updates: Partial + ) => EventsSchema | undefined; + + // Action-level actions + addAction: ( + modelUuid: string, + pointUuid: string, + action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] + ) => void; + removeAction: (actionUuid: string) => void; + updateAction: ( + actionUuid: string, + updates: Partial + ) => void; + + // Trigger-level actions + addTrigger: (actionUuid: string, trigger: TriggerSchema) => void; + removeTrigger: (triggerUuid: string) => void; + updateTrigger: (triggerUuid: string, updates: Partial) => void; + + // Helper functions + getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined; + getPointByUuid: (modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; + getActionByUuid: (actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getTriggerByUuid: (triggerUuid: string) => TriggerSchema | undefined; +}; + +export const useDuplicateEventsStore = create()( + immer((set, get) => ({ + events: [], + + // Event-level actions + addEvent: (event) => { + set((state) => { + if (!state.events.some(e => 'modelUuid' in e && e.modelUuid === event.modelUuid)) { + state.events.push(event); + } + }); + }, + + removeEvent: (modelUuid) => { + set((state) => { + state.events = state.events.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + }); + }, + + clearEvents: () => { + set((state) => { + state.events = []; + }); + }, + + updateEvent: (modelUuid, updates) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event) { + Object.assign(event, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + }); + return updatedEvent; + }, + + // Point-level actions + addPoint: (modelUuid, point) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid); + if (!existingPoint) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } + } else if (event && 'point' in event) { + if (!(event as any).point || (event as any).point.uuid !== point.uuid) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } + } + }); + }, + + removePoint: (modelUuid, pointUuid) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + (event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid); + } + // For single-point events, you might want to handle differently + }); + }, + + updatePoint: (modelUuid, pointUuid, updates) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point) { + Object.assign(point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + Object.assign((event as any).point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + }); + return updatedEvent; + }, + + // Action-level actions + addAction: (modelUuid, pointUuid, action) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) { + point.action = action as any; + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + const point = (event as any).point; + if ('action' in point && (!point.action || point.action.actionUuid !== action.actionUuid)) { + point.action = action; + } else if ('actions' in point && !point.actions.some((a: any) => a.actionUuid === action.actionUuid)) { + point.actions.push(action); + } + } + }); + }, + + removeAction: (actionUuid) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + // Handle removal for single action points + } + } + } else if ('point' in event) { + const point = (event as any).point; + if (event.type === "roboticArm") { + if ('actions' in point) { + point.actions = point.actions.filter((a: any) => a.actionUuid !== actionUuid); + } + } else if ('action' in point && point.action?.actionUuid === actionUuid) { + // Handle single action + } + } + } + }); + }, + + updateAction: (actionUuid, updates) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + Object.assign(action, updates); + return; + } + } + } + } + }); + }, + + // Trigger-level actions + addTrigger: (actionUuid, trigger) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { + point.action.triggers.push(trigger); + } + return; + } + } + } else if ('point' in event) { + const point: MachinePointSchema | VehiclePointSchema = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { + point.action.triggers.push(trigger); + } + return; + } else if ('actions' in point) { + const action = (point as RoboticArmPointSchema).actions.find((a) => a.actionUuid === actionUuid); + if (action && !action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { + action.triggers.push(trigger); + return; + } + } + } + } + }); + }, + + removeTrigger: (triggerUuid) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + } + } + } + } + } + }); + }, + + updateTrigger: (triggerUuid, updates) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } + } + } + } + } + }); + }, + + // Helper functions + getEventByModelUuid: (modelUuid) => { + return get().events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + }, + + getPointByUuid: (modelUuid, pointUuid) => { + const event = get().events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + return (event as any).point; + } + return undefined; + }, + + getActionByUuid: (actionUuid) => { + const state = get(); + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + return point.action; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + return point.action; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return action; + } + } + } + return undefined; + }, + + getTriggerByUuid: (triggerUuid) => { + const state = get(); + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } + } + } + } + } + return undefined; + } + })) +); diff --git a/app/src/modules/duplicate/duplicateStores/duplicateProductStore.ts b/app/src/modules/duplicate/duplicateStores/duplicateProductStore.ts new file mode 100644 index 0000000..d3dc8ff --- /dev/null +++ b/app/src/modules/duplicate/duplicateStores/duplicateProductStore.ts @@ -0,0 +1,798 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +type ProductsStore = { + products: productsSchema; + + // Product-level actions + addProduct: (productName: string, productUuid: string) => void; + setProducts: (products: productsSchema) => void; + clearProducts: () => void; + removeProduct: (productUuid: string) => void; + updateProduct: (productUuid: string, updates: Partial<{ productName: string; eventDatas: EventsSchema[] }>) => void; + + // Event-level actions + addEvent: (productUuid: string, event: EventsSchema) => void; + removeEvent: (productUuid: string, modelUuid: string) => void; + deleteEvent: (modelUuid: string) => void; + updateEvent: (productUuid: string, modelUuid: string, updates: Partial) => EventsSchema | undefined; + + // Point-level actions + addPoint: (productUuid: string, modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; + removePoint: (productUuid: string, modelUuid: string, pointUuid: string) => void; + updatePoint: ( + productUuid: string, + modelUuid: string, + pointUuid: string, + updates: Partial + ) => EventsSchema | undefined; + + // Action-level actions + addAction: ( + productUuid: string, + modelUuid: string, + pointUuid: string, + action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] + ) => EventsSchema | undefined; + removeAction: (productUuid: string, actionUuid: string) => EventsSchema | undefined; + updateAction: ( + productUuid: string, + actionUuid: string, + updates: Partial + ) => EventsSchema | undefined; + + // Trigger-level actionss + addTrigger: ( + productUuid: string, + actionUuid: string, + trigger: TriggerSchema + ) => EventsSchema | undefined; + removeTrigger: (productUuid: string, triggerUuid: string) => EventsSchema | undefined; + updateTrigger: ( + productUuid: string, + triggerUuid: string, + updates: Partial + ) => EventsSchema | undefined; + + // Renaming functions + renameProduct: (productUuid: string, newName: string) => void; + renameAction: (productUuid: string, actionUuid: string, newName: string) => EventsSchema | undefined; + renameTrigger: (productUuid: string, triggerUuid: string, newName: string) => EventsSchema | undefined; + + // Helper functions + getProductById: (productUuid: string) => { productName: string; productUuid: string; eventDatas: EventsSchema[] } | undefined; + getEventByModelUuid: (productUuid: string, modelUuid: string) => EventsSchema | undefined; + getEventByActionUuid: (productUuid: string, actionUuid: string) => EventsSchema | undefined; + getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined; + getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined; + getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; + getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getModelUuidByPointUuid: (productUuid: string, actionUuid: string) => (string) | undefined; + getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined; + getPointUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined; + getTriggerByUuid: (productUuid: string, triggerUuid: string) => TriggerSchema | undefined; + getIsEventInProduct: (productUuid: string, modelUuid: string) => boolean; +}; + +export const useDuplicateProductStore = create()( + immer((set, get) => ({ + products: [], + + // Product-level actions + addProduct: (productName, productUuid) => { + set((state) => { + const existingProduct = state.products.find(p => p.productUuid === productUuid); + if (!existingProduct) { + const newProduct = { + productName, + productUuid: productUuid, + eventDatas: [] + }; + state.products.push(newProduct); + } + }); + }, + + setProducts: (products) => { + set((state) => { + state.products = products; + }); + }, + + clearProducts: () => { + set((state) => { + state.products = []; + }); + }, + + removeProduct: (productUuid) => { + set((state) => { + state.products = state.products.filter(p => p.productUuid !== productUuid); + }); + }, + + updateProduct: (productUuid, updates) => { + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + Object.assign(product, updates); + } + }); + }, + + // Event-level actions + addEvent: (productUuid, event) => { + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + const existingEvent = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === event.modelUuid); + if (!existingEvent) { + product.eventDatas.push(event); + } + } + }); + }, + + removeEvent: (productUuid, modelUuid) => { + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + } + }); + }, + + deleteEvent: (modelUuid) => { + set((state) => { + for (const product of state.products) { + product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + } + }); + }, + + updateEvent: (productUuid, modelUuid, updates) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event) { + Object.assign(event, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } + }); + return updatedEvent; + }, + + // Point-level actions + addPoint: (productUuid, modelUuid, point) => { + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid); + if (!existingPoint) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } + } else if (event && 'point' in event) { + const existingPoint = (event as any).point?.uuid === point.uuid; + if (!existingPoint) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } + } + } + }); + }, + + removePoint: (productUuid, modelUuid, pointUuid) => { + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + (event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid); + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + // For events with single point, we can't remove it, only reset to empty + } + } + }); + }, + + updatePoint: (productUuid, modelUuid, pointUuid, updates) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point) { + Object.assign(point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + Object.assign((event as any).point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } + }); + return updatedEvent; + }, + + // Action-level actions + addAction: (productUuid, modelUuid, pointUuid, action) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) { + point.action = action as any; + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + if ('action' in (event as any).point) { + if (!(event as any).point.action || (event as any).point.action.actionUuid !== action.actionUuid) { + (event as any).point.action = action; + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } else if ('actions' in (event as any).point) { + const existingAction = (event as any).point.actions.find((a: any) => a.actionUuid === action.actionUuid); + if (!existingAction) { + (event as any).point.actions.push(action); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } + } + } + }); + return updatedEvent; + }, + + removeAction: (productUuid, actionUuid) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + for (const event of product.eventDatas) { + if ('points' in event) { + // Handle ConveyorEventSchema + for (const point of (event as ConveyorEventSchema).points) { + } + } else if ('point' in event) { + const point = (event as any).point; + if (event.type === "roboticArm") { + if ('actions' in point) { + const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid); + if (index !== -1) { + point.actions.splice(index, 1); + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } else if ('action' in point && point.action?.actionUuid === actionUuid) { + point.action = undefined; + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } + } + }); + return updatedEvent; + }, + + updateAction: (productUuid, actionUuid, updates) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + Object.assign(action, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } + } + } + }); + return updatedEvent; + }, + + // Trigger-level actions + addTrigger: (productUuid, actionUuid, trigger) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + const existingTrigger = point.action.triggers.find(t => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + const existingTrigger = point.action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + const existingTrigger = action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + return; + } + } + } + } + } + }); + return updatedEvent; + }, + + removeTrigger: (productUuid, triggerUuid) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const Trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (Trigger) { + point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const Trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (Trigger) { + point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const Trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (Trigger) { + action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } + } + } + } + } + } + } + }); + return updatedEvent; + }, + + updateTrigger: (productUuid, triggerUuid, updates) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } + } + } + } + } + }); + return updatedEvent; + }, + + // Renaming functions + renameProduct: (productUuid, newName) => { + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + product.productName = newName; + } + }); + }, + + renameAction: (productUuid, actionUuid, newName) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + point.action.actionName = newName; + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + point.action.actionName = newName; + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + action.actionName = newName; + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } + } + } + }); + return updatedEvent; + }, + + renameTrigger: (productUuid, triggerUuid, newName) => { + let updatedEvent: EventsSchema | undefined; + set((state) => { + const product = state.products.find(p => p.productUuid === productUuid); + if (product) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + updatedEvent = JSON.parse(JSON.stringify(event)); + return; + } + } + } + } + } + } + } + }); + return updatedEvent; + }, + + // Helper functions + getProductById: (productUuid) => { + return get().products.find(p => p.productUuid === productUuid); + }, + + getEventByModelUuid: (productUuid, modelUuid) => { + const product = get().getProductById(productUuid); + if (!product) return undefined; + return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + }, + + getEventByActionUuid: (productUuid, actionUuid) => { + const product = get().getProductById(productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.actionUuid === actionUuid) { + return event; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.actionUuid === actionUuid) { + return event; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + return event; + } + } + } + } + return undefined; + }, + + getEventByTriggerUuid: (productUuid, triggerUuid) => { + const product = get().getProductById(productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.triggers?.some(t => t.triggerUuid === triggerUuid)) { + return event; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point) { + if (point.action?.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) { + return event; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if (action.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) { + return event; + } + } + } + } + } + return undefined; + }, + + getEventByPointUuid: (productUuid, pointUuid) => { + const product = get().getProductById(productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + if ((event as ConveyorEventSchema).points.some(p => p.uuid === pointUuid)) { + return event; + } + } else if ('point' in event) { + if ((event as any).point?.uuid === pointUuid) { + return event; + } + } + } + return undefined; + }, + + getPointByUuid: (productUuid, modelUuid, pointUuid) => { + const event = get().getEventByModelUuid(productUuid, modelUuid); + if (!event) return undefined; + + if ('points' in event) { + return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + } else if ('point' in event && (event as any).point.uuid === pointUuid) { + return (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point; + } + return undefined; + }, + + getActionByUuid: (productUuid, actionUuid) => { + const product = get().products.find(p => p.productUuid === productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.actionUuid === actionUuid) { + return point.action; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.actionUuid === actionUuid) { + return point.action; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return action; + } + } + } + return undefined; + }, + + getActionByPointUuid: (productUuid, pointUuid) => { + const product = get().products.find(p => p.productUuid === productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.uuid === pointUuid) { + return point.action; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if (point.uuid === pointUuid) { + return point.action; + } + } + } + return undefined; + }, + + getModelUuidByPointUuid: (productUuid, pointUuid) => { + const product = get().products.find(p => p.productUuid === productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.uuid === pointUuid) { + return event.modelUuid; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if (point.uuid === pointUuid) { + return event.modelUuid; + } + } + } + return undefined; + }, + + getModelUuidByActionUuid: (productUuid, actionUuid) => { + const product = get().products.find(p => p.productUuid === productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.actionUuid === actionUuid) { + return event.modelUuid; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.actionUuid === actionUuid) { + return event.modelUuid; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return event.modelUuid; + } + } + } + return undefined; + }, + + getPointUuidByActionUuid: (productUuid, actionUuid) => { + const product = get().products.find(p => p.productUuid === productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.actionUuid === actionUuid) { + return point.uuid; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.actionUuid === actionUuid) { + return point.uuid; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return point.uuid; + } + } + } + return undefined; + }, + + getTriggerByUuid: (productUuid, triggerUuid) => { + const product = get().products.find(p => p.productUuid === productUuid); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + for (const trigger of point.action?.triggers || []) { + if (trigger.triggerUuid === triggerUuid) { + return trigger; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point) { + for (const trigger of point.action?.triggers || []) { + if (trigger.triggerUuid === triggerUuid) { + return trigger; + } + } + } else if ('actions' in point) { + for (const action of point.actions) { + for (const trigger of action.triggers || []) { + if (trigger.triggerUuid === triggerUuid) { + return trigger; + } + } + } + } + } + } + return undefined; + }, + + getIsEventInProduct: (productUuid, modelUuid) => { + const product = get().getProductById(productUuid); + if (!product) return false; + return product.eventDatas.some(e => 'modelUuid' in e && e.modelUuid === modelUuid); + } + })) +); diff --git a/app/src/modules/duplicate/sceneDuplicate.tsx b/app/src/modules/duplicate/sceneDuplicate.tsx index eff0082..248d044 100644 --- a/app/src/modules/duplicate/sceneDuplicate.tsx +++ b/app/src/modules/duplicate/sceneDuplicate.tsx @@ -9,36 +9,45 @@ import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr"; import ControlsDuplicate from "./duplicateSetup/controlsDuplicate"; import PostProcessingDuplicate from "./duplicateSetup/postProcessingDuplicate"; import { Color } from "three"; +import { SceneProvider } from "../scene/sceneContext"; +import SimulationDuplicate from "./duplicateSimulation/duplicateSimulation"; export default function DuplicateScene() { const projectId = "684bcd620a64bc2a815a88d6"; return ( - { - e.preventDefault(); - }} - onCreated={(e) => { - e.scene.background = new Color(0x19191d); - }} - gl={{ powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }} - > + + { + e.preventDefault(); + }} + onCreated={(e) => { + e.scene.background = new Color(0x19191d); + }} + gl={{ powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }} + > - + - + - + - + - + - - + + + + + ); } \ No newline at end of file diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index d611114..20ca348 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -4,7 +4,7 @@ import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import * as CONSTANTS from '../../../types/world/worldConstants'; -import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store"; +import { useSocketStore, useToggleView, useResetCamera, useDuplicatedCamData } from "../../../store/builder/store"; import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; import updateCamPosition from "../camera/updateCameraPosition"; import CamMode from "../camera/camMode"; @@ -21,6 +21,7 @@ export default function Controls() { const { socket } = useSocketStore(); const state = useThree(); const { projectId } = useParams(); + const { setDuplicatedCamData } = useDuplicatedCamData(); useEffect(() => { if (controlsRef.current) { @@ -106,10 +107,23 @@ export default function Controls() { } }; + const camUpdate = () => { + if (!controlsRef.current) return; + const position = controlsRef.current.getPosition(new THREE.Vector3(0, 0, 0)); + const target = controlsRef.current.getTarget(new THREE.Vector3(0, 0, 0)); + const rotation = state.camera.rotation; + setDuplicatedCamData( + [position.x, position.y, position.z], + [target.x, target.y, target.z], + [rotation.x, rotation.y, rotation.z] + ) + }; + const controls = controlsRef.current; if (controls) { controls.addEventListener("sleep", handleRest); controls.addEventListener("control", startInterval); + controls.addEventListener("control", camUpdate); controls.addEventListener("controlend", stopInterval); } @@ -117,6 +131,7 @@ export default function Controls() { if (controls) { controls.removeEventListener("sleep", handleRest); controls.removeEventListener("control", startInterval); + controls.removeEventListener("control", camUpdate); controls.removeEventListener("controlend", stopInterval); } stopInterval(); diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index 890a0e8..4cb8947 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -741,4 +741,36 @@ export const useCompareProductDataStore = create<{ }>((set) => ({ compareProductsData: [], setCompareProductsData: (x) => set({ compareProductsData: x }), -})); \ No newline at end of file +})); + +export const useDuplicatedCamData = create<{ + duplicatedCamData: { + camPosition: [number, number, number]; + camTarget: [number, number, number]; + camRotation: [number, number, number]; + }; + setDuplicatedCamData: ( + camPosition: [number, number, number], + camTarget: [number, number, number], + camRotation: [number, number, number], + ) => + void; +}>((set) => ({ + duplicatedCamData: { + camPosition: [0, 0, 0], + camTarget: [0, 0, 0], + camRotation: [0, 0, 0], + }, + setDuplicatedCamData: ( + camPosition: [number, number, number], + camTarget: [number, number, number], + camRotation: [number, number, number], + ) => set({ + duplicatedCamData: { + camPosition: camPosition, + camTarget: camTarget, + camRotation: camRotation, + } + }) +}) +) diff --git a/app/src/styles/layout/compareLayout.scss b/app/src/styles/layout/compareLayout.scss index 31966be..9d3ae0a 100644 --- a/app/src/styles/layout/compareLayout.scss +++ b/app/src/styles/layout/compareLayout.scss @@ -37,6 +37,7 @@ animation: slideInFromRight 0.4s ease-out forwards; user-select: none; border-left: 2px solid var(--border-color); + pointer-events: none; .resizer { width: 32px; @@ -54,6 +55,7 @@ cursor: ew-resize; transition: transform 0.1s ease; z-index: 100; + pointer-events: all; } .chooseLayout-container { @@ -82,6 +84,7 @@ max-width: 80%; text-align: center; position: relative; + pointer-events: all; .icon { width: 100%; @@ -162,7 +165,9 @@ width: 100%; &:hover { - background-color: var(--highlight-text-color) !important; + background-color: var( + --highlight-text-color + ) !important; border-radius: 4px; .layout { @@ -301,19 +306,21 @@ background-color: #b7b7c6; // Custom polygon shape (adjust if needed) - clip-path: polygon(96% 52%, - 96% 54%, - 45% 53%, - 3% 100%, - 0 100%, - 42% 52%); + clip-path: polygon( + 96% 52%, + 96% 54%, + 45% 53%, + 3% 100%, + 0 100%, + 42% 52% + ); z-index: 0; // Behind any inner content } } // Optional: content above the shape - >* { + > * { position: relative; z-index: 1; } @@ -368,19 +375,21 @@ height: 100%; background: var(--background-color, wheat); - clip-path: polygon(25% 0%, - 75% 0%, - 100% 50%, - 75% 100%, - 25% 100%, - 0% 50%); + clip-path: polygon( + 25% 0%, + 75% 0%, + 100% 50%, + 75% 100%, + 25% 100%, + 0% 50% + ); filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.25)); z-index: 0; } // Content stays above the shape - >* { + > * { position: relative; z-index: 1; } @@ -603,4 +612,4 @@ position: relative; } } -} \ No newline at end of file +}