diff --git a/app/src/modules/builder/aisle/Instances/aisleInstances.tsx b/app/src/modules/builder/aisle/Instances/aisleInstances.tsx new file mode 100644 index 0000000..1ec9308 --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/aisleInstances.tsx @@ -0,0 +1,24 @@ +import { useEffect } from 'react'; +import { useAisleStore } from '../../../../store/builder/useAisleStore'; +import AisleInstance from './instance/aisleInstance'; + +function AisleInstances() { + const { aisles } = useAisleStore(); + + useEffect(() => { + console.log('aisles: ', aisles); + }, [aisles]); + + return ( + + <> + + {aisles.map((aisle) => + + )} + + + ) +} + +export default AisleInstances \ No newline at end of file diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx new file mode 100644 index 0000000..beada6f --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx @@ -0,0 +1,19 @@ +import DashedAisle from './aisleTypes/dashedAisle'; +import SolidAisle from './aisleTypes/solidAisle'; + +function AisleInstance({ aisle }: { readonly aisle: Aisle }) { + + return ( + <> + {aisle.type.aisleType === 'solid-aisle' && ( + + )} + + {aisle.type.aisleType === 'dashed-aisle' && ( + + )} + + ); +} + +export default AisleInstance; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx new file mode 100644 index 0000000..5774fcd --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx @@ -0,0 +1,71 @@ +import * as THREE from 'three'; +import { useMemo } from 'react'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../../types/world/worldConstants'; + +function DashedAisle({ aisle }: { readonly aisle: Aisle }) { + const shapes = useMemo(() => { + if (aisle.points.length < 2) return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.width || 0.1; + const dashLength = 0.5; + const gapLength = 0.3; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const totalLength = new THREE.Vector3().subVectors(end, start).length(); + const segmentCount = Math.floor(totalLength / (dashLength + gapLength)); + + const shapes = []; + const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); + + for (let i = 0; i < segmentCount; i++) { + const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength)); + const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength); + + const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + shapes.push(shape); + } + + return shapes; + }, [aisle]); + + if (shapes.length === 0) return null; + + return ( + + {shapes.map((shape, index) => ( + + + + ))} + + ); +} + +export default DashedAisle; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx new file mode 100644 index 0000000..c2bfdab --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx @@ -0,0 +1,53 @@ +import * as THREE from 'three'; +import { useMemo } from 'react'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../../types/world/worldConstants'; + +function SolidAisle({ aisle }: { readonly aisle: Aisle }) { + const shape = useMemo(() => { + if (aisle.points.length < 2) return null; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.width || 0.1; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const leftStart = new THREE.Vector3().copy(start).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(start).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(end).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(end).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + return shape; + }, [aisle]); + + if (!shape) return null; + + return ( + + + + + + ); +} + +export default SolidAisle; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx new file mode 100644 index 0000000..593c437 --- /dev/null +++ b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx @@ -0,0 +1,196 @@ +import * as THREE from 'three' +import { useEffect, useMemo, useState } from 'react' +import { useThree } from '@react-three/fiber'; +import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; +import { useAisleStore } from '../../../../store/builder/useAisleStore'; +import * as Constants from '../../../../types/world/worldConstants'; +import ReferenceAisle from './referenceAisle'; + +function AisleCreator() { + const { scene, camera, raycaster, gl, pointer } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); + const { toggleView } = useToggleView(); + const { toolMode } = useToolMode(); + const { activeLayer } = useActiveLayer(); + const { socket } = useSocketStore(); + const { aisles, addAisle } = useAisleStore(); + + const [tempPoints, setTempPoints] = useState([]); + const [isCreating, setIsCreating] = useState(false); + const [aisleType, setAisleType] = useState<'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle'>('dashed-aisle'); + + useEffect(() => { + if (tempPoints.length > 0) { + setTempPoints([]); + setIsCreating(false); + } + }, [aisleType]); + + const allPoints = useMemo(() => { + const points: Point[] = []; + const seenUuids = new Set(); + + // Add points from existing aisles + aisles.forEach(aisle => { + aisle.points.forEach(point => { + if (!seenUuids.has(point.uuid)) { + seenUuids.add(point.uuid); + points.push(point); + } + }); + }); + + // Add temporary points + tempPoints.forEach(point => { + if (!seenUuids.has(point.uuid)) { + seenUuids.add(point.uuid); + points.push(point); + } + }); + + return points; + }, [aisles, tempPoints]); + + + useEffect(() => { + const canvasElement = gl.domElement; + + let drag = false; + let isLeftMouseDown = false; + + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = true; + drag = false; + } + }; + + const onMouseUp = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = false; + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) { + drag = true; + } + }; + + const onMouseClick = () => { + if (drag || !toggleView) return; + + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (!point) return; + + if (['solid-aisle', 'dashed-aisle', 'stripped-aisle', 'dotted-aisle', 'arrows-aisle'].includes(aisleType)) { + const newPoint: Point = { + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + layer: activeLayer + }; + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + uuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + typeName: 'Aisle', + material: 'default', + aisleType: aisleType, + color: Constants.aisleConfig.defaultColor, + width: Constants.aisleConfig.width + } + }; + + addAisle(aisle); + + setTempPoints([newPoint]); + } + } else if (['arc-aisle', 'circle-aisle', 'arrow-aisle', 'junction-aisle'].includes(aisleType)) { + const newPoint: Point = { + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + layer: activeLayer + }; + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + uuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + typeName: 'Aisle', + material: 'default', + aisleType: aisleType, + color: Constants.aisleConfig.defaultColor, + width: Constants.aisleConfig.width + } + }; + + addAisle(aisle); + + setTempPoints([]); + setIsCreating(false); + } + } + }; + + const onContext = (event: any) => { + event.preventDefault(); + if (isCreating) { + setTempPoints([]); + setIsCreating(false); + } + }; + + if (toolMode === "Aisle" && toggleView) { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("click", onMouseClick); + canvasElement.addEventListener("contextmenu", onContext); + } else { + setTempPoints([]); + setIsCreating(false); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("click", onMouseClick); + canvasElement.removeEventListener("contextmenu", onContext); + }; + }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType]); + + return ( + <> + + {allPoints.map((point) => ( + + + + + ))} + + + + + ); +} + +export default AisleCreator; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx new file mode 100644 index 0000000..437337a --- /dev/null +++ b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx @@ -0,0 +1,193 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import * as THREE from 'three'; +import { useFrame, useThree } from '@react-three/fiber'; +import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store'; +import * as Constants from '../../../../types/world/worldConstants'; +import { Extrude } from '@react-three/drei'; + +interface ReferenceAisleProps { + tempPoints: Point[]; + aisleType: 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle'; +} + +function ReferenceAisle({ tempPoints, aisleType }: Readonly) { + const { pointer, raycaster, camera } = useThree(); + const { toolMode } = useToolMode(); + const { toggleView } = useToggleView(); + const { activeLayer } = useActiveLayer(); + const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + + const [tempAisle, setTempAisle] = useState(null); + const mousePosRef = useRef(new THREE.Vector3()); + + useFrame(() => { + if (toolMode === "Aisle" && toggleView && tempPoints.length === 1) { + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (intersectionPoint) { + mousePosRef.current.copy(intersectionPoint); + + setTempAisle({ + uuid: 'temp-aisle', + points: [ + tempPoints[0], + { + uuid: 'temp-point', + position: [mousePosRef.current.x, mousePosRef.current.y, mousePosRef.current.z], + layer: activeLayer + } + ], + type: { + typeName: 'Aisle', + material: 'default', + aisleType: aisleType, + color: Constants.aisleConfig.defaultColor, + width: Constants.aisleConfig.width + } + }); + } + } else if (tempAisle !== null) { + setTempAisle(null); + } + }); + + useEffect(() => { + setTempAisle(null); + }, [toolMode, toggleView, tempPoints.length, aisleType]); + + if (!tempAisle) return null; + + const renderAisle = () => { + switch (aisleType) { + case 'solid-aisle': + return ; + case 'dashed-aisle': + return ; + default: + return null; + } + }; + + return ( + + {renderAisle()} + + ); +} + +export default ReferenceAisle; + + +function SolidAisle({ aisle }: { readonly aisle: Aisle }) { + const shape = useMemo(() => { + if (aisle.points.length < 2) return null; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.width || 0.1; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const leftStart = new THREE.Vector3().copy(start).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(start).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(end).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(end).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + return shape; + }, [aisle]); + + if (!shape) return null; + + return ( + + + + + + ); +} + +function DashedAisle({ aisle }: { readonly aisle: Aisle }) { + const shapes = useMemo(() => { + if (aisle.points.length < 2) return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.width || 0.1; + const dashLength = 0.5; + const gapLength = 0.3; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const totalLength = new THREE.Vector3().subVectors(end, start).length(); + const segmentCount = Math.floor(totalLength / (dashLength + gapLength)); + + const shapes = []; + const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); + + for (let i = 0; i < segmentCount; i++) { + const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength)); + const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength); + + const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + shapes.push(shape); + } + + return shapes; + }, [aisle]); + + if (shapes.length === 0) return null; + + return ( + + {shapes.map((shape, index) => ( + + + + ))} + + ); +} diff --git a/app/src/modules/builder/aisle/aislesGroup.tsx b/app/src/modules/builder/aisle/aislesGroup.tsx new file mode 100644 index 0000000..6639672 --- /dev/null +++ b/app/src/modules/builder/aisle/aislesGroup.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import AisleCreator from './aisleCreator/aisleCreator' +import AisleInstances from './Instances/aisleInstances' + +function AislesGroup() { + + return ( + + <> + + + + + + + + + ) +} + +export default AislesGroup \ No newline at end of file diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index fe1649b..d7af8d4 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -4,18 +4,17 @@ import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; -import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem } from '../../../../../store/builder/store'; +import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem, useSocketStore } from '../../../../../store/builder/store'; import { AssetBoundingBox } from '../../functions/assetBoundingBox'; import { CameraControls } from '@react-three/drei'; import { useAssetsStore } from '../../../../../store/builder/useAssetStore'; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSocketStore } from '../../../../../store/builder/store'; import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore'; import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore'; import { useSelectedAsset, useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; -function Model({ asset }: { asset: Asset }) { +function Model({ asset }: { readonly asset: Asset }) { const { camera, controls, gl } = useThree(); const { activeTool } = useActiveTool(); const { subModule } = useSubModuleStore(); @@ -46,7 +45,8 @@ function Model({ asset }: { asset: Asset }) { const loadModel = async () => { try { // Check Cache - const cachedModel = THREE.Cache.get(asset.assetId!); + const assetId = asset.assetId; + const cachedModel = THREE.Cache.get(assetId); if (cachedModel) { setGltfScene(cachedModel); calculateBoundingBox(cachedModel.scene); @@ -54,13 +54,13 @@ function Model({ asset }: { asset: Asset }) { } // Check IndexedDB - const indexedDBModel = await retrieveGLTF(asset.assetId!); + const indexedDBModel = await retrieveGLTF(assetId); if (indexedDBModel) { const blobUrl = URL.createObjectURL(indexedDBModel); loader.load(blobUrl, (gltf) => { URL.revokeObjectURL(blobUrl); THREE.Cache.remove(blobUrl); - THREE.Cache.add(asset.assetId!, gltf); + THREE.Cache.add(assetId, gltf); setGltfScene(gltf); calculateBoundingBox(gltf.scene); }, @@ -74,14 +74,22 @@ function Model({ asset }: { asset: Asset }) { } // Fetch from Backend - const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${asset.assetId!}`; - loader.load(modelUrl, async (gltf) => { - const modelBlob = await fetch(modelUrl).then((res) => res.blob()); - await storeGLTF(asset.assetId!, modelBlob); - THREE.Cache.add(asset.assetId!, gltf); - setGltfScene(gltf); - calculateBoundingBox(gltf.scene); - }, + const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`; + const handleBackendLoad = async (gltf: GLTF) => { + try { + const response = await fetch(modelUrl); + const modelBlob = await response.blob(); + await storeGLTF(assetId, modelBlob); + THREE.Cache.add(assetId, gltf); + setGltfScene(gltf); + calculateBoundingBox(gltf.scene); + } catch (error) { + console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error); + } + }; + loader.load( + modelUrl, + handleBackendLoad, undefined, (error) => { echo.error(`[Backend] Error loading ${asset.modelName}:`); @@ -130,7 +138,7 @@ function Model({ asset }: { asset: Asset }) { true ); (controls as CameraControls).setTarget(center.x, center.y, center.z, true); - (controls as CameraControls).fitToBox(groupRef.current!, true, { + (controls as CameraControls).fitToBox(groupRef.current, true, { cover: true, paddingTop: 5, paddingLeft: 5, @@ -191,6 +199,8 @@ function Model({ asset }: { asset: Asset }) { } } + + const handleContextMenu = (asset: Asset, evt: ThreeEvent) => { if (activeTool === "cursor" && subModule === 'simulations') { if (asset.modelUuid) { @@ -215,7 +225,6 @@ function Model({ asset }: { asset: Asset }) { const canvasRect = canvasElement.getBoundingClientRect(); const relativeX = evt.clientX - canvasRect.left; const relativeY = evt.clientY - canvasRect.top; - setTop(relativeY); setLeft(relativeX); } else { diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index c69252b..3263b90 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -48,6 +48,7 @@ import CalculateAreaGroup from "./groups/calculateAreaGroup"; import LayoutImage from "./layout/layoutImage"; import AssetsGroup from "./asset/assetsGroup"; import { Bvh } from "@react-three/drei"; +import AislesGroup from "./aisle/aislesGroup"; export default function Builder() { const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. @@ -274,7 +275,7 @@ export default function Builder() { - + /> */} + + diff --git a/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts b/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts index 236905b..86fd13f 100644 --- a/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts +++ b/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts @@ -28,7 +28,7 @@ export const handleAddEventToProduct = ({ organization: organization, eventDatas: event }).then((data) => { - console.log(data); + // console.log(data); }) if (clearSelectedAsset) { diff --git a/app/src/store/builder/useAisleStore.ts b/app/src/store/builder/useAisleStore.ts new file mode 100644 index 0000000..22bdbd3 --- /dev/null +++ b/app/src/store/builder/useAisleStore.ts @@ -0,0 +1,86 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface AisleStore { + aisles: Aisles; + setAisles: (aisles: Aisles) => void; + addAisle: (aisle: Aisle) => void; + updateAisle: (uuid: string, updated: Partial) => void; + removeAisle: (uuid: string) => void; + setPosition: (pointUuid: string, position: [number, number, number]) => void; + setLayer: (pointUuid: string, layer: number) => void; + setMaterial: (aisleUuid: string, material: string) => void; + setColor: (aisleUuid: string, color: string) => void; + setWidth: (aisleUuid: string, width: number) => void; + getAisleById: (uuid: string) => Aisle | undefined; +} + +export const useAisleStore = create()( + immer((set, get) => ({ + aisles: [], + + setAisles: (aisles) => set((state) => { + state.aisles = aisles; + }), + + addAisle: (aisle) => set((state) => { + state.aisles.push(aisle); + }), + + updateAisle: (uuid, updated) => set((state) => { + const aisle = state.aisles.find((a) => a.uuid === uuid); + if (aisle) { + Object.assign(aisle, updated); + } + }), + + removeAisle: (uuid) => set((state) => { + state.aisles = state.aisles.filter((a) => a.uuid !== uuid); + }), + + setPosition: (pointUuid: string, position: [number, number, number]) => set((state) => { + for (const aisle of state.aisles) { + const point = aisle.points.find(p => p.uuid === pointUuid); + if (point) { + point.position = position; + break; + } + } + }), + + setLayer: (pointUuid: string, layer: number) => set((state) => { + for (const aisle of state.aisles) { + const point = aisle.points.find(p => p.uuid === pointUuid); + if (point) { + point.layer = layer; + break; + } + } + }), + + setMaterial: (aisleUuid: string, material: string) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle) { + aisle.type.material = material; + } + }), + + setColor: (aisleUuid: string, color: string) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle) { + aisle.type.color = color; + } + }), + + setWidth: (aisleUuid: string, width: number) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle) { + aisle.type.width = width; + } + }), + + getAisleById: (uuid) => { + return get().aisles.find((a) => a.uuid === uuid); + }, + })) +); diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index adfdc78..b2409a0 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -28,4 +28,27 @@ interface Asset { } }; -type Assets = Asset[]; \ No newline at end of file +type Assets = Asset[]; + + +interface Point { + uuid: string; + position: [number, number, number]; + layer: number; +} + +interface AisleType { + typeName: 'Aisle'; + material: string; + aisleType: 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle'| 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle'; + color: string; + width: number; +} + +interface Aisle { + uuid: string; + points: [Point, Point]; + type: AisleType; +} + +type Aisles = Aisle[]; \ No newline at end of file diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 5193ee1..c2cb62d 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -158,7 +158,7 @@ export type RoofConfig = { export type AisleConfig = { width: number; height: number; - defaultColor: number; + defaultColor: string; }; export type ZoneConfig = { @@ -345,7 +345,7 @@ export const roofConfig: RoofConfig = { export const aisleConfig: AisleConfig = { width: 0.1, // Width of the aisles height: 0.01, // Height of the aisles - defaultColor: 0xE2AC09, // Default color of the aisles + defaultColor: '#E2AC09', // Default color of the aisles }; export const zoneConfig: ZoneConfig = {