diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index e7cc3db..d6413bc 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -23,12 +23,14 @@ import { useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore"; import GlobalProperties from "./properties/GlobalProperties"; -import AsstePropertiies from "./properties/AssetProperties"; +import AssetProperties from "./properties/AssetProperties"; import ZoneProperties from "./properties/ZoneProperties"; import EventProperties from "./properties/eventProperties/EventProperties"; import VersionHistory from "./versionHisory/VersionHistory"; import AisleProperties from "./properties/AisleProperties"; -import WallProperties from "./properties/eventProperties/WallProperties"; +import WallProperties from "./properties/WallProperties"; +import { useBuilderStore } from "../../../store/builder/useBuilderStore"; +import SelectedWallProperties from "./properties/SelectedWallProperties"; const SideBarRight: React.FC = () => { const { activeModule } = useModuleStore(); @@ -36,6 +38,7 @@ const SideBarRight: React.FC = () => { const { toolMode } = useToolMode(); const { subModule, setSubModule } = useSubModuleStore(); const { selectedFloorItem } = useSelectedFloorItem(); + const { selectedWall } = useBuilderStore(); const { selectedEventData } = useSelectedEventData(); const { selectedEventSphere } = useSelectedEventSphere(); const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore(); @@ -143,7 +146,8 @@ const SideBarRight: React.FC = () => { {!viewVersionHistory && subModule === "properties" && activeModule !== "visualization" && - !selectedFloorItem && ( + !selectedFloorItem && + !selectedWall && (
{(() => { @@ -158,13 +162,26 @@ const SideBarRight: React.FC = () => {
)} + {!viewVersionHistory && subModule === "properties" && activeModule !== "visualization" && selectedFloorItem && (
- + +
+
+ )} + + {!viewVersionHistory && + subModule === "properties" && + activeModule !== "visualization" && + !selectedFloorItem && + selectedWall && ( +
+
+
)} @@ -178,6 +195,7 @@ const SideBarRight: React.FC = () => { )} + {/* simulation */} {!isVersionSaved && !viewVersionHistory && diff --git a/app/src/components/layout/sidebarRight/properties/SelectedWallProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedWallProperties.tsx new file mode 100644 index 0000000..b647145 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/SelectedWallProperties.tsx @@ -0,0 +1,143 @@ +import { useState } from "react"; +import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; + +import defaultTexture from '../../../../assets/textures/floor/wall-tex.png'; +import wallTexture1 from '../../../../assets/textures/floor/factory wall texture.jpg'; + +import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; +import { useSceneContext } from "../../../../modules/scene/sceneContext"; + +const SelectedWallProperties = () => { + const { selectedWall } = useBuilderStore(); + const { wallStore } = useSceneContext(); + const { getWallById, updateWall } = wallStore(); + + const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1"); + + const materials = [ + { texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" }, + { texture: wallTexture1, textureId: "Material 1", textureName: "Grunge Concrete Wall" } + ]; + + const wall = selectedWall ? getWallById(selectedWall.userData.wallUuid) : null; + + const handleHeightChange = (val: string) => { + const height = parseFloat(val); + if (!isNaN(height) && wall) { + updateWall(wall.wallUuid, { wallHeight: height }); + } + }; + + const handleThicknessChange = (val: string) => { + const thickness = parseFloat(val); + if (!isNaN(thickness) && wall) { + updateWall(wall.wallUuid, { wallThickness: thickness }); + } + }; + + const handleSelectMaterial = (material: { textureId: string; textureName: string }) => { + if (!wall) return; + + const updated = (activeSide === "side1" ? { insideMaterial: material.textureId } : { outsideMaterial: material.textureId }) + + updateWall(wall.wallUuid, updated); + }; + + if (!wall) return null; + + const selectedMaterials = { + side1: { + texture: materials.find((material) => material.textureId === wall.insideMaterial)?.texture || 'Unknown', + textureId: materials.find((material) => material.textureId === wall.insideMaterial)?.textureId || 'Unknown', + textureName: materials.find((material) => material.textureId === wall.insideMaterial)?.textureName || 'Unknown' + }, + side2: { + texture: materials.find((material) => material.textureId === wall.outsideMaterial)?.texture || 'Unknown', + textureId: materials.find((material) => material.textureId === wall.outsideMaterial)?.textureId || 'Unknown', + textureName: materials.find((material) => material.textureId === wall.outsideMaterial)?.textureName || 'Unknown' + } + }; + + return ( +
+
+
Wall
+
+ + +
+
+
+
+
Materials
+
+ +
+
+ {(["side1", "side2"] as const).map((side) => ( + + ))} +
+ +
+ {selectedMaterials[activeSide].textureName} +
+
+ +
+
+ {materials.map((material, index) => { + const isSelected = selectedMaterials[activeSide].textureId === material.textureId; + + return ( + + ); + })} +
+
+
+
+ ); +}; + +export default SelectedWallProperties; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/WallProperties.tsx b/app/src/components/layout/sidebarRight/properties/WallProperties.tsx similarity index 67% rename from app/src/components/layout/sidebarRight/properties/eventProperties/WallProperties.tsx rename to app/src/components/layout/sidebarRight/properties/WallProperties.tsx index f3cb792..eb835f1 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/WallProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/WallProperties.tsx @@ -1,11 +1,12 @@ import { useEffect, useState } from "react"; -import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown"; -import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons"; +import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; // Texture Imports -import wallTexture1 from "../../../../../assets/image/wallTextures/wallTexture.png"; -import defaultTexture from "../../../../../assets/image/wallTextures/defaultTexture.jpg"; -import { useBuilderStore } from "../../../../../store/builder/useBuilderStore"; + +import defaultTexture from '../../../../assets/textures/floor/wall-tex.png'; +import wallTexture1 from '../../../../assets/textures/floor/factory wall texture.jpg'; + +import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; // Define Material type type Material = { @@ -58,14 +59,6 @@ const WallProperties = () => { setWallThickness(parseFloat(newValue)); }; - const handleAddMaterial = () => { - const newMaterial: Material = { - texture: defaultMaterial.texture, - textureName: `Material ${materials.length + 1}`, - }; - setMaterials([...materials, newMaterial]); - }; - const handleSelectMaterial = (material: Material) => { setSelectedMaterials((prev) => ({ ...prev, @@ -73,46 +66,26 @@ const WallProperties = () => { })); }; - const handleRemoveMaterial = (index: number) => { - const removedTexture = materials[index].texture; - - const updatedMaterials = materials.filter((_, i) => i !== index); - const newMaterials = updatedMaterials.length === 0 ? [defaultMaterial] : updatedMaterials; - setMaterials(newMaterials); - - setSelectedMaterials((prev) => { - const updated = { ...prev }; - ["side1", "side2"].forEach((side) => { - if (updated[side as "side1" | "side2"]?.texture === removedTexture) { - updated[side as "side1" | "side2"] = defaultMaterial; - } - }); - return updated; - }); - }; - return (
-
Wall
-
- handleHeightChange(val)} - /> - handleThicknessChange(val)} - /> -
- +
+
Wall
+
+ handleHeightChange(val)} + /> + handleThicknessChange(val)} + /> +
+
Materials
-
@@ -185,15 +158,6 @@ const WallProperties = () => {
{material.textureName}
- ); })} diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index b977781..cbbf2fe 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -276,7 +276,7 @@ export default function Builder() { - {/* */} + ); } diff --git a/app/src/modules/builder/groups/floorPlanGroup.tsx b/app/src/modules/builder/groups/floorPlanGroup.tsx index 8f1c2f2..ed3065d 100644 --- a/app/src/modules/builder/groups/floorPlanGroup.tsx +++ b/app/src/modules/builder/groups/floorPlanGroup.tsx @@ -148,7 +148,7 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin } if (toolMode === "Wall") { - drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',); + // drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',); } if (toolMode === "Floor") { diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx index 68402d9..19f0810 100644 --- a/app/src/modules/builder/line/line.tsx +++ b/app/src/modules/builder/line/line.tsx @@ -1,11 +1,11 @@ import * as THREE from 'three'; +import { useThree } from '@react-three/fiber'; import { useEffect, useMemo, useState } from "react"; -import * as Constants from '../../../types/world/worldConstants'; import { DragControls, Tube } from '@react-three/drei'; import { useToolMode } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; -import { useWallStore } from '../../../store/builder/useWallStore'; -import { useThree } from '@react-three/fiber'; +import { useSceneContext } from '../../scene/sceneContext'; +import * as Constants from '../../../types/world/worldConstants'; interface LineProps { points: [Point, Point]; @@ -17,7 +17,8 @@ function Line({ points }: Readonly) { const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const [isDeletable, setIsDeletable] = useState(false); const { toolMode } = useToolMode(); - const { removeWallByPoints, setPosition } = useWallStore(); + const { wallStore } = useSceneContext(); + const { removeWallByPoints, setPosition } = wallStore(); const [dragOffset, setDragOffset] = useState(null); const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore(); @@ -79,6 +80,7 @@ function Line({ points }: Readonly) { if (toolMode === '2D-Delete') { if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { removeWallByPoints(points); + setHoveredLine(null); } gl.domElement.style.cursor = 'default'; } diff --git a/app/src/modules/builder/point/helpers/usePointSnapping.tsx b/app/src/modules/builder/point/helpers/usePointSnapping.tsx index 88c9890..c7d31cc 100644 --- a/app/src/modules/builder/point/helpers/usePointSnapping.tsx +++ b/app/src/modules/builder/point/helpers/usePointSnapping.tsx @@ -1,6 +1,5 @@ import * as THREE from 'three'; import { useCallback } from 'react'; -import { useWallStore } from '../../../../store/builder/useWallStore'; import { useSceneContext } from '../../../scene/sceneContext'; const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters @@ -12,9 +11,9 @@ const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping i const CAN_ANGLE_SNAP = true; // Whether snapping is enabled or not export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => { - const { aisleStore } = useSceneContext(); + const { aisleStore, wallStore } = useSceneContext(); const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore(); - const { walls, getConnectedPoints: getConnectedWallPoints } = useWallStore(); + const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore(); // Wall Snapping diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index 3545e99..a671806 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -6,7 +6,6 @@ import { DragControls } from '@react-three/drei'; import { useThree } from '@react-three/fiber'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import { usePointSnapping } from './helpers/usePointSnapping'; -import { useWallStore } from '../../../store/builder/useWallStore'; import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; import { useParams } from 'react-router-dom'; import { createAisleApi } from '../../../services/factoryBuilder/aisle/createAisleApi'; @@ -20,9 +19,9 @@ function Point({ point }: { readonly point: Point }) { const [isHovered, setIsHovered] = useState(false); const [dragOffset, setDragOffset] = useState(null); const { toolMode } = useToolMode(); - const { aisleStore } = useSceneContext(); + const { aisleStore, wallStore } = useSceneContext(); const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore(); - const { setPosition: setWallPosition, removePoint: removeWallPoint } = useWallStore(); + const { setPosition: setWallPosition, removePoint: removeWallPoint } = wallStore(); const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); const { hoveredPoint, setHoveredPoint } = useBuilderStore(); const { selectedVersionStore } = useVersionContext(); @@ -31,6 +30,10 @@ function Point({ point }: { readonly point: Point }) { const boxScale: [number, number, number] = Constants.pointConfig.boxScale; const colors = getColor(point); + useEffect(() => { + gl.domElement.style.cursor = 'default'; + }, [toolMode]) + function getColor(point: Point) { if (point.pointType === 'Aisle') { return { diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 6c2d858..4ada970 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -1,18 +1,25 @@ import * as THREE from 'three'; +import { Base } from '@react-three/csg'; import { useMemo, useRef, useState } from 'react'; -import * as Constants from '../../../../../types/world/worldConstants'; +import { Decal, MeshDiscardMaterial } from '@react-three/drei'; +import { useFrame, useThree } from '@react-three/fiber'; import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png'; import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg'; -import { useWallStore } from '../../../../../store/builder/useWallStore'; + +import useModuleStore from '../../../../../store/useModuleStore'; +import { useSceneContext } from '../../../../scene/sceneContext'; import { useWallClassification } from './helpers/useWallClassification'; -import { useFrame, useThree } from '@react-three/fiber'; -import { useWallVisibility } from '../../../../../store/builder/store'; -import { Decal } from '@react-three/drei'; -import { Base } from '@react-three/csg'; +import { useToggleView, useWallVisibility } from '../../../../../store/builder/store'; +import { useBuilderStore } from '../../../../../store/builder/useBuilderStore'; +import * as Constants from '../../../../../types/world/worldConstants'; function Wall({ wall }: { readonly wall: Wall }) { - const { walls } = useWallStore(); + const { wallStore } = useSceneContext(); + const { walls } = wallStore(); + const { togglView } = useToggleView(); + const { setSelectedWall } = useBuilderStore(); + const { activeModule } = useModuleStore(); const { camera } = useThree(); const { wallVisibility } = useWallVisibility(); const { getWallType, isWallFlipped } = useWallClassification(walls); @@ -67,7 +74,7 @@ function Wall({ wall }: { readonly wall: Wall }) { new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, - map: wall.outsideMaterial === 'Default Material`' ? defaultWallTexture : material1WallTexture, + map: wall.outsideMaterial === 'Default Material' ? defaultWallTexture : material1WallTexture, }), ]; }, [defaultWallTexture, material1WallTexture, wall]); @@ -100,6 +107,7 @@ function Wall({ wall }: { readonly wall: Wall }) { geometry={geometry} position={[centerX, centerY, centerZ]} rotation={[0, -angle, 0]} + userData={wall} > {materials.map((material, index) => ( @@ -112,6 +120,7 @@ function Wall({ wall }: { readonly wall: Wall }) { position={[decal.decalPosition[0], decal.decalPosition[1], wall.wallThickness / 2]} rotation={[0, 0, decal.decalRotation]} scale={[decal.decalScale, decal.decalScale, 0.001]} + userData={decal} > + { + if (visible && !togglView && activeModule === 'builder') { + setSelectedWall(e.object) + } + }} + onPointerMissed={() => { setSelectedWall(null) }} + > + + ); } diff --git a/app/src/modules/builder/wall/Instances/wallInstances.tsx b/app/src/modules/builder/wall/Instances/wallInstances.tsx index 37f07e0..b22d0e6 100644 --- a/app/src/modules/builder/wall/Instances/wallInstances.tsx +++ b/app/src/modules/builder/wall/Instances/wallInstances.tsx @@ -2,9 +2,10 @@ import React, { useEffect, useMemo } from 'react'; import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three'; import { Geometry } from '@react-three/csg'; import { Html, Extrude } from '@react-three/drei'; -import { useWallStore } from '../../../../store/builder/useWallStore' -import { useWallClassification } from './instance/helpers/useWallClassification'; +import { useLoader } from '@react-three/fiber'; +import { useSceneContext } from '../../../scene/sceneContext'; import { useToggleView } from '../../../../store/builder/store'; +import { useWallClassification } from './instance/helpers/useWallClassification'; import Line from '../../line/line'; import Point from '../../point/point'; import WallInstance from './instance/wallInstance'; @@ -12,10 +13,10 @@ import * as Constants from '../../../../types/world/worldConstants'; import texturePath from "../../../../assets/textures/floor/white.png"; import texturePathDark from "../../../../assets/textures/floor/black.png"; -import { useLoader } from '@react-three/fiber'; function WallInstances() { - const { walls } = useWallStore(); + const { wallStore } = useSceneContext(); + const { walls } = wallStore(); const { rooms } = useWallClassification(walls) const { toggleView } = useToggleView(); diff --git a/app/src/modules/builder/wall/wallCreator/wallCreator.tsx b/app/src/modules/builder/wall/wallCreator/wallCreator.tsx index 61af916..a136a41 100644 --- a/app/src/modules/builder/wall/wallCreator/wallCreator.tsx +++ b/app/src/modules/builder/wall/wallCreator/wallCreator.tsx @@ -3,7 +3,7 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { useThree } from '@react-three/fiber'; import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; import * as Constants from '../../../../types/world/worldConstants'; -import { useWallStore } from '../../../../store/builder/useWallStore'; +import { useSceneContext } from '../../../scene/sceneContext'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import ReferencePoint from '../../point/reference/referencePoint'; import ReferenceWall from './referenceWall'; @@ -16,7 +16,8 @@ function WallCreator() { const { toolMode } = useToolMode(); const { activeLayer } = useActiveLayer(); const { socket } = useSocketStore(); - const { addWall, getWallPointById, removeWall, getWallByPoints } = useWallStore(); + const { wallStore } = useSceneContext(); + const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore(); const drag = useRef(false); const isLeftMouseDown = useRef(false); const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint } = useBuilderStore(); diff --git a/app/src/modules/builder/wall/wallGroup.tsx b/app/src/modules/builder/wall/wallGroup.tsx index b3fe3c4..07ef700 100644 --- a/app/src/modules/builder/wall/wallGroup.tsx +++ b/app/src/modules/builder/wall/wallGroup.tsx @@ -1,7 +1,21 @@ +import { useEffect } from 'react'; +import { useToggleView } from '../../../store/builder/store' +import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import WallCreator from './wallCreator/wallCreator' import WallInstances from './Instances/wallInstances' +import useModuleStore from '../../../store/useModuleStore'; function WallGroup() { + const { togglView } = useToggleView(); + const { setSelectedWall } = useBuilderStore(); + const { activeModule } = useModuleStore(); + + useEffect(() => { + if (togglView || activeModule !== 'builder') { + setSelectedWall(null); + } + }, [togglView, activeModule]) + return ( <> diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 276f0bf..1b91386 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -15,7 +15,7 @@ export default function PostProcessing() { const { selectedWallItem } = useSelectedWallItem(); const { selectedFloorItem } = useSelectedFloorItem(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedAisle } = useBuilderStore(); + const { selectedAisle, selectedWall } = useBuilderStore(); function flattenChildren(children: any[]) { const allChildren: any[] = []; @@ -40,6 +40,10 @@ export default function PostProcessing() { // console.log('selectedAisle: ', selectedAisle); }, [selectedAisle]) + useEffect(() => { + // console.log('selectedWall: ', selectedWall); + }, [selectedWall]) + return ( )} + {selectedWall && ( + + )} {deletableFloorItem && ( createAssetStore(), []); + const wallAssetStore = useMemo(() => createWallAssetStore(), []); + const wallStore = useMemo(() => createWallStore(), []); const aisleStore = useMemo(() => createAisleStore(), []); + const zoneStore = useMemo(() => createZoneStore(), []); + const floorStore = useMemo(() => createFloorStore(), []); const eventStore = useMemo(() => createEventStore(), []); const productStore = useMemo(() => createProductStore(), []); @@ -58,7 +70,11 @@ export function SceneProvider({ const clearStores = useMemo(() => () => { assetStore.getState().clearAssets(); + wallAssetStore.getState().clearWallAssets(); + wallStore.getState().clearWalls(); aisleStore.getState().clearAisles(); + zoneStore.getState().clearZones(); + floorStore.getState().clearFloors(); eventStore.getState().clearEvents(); productStore.getState().clearProducts(); materialStore.getState().clearMaterials(); @@ -67,12 +83,16 @@ export function SceneProvider({ conveyorStore.getState().clearConveyors(); vehicleStore.getState().clearVehicles(); storageUnitStore.getState().clearStorageUnits(); - }, [assetStore, aisleStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore]); + }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore]); const contextValue = useMemo(() => ( { assetStore, + wallAssetStore, + wallStore, aisleStore, + zoneStore, + floorStore, eventStore, productStore, materialStore, @@ -84,7 +104,7 @@ export function SceneProvider({ clearStores, layout } - ), [assetStore, aisleStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, clearStores, layout]); + ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, clearStores, layout]); return ( diff --git a/app/src/store/builder/useBuilderStore.ts b/app/src/store/builder/useBuilderStore.ts index 086a020..f59ab54 100644 --- a/app/src/store/builder/useBuilderStore.ts +++ b/app/src/store/builder/useBuilderStore.ts @@ -3,74 +3,58 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; interface BuilderState { - // Common properties - + // Point & Line Interaction hoveredPoint: Point | null; snappedPoint: Point | null; snappedPosition: [number, number, number] | null; - hoveredLine: [Point, Point] | null; - // Wall - + // Wall Settings + selectedWall: Object3D | null; wallThickness: number; wallHeight: number; outsideMaterial: string; insideMaterial: string; - setWallThickness: (thickness: number) => void; - setWallHeight: (height: number) => void; - setWallMaterial: (material: string, side: 'inside' | 'outside') => void; - - // Aisle - + // Aisle General selectedAisle: Object3D | null; - aisleType: AisleTypes; aisleWidth: number; aisleColor: AisleColors; - // Dashed aisle properties + // Aisle Specific Styles dashLength: number; gapLength: number; - - // Dotted aisle properties dotRadius: number; - - // Arrows aisle properties aisleLength: number; - - // Junction aisle properties isFlipped: boolean; - // Setters for common properties - + // Setters - Point/Line setHoveredPoint: (point: Point | null) => void; setSnappedPoint: (point: Point | null) => void; setSnappedPosition: (position: [number, number, number] | null) => void; - setHoveredLine: (line: [Point, Point] | null) => void; - setSelectedAisle: (aisle: Object3D | null) => void; + // Setters - Wall + setSelectedWall: (wall: Object3D | null) => void; + setWallThickness: (thickness: number) => void; + setWallHeight: (height: number) => void; + setWallMaterial: (material: string, side: 'inside' | 'outside') => void; + // Setters - Aisle General + setSelectedAisle: (aisle: Object3D | null) => void; setAisleType: (type: AisleTypes) => void; setAisleWidth: (width: number) => void; setAisleColor: (color: AisleColors) => void; - // Setters for dashed aisle + // Setters - Aisle Specific setDashLength: (length: number) => void; setGapLength: (length: number) => void; - - // Setters for dotted aisle setDotRadius: (radius: number) => void; - - // Setters for arrows aisle setAisleLength: (length: number) => void; - - // Setters for junction aisle setIsFlipped: (isFlipped: boolean) => void; - // Batch setters + // Batch Setters setDashedAisleProperties: (width: number, dashLength: number, gapLength: number) => void; setDottedAisleProperties: (width: number, dotRadius: number, gapLength: number) => void; setArrowsAisleProperties: (width: number, aisleLength: number, gapLength: number) => void; @@ -79,20 +63,62 @@ interface BuilderState { export const useBuilderStore = create()( immer((set) => ({ - // Default values - + // === Defaults === hoveredPoint: null, snappedPoint: null, snappedPosition: null, - hoveredLine: null, - // Wall - + selectedWall: null, wallThickness: 0.5, wallHeight: 7, outsideMaterial: 'Default Material', - insideMaterial: 'Default Material', + insideMaterial: 'Material 1', + + selectedAisle: null, + aisleType: 'solid-aisle', + aisleWidth: 0.1, + aisleColor: 'yellow', + + dashLength: 0.5, + gapLength: 0.3, + dotRadius: 0.1, + aisleLength: 0.6, + isFlipped: false, + + // === Setters: Point/Line === + + setHoveredPoint: (point: Point | null) => { + set((state) => { + state.hoveredPoint = point; + }); + }, + + setSnappedPoint: (point: Point | null) => { + set((state) => { + state.snappedPoint = point; + }); + }, + + setSnappedPosition: (position: [number, number, number] | null) => { + set((state) => { + state.snappedPosition = position; + }); + }, + + setHoveredLine: (line: [Point, Point] | null) => { + set((state) => { + state.hoveredLine = line; + }) + }, + + // === Setters: Wall === + + setSelectedWall: (wall: Object3D | null) => { + set((state) => { + state.selectedWall = wall; + }) + }, setWallThickness: (thickness: number) => { set((state) => { @@ -113,44 +139,7 @@ export const useBuilderStore = create()( }); }, - // Aisle - - selectedAisle: null, - - aisleType: 'solid-aisle', - aisleWidth: 0.1, - aisleColor: 'yellow', - dashLength: 0.5, - gapLength: 0.3, - dotRadius: 0.1, - aisleLength: 0.6, - isFlipped: false, - - // Individual setters - - setHoveredPoint: (point: Point | null) => { - set((state) => { - state.hoveredPoint = point; - }); - }, - - setHoveredLine: (line: [Point, Point] | null) => { - set((state) => { - state.hoveredLine = line; - }) - }, - - setSnappedPoint: (point: Point | null) => { - set((state) => { - state.snappedPoint = point; - }); - }, - - setSnappedPosition: (position: [number, number, number] | null) => { - set((state) => { - state.snappedPosition = position; - }); - }, + // === Setters: Aisle General === setSelectedAisle: (aisle: Object3D | null) => { set((state) => { @@ -163,73 +152,78 @@ export const useBuilderStore = create()( state.aisleType = type; }); }, + setAisleWidth: (width) => { set((state) => { state.aisleWidth = width; }); }, + setAisleColor: (color) => { set((state) => { state.aisleColor = color; }); }, + + // === Setters: Aisle Specific === + setDashLength: (length) => { set((state) => { state.dashLength = length; }); }, + setGapLength: (length) => { set((state) => { state.gapLength = length; }); }, + setDotRadius: (radius) => { set((state) => { state.dotRadius = radius; }); }, + setAisleLength: (length) => { set((state) => { state.aisleLength = length; }); }, + setIsFlipped: (isFlipped) => { set((state) => { state.isFlipped = isFlipped; }); }, - // Batch setters - setDashedAisleProperties: (width, dashLength, gapLength) => { - set((state) => { - state.aisleType = 'dashed-aisle'; - state.aisleWidth = width; - state.dashLength = dashLength; - state.gapLength = gapLength; - }); - }, - setDottedAisleProperties: (width, dotRadius, gapLength) => { - set((state) => { - state.aisleType = 'dotted-aisle'; - state.aisleWidth = width; - state.dotRadius = dotRadius; - state.gapLength = gapLength; - }); - }, - setArrowsAisleProperties: (width, aisleLength, gapLength) => { - set((state) => { - state.aisleType = 'arrows-aisle'; - state.aisleWidth = width; - state.aisleLength = aisleLength; - state.gapLength = gapLength; - }); - }, - setAisleProperties: (type, width, color) => { - set((state) => { - state.aisleType = type; - state.aisleWidth = width; - state.aisleColor = color; - }); - } + // === Batch Setters === + + setDashedAisleProperties: (width, dashLength, gapLength) => set((state) => { + state.aisleType = 'dashed-aisle'; + state.aisleWidth = width; + state.dashLength = dashLength; + state.gapLength = gapLength; + }), + + setDottedAisleProperties: (width, dotRadius, gapLength) => set((state) => { + state.aisleType = 'dotted-aisle'; + state.aisleWidth = width; + state.dotRadius = dotRadius; + state.gapLength = gapLength; + }), + + setArrowsAisleProperties: (width, aisleLength, gapLength) => set((state) => { + state.aisleType = 'arrows-aisle'; + state.aisleWidth = width; + state.aisleLength = aisleLength; + state.gapLength = gapLength; + }), + + setAisleProperties: (type, width, color) => set((state) => { + state.aisleType = type; + state.aisleWidth = width; + state.aisleColor = color; + }) })) -); \ No newline at end of file +); diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts new file mode 100644 index 0000000..4c89333 --- /dev/null +++ b/app/src/store/builder/useFloorStore.ts @@ -0,0 +1,90 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface FloorStore { + floors: Floor[]; + setFloors: (floors: Floor[]) => void; + addFloor: (floor: Floor) => void; + updateFloor: (uuid: string, updated: Partial) => void; + removeFloor: (uuid: string) => void; + removePointFromFloors: (pointUuid: string) => void; + clearFloors: () => void; + setIsBeveled: (uuid: string, isBeveled: boolean) => void; + setBevelStrength: (uuid: string, strength: number) => void; + setDepth: (uuid: string, depth: number) => void; + setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void; + + getFloorById: (uuid: string) => Floor | undefined; +} + +export const createFloorStore = () => { + return create()( + immer((set, get) => ({ + floors: [], + + setFloors: (floors) => set(state => { + state.floors = floors; + }), + + addFloor: (floor) => set(state => { + state.floors.push(floor); + }), + + updateFloor: (uuid, updated) => set(state => { + const floor = state.floors.find(f => f.floorUuid === uuid); + if (floor) { + Object.assign(floor, updated); + } + }), + + removeFloor: (uuid) => set(state => { + state.floors = state.floors.filter(f => f.floorUuid !== uuid); + }), + + removePointFromFloors: (pointUuid) => set(state => { + for (const floor of state.floors) { + floor.points = floor.points.filter(p => p.pointUuid !== pointUuid); + } + }), + + clearFloors: () => set(state => { + state.floors = []; + }), + + setIsBeveled: (uuid, isBeveled) => set(state => { + const floor = state.floors.find(f => f.floorUuid === uuid); + if (floor) { + floor.isBeveled = isBeveled; + } + }), + + setBevelStrength: (uuid, strength) => set(state => { + const floor = state.floors.find(f => f.floorUuid === uuid); + if (floor) { + floor.bevelStrength = strength; + } + }), + + setDepth: (uuid, depth) => set(state => { + const floor = state.floors.find(f => f.floorUuid === uuid); + if (floor) { + floor.floorDepth = depth; + } + }), + + setMaterial: (uuid, sideMaterial, topMaterial) => set(state => { + const floor = state.floors.find(f => f.floorUuid === uuid); + if (floor) { + floor.sideMaterial = sideMaterial; + floor.topMaterial = topMaterial; + } + }), + + getFloorById: (uuid) => { + return get().floors.find(f => f.floorUuid === uuid); + }, + })) + ); +}; + +export type FloorStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/builder/useWallAssetStore.ts b/app/src/store/builder/useWallAssetStore.ts new file mode 100644 index 0000000..408aade --- /dev/null +++ b/app/src/store/builder/useWallAssetStore.ts @@ -0,0 +1,82 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface WallAssetStore { + wallAssets: WallAsset[]; + setWallAssets: (assets: WallAsset[]) => void; + addWallAsset: (asset: WallAsset) => void; + updateWallAsset: (uuid: string, updated: Partial) => void; + removeWallAsset: (uuid: string) => void; + clearWallAssets: () => void; + + setVisibility: (uuid: string, isVisible: boolean) => void; + setLock: (uuid: string, isLocked: boolean) => void; + setOpacity: (uuid: string, opacity: number) => void; + + getWallAssetById: (uuid: string) => WallAsset | undefined; + getAssetsByWall: (wallUuid: string) => WallAsset[]; +} + +export const createWallAssetStore = () => { + return create()( + immer((set, get) => ({ + wallAssets: [], + + setWallAssets: (assets) => set(state => { + state.wallAssets = assets; + }), + + addWallAsset: (asset) => set(state => { + state.wallAssets.push(asset); + }), + + updateWallAsset: (uuid, updated) => set(state => { + const asset = state.wallAssets.find(a => a.modelUuid === uuid); + if (asset) { + Object.assign(asset, updated); + } + }), + + removeWallAsset: (uuid) => set(state => { + state.wallAssets = state.wallAssets.filter(a => a.modelUuid !== uuid); + }), + + clearWallAssets: () => { + set(state => { + state.wallAssets = []; + }) + }, + + setVisibility: (uuid, isVisible) => set(state => { + const asset = state.wallAssets.find(a => a.modelUuid === uuid); + if (asset) { + asset.isVisible = isVisible; + } + }), + + setLock: (uuid, isLocked) => set(state => { + const asset = state.wallAssets.find(a => a.modelUuid === uuid); + if (asset) { + asset.isLocked = isLocked; + } + }), + + setOpacity: (uuid, opacity) => set(state => { + const asset = state.wallAssets.find(a => a.modelUuid === uuid); + if (asset) { + asset.opacity = opacity; + } + }), + + getWallAssetById: (uuid) => { + return get().wallAssets.find(a => a.modelUuid === uuid); + }, + + getAssetsByWall: (wallUuid) => { + return get().wallAssets.filter(a => a.wallUuid === wallUuid); + }, + })) + ); +}; + +export type WallAssetStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts new file mode 100644 index 0000000..3547330 --- /dev/null +++ b/app/src/store/builder/useWallStore.ts @@ -0,0 +1,211 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface WallStore { + walls: Wall[]; + setWalls: (walls: Wall[]) => void; + addWall: (wall: Wall) => void; + updateWall: (uuid: string, updated: Partial) => void; + removeWall: (uuid: string) => void; + clearWalls: () => void; + removeWallByPoints: (Points: [Point, Point]) => Wall | undefined; + addDecal: (wallUuid: string, decal: Decal) => void; + updateDecal: (decalUuid: string, decal: Decal) => void; + removeDecal: (decalUuid: string) => void; + updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; + updateDecalRotation: (decalUuid: string, rotation: number) => void; + updateDecalScale: (decalUuid: string, scale: number) => void; + + removePoint: (pointUuid: string) => Wall[]; + setPosition: (pointUuid: string, position: [number, number, number]) => void; + setLayer: (pointUuid: string, layer: number) => void; + + getWallById: (uuid: string) => Wall | undefined; + getWallByPointId: (uuid: string) => Wall | undefined; + getWallByPoints: (points: Point[]) => Wall | undefined; + getWallPointById: (uuid: string) => Point | undefined; + getConnectedPoints: (uuid: string) => Point[]; +} + +export const createWallStore = () => { + return create()( + immer((set, get) => ({ + walls: [], + + setWalls: (walls) => set((state) => { + state.walls = walls; + }), + + addWall: (wall) => set((state) => { + state.walls.push(wall); + }), + + updateWall: (uuid, updated) => set((state) => { + const wall = state.walls.find(w => w.wallUuid === uuid); + if (wall) { + Object.assign(wall, updated); + } + }), + + removeWall: (uuid) => set((state) => { + state.walls = state.walls.filter(w => w.wallUuid !== uuid); + }), + + clearWalls: () => { + set((state) => { + state.walls = []; + }) + }, + + removeWallByPoints: (points) => { + let removedWall: Wall | undefined; + const [pointA, pointB] = points; + + set((state) => { + state.walls = state.walls.filter(wall => { + const wallPoints = wall.points.map(p => p.pointUuid); + const hasBothPoints = wallPoints.includes(pointA.pointUuid) && wallPoints.includes(pointB.pointUuid); + + if (hasBothPoints) { + removedWall = JSON.parse(JSON.stringify(wall)); + return false; + } + return true; + }); + }); + + return removedWall; + }, + + addDecal: (wallUuid, decal) => set((state) => { + const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid); + if (wallToUpdate) { + wallToUpdate.decals.push(decal); + } + }), + + updateDecal: (decalUuid, decal) => set((state) => { + for (const wall of state.walls) { + const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid); + if (decalToUpdate) { + Object.assign(decalToUpdate, decal); + } + } + }), + + removeDecal: (decalUuid) => set((state) => { + for (const wall of state.walls) { + wall.decals = wall.decals.filter(d => d.decalUuid !== decalUuid); + } + }), + + updateDecalPosition: (decalUuid, position) => set((state) => { + for (const wall of state.walls) { + const decal = wall.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalPosition = position; + break; + } + } + }), + + updateDecalRotation: (decalUuid, rotation) => set((state) => { + for (const wall of state.walls) { + const decal = wall.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalRotation = rotation; + break; + } + } + }), + + updateDecalScale: (decalUuid, scale) => set((state) => { + for (const wall of state.walls) { + const decal = wall.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalScale = scale; + break; + } + } + }), + + removePoint: (pointUuid) => { + const removedWalls: Wall[] = []; + set((state) => { + state.walls = state.walls.filter((wall) => { + const hasPoint = wall.points.some(p => p.pointUuid === pointUuid); + if (hasPoint) { + removedWalls.push(JSON.parse(JSON.stringify(wall))); + return false; + } + return true; + }); + }); + return removedWalls; + }, + + setPosition: (pointUuid, position) => set((state) => { + for (const wall of state.walls) { + const point = wall.points.find(p => p.pointUuid === pointUuid); + if (point) { + point.position = position; + } + } + }), + + setLayer: (pointUuid, layer) => set((state) => { + for (const wall of state.walls) { + const point = wall.points.find(p => p.pointUuid === pointUuid); + if (point) { + point.layer = layer; + } + } + }), + + getWallById: (uuid) => { + return get().walls.find(w => w.wallUuid === uuid); + }, + + getWallByPointId: (uuid) => { + for (const wall of get().walls) { + if (wall.points.some(p => p.pointUuid === uuid)) { + return wall; + } + } + return undefined; + }, + + getWallByPoints: (point) => { + for (const wall of get().walls) { + if (((wall.points[0].pointUuid === point[0].pointUuid) || (wall.points[1].pointUuid === point[0].pointUuid)) && + ((wall.points[0].pointUuid === point[1].pointUuid) || (wall.points[1].pointUuid === point[1].pointUuid))) { + return wall; + } + } + return undefined; + }, + + getWallPointById: (uuid) => { + for (const wall of get().walls) { + const point = wall.points.find(p => p.pointUuid === uuid); + if (point) return point; + } + return undefined; + }, + + getConnectedPoints: (uuid) => { + const connected: Point[] = []; + for (const wall of get().walls) { + for (const point of wall.points) { + if (point.pointUuid === uuid) { + connected.push(...wall.points.filter(p => p.pointUuid !== uuid)); + } + } + } + return connected; + }, + })) + ) +} + +export type WallStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/builder/useWallStore.tsx b/app/src/store/builder/useWallStore.tsx deleted file mode 100644 index 4f57849..0000000 --- a/app/src/store/builder/useWallStore.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import { create } from 'zustand'; -import { immer } from 'zustand/middleware/immer'; - -interface WallStore { - walls: Wall[]; - setWalls: (walls: Wall[]) => void; - addWall: (wall: Wall) => void; - updateWall: (uuid: string, updated: Partial) => void; - removeWall: (uuid: string) => void; - removeWallByPoints: (Points: [Point, Point]) => Wall | undefined; - addDecal: (wallUuid: string, decal: Decal) => void; - updateDecal: (decalUuid: string, decal: Decal) => void; - removeDecal: (decalUuid: string) => void; - updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; - updateDecalRotation: (decalUuid: string, rotation: number) => void; - updateDecalScale: (decalUuid: string, scale: number) => void; - - removePoint: (pointUuid: string) => Wall[]; - setPosition: (pointUuid: string, position: [number, number, number]) => void; - setLayer: (pointUuid: string, layer: number) => void; - - getWallById: (uuid: string) => Wall | undefined; - getWallByPointId: (uuid: string) => Wall | undefined; - getWallByPoints: (points: Point[]) => Wall | undefined; - getWallPointById: (uuid: string) => Point | undefined; - getConnectedPoints: (uuid: string) => Point[]; -} - -export const useWallStore = create()( - immer((set, get) => ({ - walls: [], - - setWalls: (walls) => set((state) => { - state.walls = walls; - }), - - addWall: (wall) => set((state) => { - state.walls.push(wall); - }), - - updateWall: (uuid, updated) => set((state) => { - const wall = state.walls.find(w => w.wallUuid === uuid); - if (wall) { - Object.assign(wall, updated); - } - }), - - removeWall: (uuid) => set((state) => { - state.walls = state.walls.filter(w => w.wallUuid !== uuid); - }), - - removeWallByPoints: (points) => { - let removedWall: Wall | undefined; - const [pointA, pointB] = points; - - set((state) => { - state.walls = state.walls.filter(wall => { - const wallPoints = wall.points.map(p => p.pointUuid); - const hasBothPoints = wallPoints.includes(pointA.pointUuid) && wallPoints.includes(pointB.pointUuid); - - if (hasBothPoints) { - removedWall = JSON.parse(JSON.stringify(wall)); - return false; - } - return true; - }); - }); - - return removedWall; - }, - - addDecal: (wallUuid, decal) => set((state) => { - const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid); - if (wallToUpdate) { - wallToUpdate.decals.push(decal); - } - }), - - updateDecal: (decalUuid, decal) => set((state) => { - for (const wall of state.walls) { - const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid); - if (decalToUpdate) { - Object.assign(decalToUpdate, decal); - } - } - }), - - removeDecal: (decalUuid) => set((state) => { - for (const wall of state.walls) { - wall.decals = wall.decals.filter(d => d.decalUuid !== decalUuid); - } - }), - - updateDecalPosition: (decalUuid, position) => set((state) => { - for (const wall of state.walls) { - const decal = wall.decals.find(d => d.decalUuid === decalUuid); - if (decal) { - decal.decalPosition = position; - break; - } - } - }), - - updateDecalRotation: (decalUuid, rotation) => set((state) => { - for (const wall of state.walls) { - const decal = wall.decals.find(d => d.decalUuid === decalUuid); - if (decal) { - decal.decalRotation = rotation; - break; - } - } - }), - - updateDecalScale: (decalUuid, scale) => set((state) => { - for (const wall of state.walls) { - const decal = wall.decals.find(d => d.decalUuid === decalUuid); - if (decal) { - decal.decalScale = scale; - break; - } - } - }), - - removePoint: (pointUuid) => { - const removedWalls: Wall[] = []; - set((state) => { - state.walls = state.walls.filter((wall) => { - const hasPoint = wall.points.some(p => p.pointUuid === pointUuid); - if (hasPoint) { - removedWalls.push(JSON.parse(JSON.stringify(wall))); - return false; - } - return true; - }); - }); - return removedWalls; - }, - - setPosition: (pointUuid, position) => set((state) => { - for (const wall of state.walls) { - const point = wall.points.find(p => p.pointUuid === pointUuid); - if (point) { - point.position = position; - } - } - }), - - setLayer: (pointUuid, layer) => set((state) => { - for (const wall of state.walls) { - const point = wall.points.find(p => p.pointUuid === pointUuid); - if (point) { - point.layer = layer; - } - } - }), - - getWallById: (uuid) => { - return get().walls.find(w => w.wallUuid === uuid); - }, - - getWallByPointId: (uuid) => { - for (const wall of get().walls) { - if (wall.points.some(p => p.pointUuid === uuid)) { - return wall; - } - } - return undefined; - }, - - getWallByPoints: (point) => { - for (const wall of get().walls) { - if (((wall.points[0].pointUuid === point[0].pointUuid) || (wall.points[1].pointUuid === point[0].pointUuid)) && - ((wall.points[0].pointUuid === point[1].pointUuid) || (wall.points[1].pointUuid === point[1].pointUuid))) { - return wall; - } - } - return undefined; - }, - - getWallPointById: (uuid) => { - for (const wall of get().walls) { - const point = wall.points.find(p => p.pointUuid === uuid); - if (point) return point; - } - return undefined; - }, - - getConnectedPoints: (uuid) => { - const connected: Point[] = []; - for (const wall of get().walls) { - for (const point of wall.points) { - if (point.pointUuid === uuid) { - connected.push(...wall.points.filter(p => p.pointUuid !== uuid)); - } - } - } - return connected; - }, - })) -); diff --git a/app/src/store/builder/useZoneStore.ts b/app/src/store/builder/useZoneStore.ts new file mode 100644 index 0000000..ee3c736 --- /dev/null +++ b/app/src/store/builder/useZoneStore.ts @@ -0,0 +1,74 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface ZoneStore { + zones: Zone[]; + setZones: (zones: Zone[]) => void; + addZone: (zone: Zone) => void; + updateZone: (uuid: string, updated: Partial) => void; + removeZone: (uuid: string) => void; + removePointFromZones: (pointUuid: string) => void; + clearZones: () => void; + setViewPort: (uuid: string, position: [number, number, number], target: [number, number, number]) => void; + setColor: (uuid: string, color: string) => void; + + getZoneById: (uuid: string) => Zone | undefined; +} + +export const createZoneStore = () => { + return create()( + immer((set, get) => ({ + zones: [], + + setZones: (zones) => set(state => { + state.zones = zones; + }), + + addZone: (zone) => set(state => { + state.zones.push(zone); + }), + + updateZone: (uuid, updated) => set(state => { + const zone = state.zones.find(z => z.zoneUuid === uuid); + if (zone) { + Object.assign(zone, updated); + } + }), + + removeZone: (uuid) => set(state => { + state.zones = state.zones.filter(z => z.zoneUuid !== uuid); + }), + + removePointFromZones: (pointUuid) => set(state => { + for (const zone of state.zones) { + zone.points = zone.points.filter(p => p.pointUuid !== pointUuid); + } + }), + + clearZones: () => set(state => { + state.zones = []; + }), + + setViewPort: (uuid, position, target) => set(state => { + const zone = state.zones.find(z => z.zoneUuid === uuid); + if (zone) { + zone.viewPortPosition = position; + zone.viewPortTarget = target; + } + }), + + setColor: (uuid, color) => set(state => { + const zone = state.zones.find(z => z.zoneUuid === uuid); + if (zone) { + zone.zoneColor = color; + } + }), + + getZoneById: (uuid) => { + return get().zones.find(z => z.zoneUuid === uuid); + }, + })) + ); +}; + +export type ZoneStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 51f32df..6b24256 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -1657,8 +1657,13 @@ .sidebar-right-wrapper { .wall-properties-container { + .wall-properties-section{ + padding: 14px; + padding-bottom: 0; + margin-bottom: 8px; + } .header { - color: var(--background-color-button); + color: var(--text-color); } .wall-properties { @@ -1692,7 +1697,7 @@ flex-direction: column; align-items: center; gap: 15px; - background: var(--Grays-Gray-6, #f2f2f7); + background: var(--background-color-secondary); padding: 18px 25px; .sides-wrapper { @@ -1736,8 +1741,10 @@ } .preview { - width: 90%; + width: 100%; height: 111px; + border-radius: 20px; + overflow: hidden; img { width: 100%; @@ -1750,7 +1757,8 @@ .materials { max-height: 250px; overflow: auto; - margin-bottom: 6px; + margin-top: 12px; + margin-bottom: 16px; padding: 0 12px; .material-container {