feat: implement aisle management features with creator and instances

This commit is contained in:
2025-05-27 18:11:26 +05:30
parent 42a3d7285e
commit bb46eeb3cc
13 changed files with 720 additions and 22 deletions

View File

@@ -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) =>
<AisleInstance aisle={aisle} key={aisle.uuid} />
)}
</>
)
}
export default AisleInstances

View File

@@ -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' && (
<SolidAisle aisle={aisle} />
)}
{aisle.type.aisleType === 'dashed-aisle' && (
<DashedAisle aisle={aisle} />
)}
</>
);
}
export default AisleInstance;

View File

@@ -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 (
<group
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
>
{shapes.map((shape, index) => (
<Extrude
key={index}
args={[shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.color || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
))}
</group>
);
}
export default DashedAisle;

View File

@@ -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 (
<group
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
>
<Extrude
args={[shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.color || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
);
}
export default SolidAisle;

View File

@@ -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<Point[]>([]);
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<string>();
// 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 (
<>
<group >
{allPoints.map((point) => (
<mesh
key={point.uuid}
position={new THREE.Vector3(...point.position)}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshBasicMaterial
color={0xffff00}
/>
</mesh>
))}
</group>
<ReferenceAisle tempPoints={tempPoints} aisleType={aisleType} />
</>
);
}
export default AisleCreator;

View File

@@ -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<ReferenceAisleProps>) {
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<Aisle | null>(null);
const mousePosRef = useRef<THREE.Vector3>(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 <SolidAisle aisle={tempAisle} />;
case 'dashed-aisle':
return <DashedAisle aisle={tempAisle} />;
default:
return null;
}
};
return (
<group>
{renderAisle()}
</group>
);
}
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 (
<group
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
>
<Extrude
args={[shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.color || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
);
}
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 (
<group
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
>
{shapes.map((shape, index) => (
<Extrude
key={index}
args={[shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.color || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
))}
</group>
);
}

View File

@@ -0,0 +1,21 @@
import React from 'react'
import AisleCreator from './aisleCreator/aisleCreator'
import AisleInstances from './Instances/aisleInstances'
function AislesGroup() {
return (
<>
<AisleCreator />
<AisleInstances />
</>
)
}
export default AislesGroup

View File

@@ -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<MouseEvent>) => {
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 {

View File

@@ -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<Types.ThreeState>(); // 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() {
<ZoneGroup />
<FloorGroupAilse
{/* <FloorGroupAilse
floorGroupAisle={floorGroupAisle}
plane={plane}
floorPlanGroupLine={floorPlanGroupLine}
@@ -292,13 +293,15 @@ export default function Builder() {
isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint}
/>
/> */}
<AssetsGroup
floorGroup={floorGroup}
plane={plane}
/>
<AislesGroup />
<MeasurementTool />
<CalculateAreaGroup />

View File

@@ -28,7 +28,7 @@ export const handleAddEventToProduct = ({
organization: organization,
eventDatas: event
}).then((data) => {
console.log(data);
// console.log(data);
})
if (clearSelectedAsset) {

View File

@@ -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<Aisle>) => 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<AisleStore>()(
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);
},
}))
);

View File

@@ -28,4 +28,27 @@ interface Asset {
}
};
type Assets = Asset[];
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[];

View File

@@ -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 = {