Refactor builder store and remove wall store
Refactor builder store and remove wall store - Consolidated wall-related state management into the builder store by removing the useWallStore. - Added new properties and setters for wall attributes (thickness, height, materials) in the builder store. - Introduced SelectedWallProperties and WallProperties components for managing wall properties in the sidebar. - Created a new floor store for managing floor-related state. - Added a wall asset store for managing wall assets. - Implemented a zone store for managing zones and their properties. - Updated sidebar styles for better layout and appearance.
This commit is contained in:
@@ -23,12 +23,14 @@ import {
|
|||||||
useSelectedEventSphere,
|
useSelectedEventSphere,
|
||||||
} from "../../../store/simulation/useSimulationStore";
|
} from "../../../store/simulation/useSimulationStore";
|
||||||
import GlobalProperties from "./properties/GlobalProperties";
|
import GlobalProperties from "./properties/GlobalProperties";
|
||||||
import AsstePropertiies from "./properties/AssetProperties";
|
import AssetProperties from "./properties/AssetProperties";
|
||||||
import ZoneProperties from "./properties/ZoneProperties";
|
import ZoneProperties from "./properties/ZoneProperties";
|
||||||
import EventProperties from "./properties/eventProperties/EventProperties";
|
import EventProperties from "./properties/eventProperties/EventProperties";
|
||||||
import VersionHistory from "./versionHisory/VersionHistory";
|
import VersionHistory from "./versionHisory/VersionHistory";
|
||||||
import AisleProperties from "./properties/AisleProperties";
|
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 SideBarRight: React.FC = () => {
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
@@ -36,6 +38,7 @@ const SideBarRight: React.FC = () => {
|
|||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { subModule, setSubModule } = useSubModuleStore();
|
const { subModule, setSubModule } = useSubModuleStore();
|
||||||
const { selectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem } = useSelectedFloorItem();
|
||||||
|
const { selectedWall } = useBuilderStore();
|
||||||
const { selectedEventData } = useSelectedEventData();
|
const { selectedEventData } = useSelectedEventData();
|
||||||
const { selectedEventSphere } = useSelectedEventSphere();
|
const { selectedEventSphere } = useSelectedEventSphere();
|
||||||
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
||||||
@@ -143,7 +146,8 @@ const SideBarRight: React.FC = () => {
|
|||||||
{!viewVersionHistory &&
|
{!viewVersionHistory &&
|
||||||
subModule === "properties" &&
|
subModule === "properties" &&
|
||||||
activeModule !== "visualization" &&
|
activeModule !== "visualization" &&
|
||||||
!selectedFloorItem && (
|
!selectedFloorItem &&
|
||||||
|
!selectedWall && (
|
||||||
<div className="sidebar-right-container">
|
<div className="sidebar-right-container">
|
||||||
<div className="sidebar-right-content-container">
|
<div className="sidebar-right-content-container">
|
||||||
{(() => {
|
{(() => {
|
||||||
@@ -158,13 +162,26 @@ const SideBarRight: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!viewVersionHistory &&
|
{!viewVersionHistory &&
|
||||||
subModule === "properties" &&
|
subModule === "properties" &&
|
||||||
activeModule !== "visualization" &&
|
activeModule !== "visualization" &&
|
||||||
selectedFloorItem && (
|
selectedFloorItem && (
|
||||||
<div className="sidebar-right-container">
|
<div className="sidebar-right-container">
|
||||||
<div className="sidebar-right-content-container">
|
<div className="sidebar-right-content-container">
|
||||||
<AsstePropertiies />
|
<AssetProperties />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!viewVersionHistory &&
|
||||||
|
subModule === "properties" &&
|
||||||
|
activeModule !== "visualization" &&
|
||||||
|
!selectedFloorItem &&
|
||||||
|
selectedWall && (
|
||||||
|
<div className="sidebar-right-container">
|
||||||
|
<div className="sidebar-right-content-container">
|
||||||
|
<SelectedWallProperties />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -178,6 +195,7 @@ const SideBarRight: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* simulation */}
|
{/* simulation */}
|
||||||
{!isVersionSaved &&
|
{!isVersionSaved &&
|
||||||
!viewVersionHistory &&
|
!viewVersionHistory &&
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<div className="wall-properties-container">
|
||||||
|
<section className="wall-properties-section">
|
||||||
|
<div className="header">Wall</div>
|
||||||
|
<div className="wall-properties">
|
||||||
|
<InputWithDropDown
|
||||||
|
label="Height"
|
||||||
|
value={`${wall.wallHeight}`}
|
||||||
|
onChange={handleHeightChange}
|
||||||
|
/>
|
||||||
|
<InputWithDropDown
|
||||||
|
label="Thickness"
|
||||||
|
value={`${wall.wallThickness}`}
|
||||||
|
onChange={handleThicknessChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<div className="header-wrapper">
|
||||||
|
<div className="header">Materials</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="material-preview">
|
||||||
|
<div className="sides-wrapper">
|
||||||
|
{(["side1", "side2"] as const).map((side) => (
|
||||||
|
<button
|
||||||
|
key={side}
|
||||||
|
className={`side-wrapper ${activeSide === side ? "active" : ""}`}
|
||||||
|
onClick={() => setActiveSide(side)}
|
||||||
|
>
|
||||||
|
<div className="label">Side {side === "side1" ? 1 : 2}</div>
|
||||||
|
<div className="texture-image">
|
||||||
|
<img
|
||||||
|
draggable={false}
|
||||||
|
src={selectedMaterials[side].texture}
|
||||||
|
alt={selectedMaterials[side].textureName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="preview">
|
||||||
|
<img
|
||||||
|
draggable={false}
|
||||||
|
src={selectedMaterials[activeSide].texture}
|
||||||
|
alt={selectedMaterials[activeSide].textureName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="materials">
|
||||||
|
<div className="material-container">
|
||||||
|
{materials.map((material, index) => {
|
||||||
|
const isSelected = selectedMaterials[activeSide].textureId === material.textureId;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`material-wrapper ${isSelected ? "selectedMaterial" : ""}`}
|
||||||
|
key={`${material.textureName}_${index}`}
|
||||||
|
onClick={() => handleSelectMaterial(material)}
|
||||||
|
>
|
||||||
|
<div className="material-property">
|
||||||
|
<div className="material-image">
|
||||||
|
<img
|
||||||
|
draggable={false}
|
||||||
|
src={material.texture}
|
||||||
|
alt={material.textureName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="material-name">{material.textureName}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectedWallProperties;
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown";
|
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||||
import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons";
|
|
||||||
|
|
||||||
// Texture Imports
|
// Texture Imports
|
||||||
import wallTexture1 from "../../../../../assets/image/wallTextures/wallTexture.png";
|
|
||||||
import defaultTexture from "../../../../../assets/image/wallTextures/defaultTexture.jpg";
|
import defaultTexture from '../../../../assets/textures/floor/wall-tex.png';
|
||||||
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
|
import wallTexture1 from '../../../../assets/textures/floor/factory wall texture.jpg';
|
||||||
|
|
||||||
|
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||||
|
|
||||||
// Define Material type
|
// Define Material type
|
||||||
type Material = {
|
type Material = {
|
||||||
@@ -58,14 +59,6 @@ const WallProperties = () => {
|
|||||||
setWallThickness(parseFloat(newValue));
|
setWallThickness(parseFloat(newValue));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddMaterial = () => {
|
|
||||||
const newMaterial: Material = {
|
|
||||||
texture: defaultMaterial.texture,
|
|
||||||
textureName: `Material ${materials.length + 1}`,
|
|
||||||
};
|
|
||||||
setMaterials([...materials, newMaterial]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelectMaterial = (material: Material) => {
|
const handleSelectMaterial = (material: Material) => {
|
||||||
setSelectedMaterials((prev) => ({
|
setSelectedMaterials((prev) => ({
|
||||||
...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 (
|
return (
|
||||||
<div className="wall-properties-container">
|
<div className="wall-properties-container">
|
||||||
<div className="header">Wall</div>
|
<section className="wall-properties-section">
|
||||||
<div className="wall-properties">
|
<div className="header">Wall</div>
|
||||||
<InputWithDropDown
|
<div className="wall-properties">
|
||||||
label="Height"
|
<InputWithDropDown
|
||||||
value={`${wallHeight}`}
|
label="Height"
|
||||||
onChange={(val) => handleHeightChange(val)}
|
value={`${wallHeight}`}
|
||||||
/>
|
onChange={(val) => handleHeightChange(val)}
|
||||||
<InputWithDropDown
|
/>
|
||||||
label="Thickness"
|
<InputWithDropDown
|
||||||
value={`${wallThickness}`}
|
label="Thickness"
|
||||||
onChange={(val) => handleThicknessChange(val)}
|
value={`${wallThickness}`}
|
||||||
/>
|
onChange={(val) => handleThicknessChange(val)}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<div className="header-wrapper">
|
<div className="header-wrapper">
|
||||||
<div className="header">Materials</div>
|
<div className="header">Materials</div>
|
||||||
<button className="addMaterial" onClick={handleAddMaterial}>
|
|
||||||
<AddIcon />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="material-preview">
|
<div className="material-preview">
|
||||||
@@ -185,15 +158,6 @@ const WallProperties = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="material-name">{material.textureName}</div>
|
<div className="material-name">{material.textureName}</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className="delete-material"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleRemoveMaterial(index);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RemoveIcon />
|
|
||||||
</button>
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -276,7 +276,7 @@ export default function Builder() {
|
|||||||
<LayoutImage />
|
<LayoutImage />
|
||||||
</Bvh>
|
</Bvh>
|
||||||
|
|
||||||
{/* <WallGroup /> */}
|
<WallGroup />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toolMode === "Wall") {
|
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") {
|
if (toolMode === "Floor") {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
import { useThree } from '@react-three/fiber';
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import * as Constants from '../../../types/world/worldConstants';
|
|
||||||
import { DragControls, Tube } from '@react-three/drei';
|
import { DragControls, Tube } from '@react-three/drei';
|
||||||
import { useToolMode } from '../../../store/builder/store';
|
import { useToolMode } from '../../../store/builder/store';
|
||||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||||
import { useWallStore } from '../../../store/builder/useWallStore';
|
import { useSceneContext } from '../../scene/sceneContext';
|
||||||
import { useThree } from '@react-three/fiber';
|
import * as Constants from '../../../types/world/worldConstants';
|
||||||
|
|
||||||
interface LineProps {
|
interface LineProps {
|
||||||
points: [Point, Point];
|
points: [Point, Point];
|
||||||
@@ -17,7 +17,8 @@ function Line({ points }: Readonly<LineProps>) {
|
|||||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||||
const [isDeletable, setIsDeletable] = useState(false);
|
const [isDeletable, setIsDeletable] = useState(false);
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { removeWallByPoints, setPosition } = useWallStore();
|
const { wallStore } = useSceneContext();
|
||||||
|
const { removeWallByPoints, setPosition } = wallStore();
|
||||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||||
const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();
|
const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ function Line({ points }: Readonly<LineProps>) {
|
|||||||
if (toolMode === '2D-Delete') {
|
if (toolMode === '2D-Delete') {
|
||||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||||
removeWallByPoints(points);
|
removeWallByPoints(points);
|
||||||
|
setHoveredLine(null);
|
||||||
}
|
}
|
||||||
gl.domElement.style.cursor = 'default';
|
gl.domElement.style.cursor = 'default';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useWallStore } from '../../../../store/builder/useWallStore';
|
|
||||||
import { useSceneContext } from '../../../scene/sceneContext';
|
import { useSceneContext } from '../../../scene/sceneContext';
|
||||||
|
|
||||||
const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
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
|
const CAN_ANGLE_SNAP = true; // Whether snapping is enabled or not
|
||||||
|
|
||||||
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
|
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 { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore();
|
||||||
const { walls, getConnectedPoints: getConnectedWallPoints } = useWallStore();
|
const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore();
|
||||||
|
|
||||||
// Wall Snapping
|
// Wall Snapping
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { DragControls } from '@react-three/drei';
|
|||||||
import { useThree } from '@react-three/fiber';
|
import { useThree } from '@react-three/fiber';
|
||||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||||
import { usePointSnapping } from './helpers/usePointSnapping';
|
import { usePointSnapping } from './helpers/usePointSnapping';
|
||||||
import { useWallStore } from '../../../store/builder/useWallStore';
|
|
||||||
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
|
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { createAisleApi } from '../../../services/factoryBuilder/aisle/createAisleApi';
|
import { createAisleApi } from '../../../services/factoryBuilder/aisle/createAisleApi';
|
||||||
@@ -20,9 +19,9 @@ function Point({ point }: { readonly point: Point }) {
|
|||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { aisleStore } = useSceneContext();
|
const { aisleStore, wallStore } = useSceneContext();
|
||||||
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
|
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 { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
|
||||||
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
|
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
|
||||||
const { selectedVersionStore } = useVersionContext();
|
const { selectedVersionStore } = useVersionContext();
|
||||||
@@ -31,6 +30,10 @@ function Point({ point }: { readonly point: Point }) {
|
|||||||
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
||||||
const colors = getColor(point);
|
const colors = getColor(point);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
gl.domElement.style.cursor = 'default';
|
||||||
|
}, [toolMode])
|
||||||
|
|
||||||
function getColor(point: Point) {
|
function getColor(point: Point) {
|
||||||
if (point.pointType === 'Aisle') {
|
if (point.pointType === 'Aisle') {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
import { Base } from '@react-three/csg';
|
||||||
import { useMemo, useRef, useState } from 'react';
|
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 defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
|
||||||
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
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 { useWallClassification } from './helpers/useWallClassification';
|
||||||
import { useFrame, useThree } from '@react-three/fiber';
|
import { useToggleView, useWallVisibility } from '../../../../../store/builder/store';
|
||||||
import { useWallVisibility } from '../../../../../store/builder/store';
|
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
||||||
import { Decal } from '@react-three/drei';
|
import * as Constants from '../../../../../types/world/worldConstants';
|
||||||
import { Base } from '@react-three/csg';
|
|
||||||
|
|
||||||
function Wall({ wall }: { readonly wall: Wall }) {
|
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 { camera } = useThree();
|
||||||
const { wallVisibility } = useWallVisibility();
|
const { wallVisibility } = useWallVisibility();
|
||||||
const { getWallType, isWallFlipped } = useWallClassification(walls);
|
const { getWallType, isWallFlipped } = useWallClassification(walls);
|
||||||
@@ -67,7 +74,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
new THREE.MeshStandardMaterial({
|
new THREE.MeshStandardMaterial({
|
||||||
color: Constants.wallConfig.defaultColor,
|
color: Constants.wallConfig.defaultColor,
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
map: wall.outsideMaterial === 'Default Material`' ? defaultWallTexture : material1WallTexture,
|
map: wall.outsideMaterial === 'Default Material' ? defaultWallTexture : material1WallTexture,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
}, [defaultWallTexture, material1WallTexture, wall]);
|
}, [defaultWallTexture, material1WallTexture, wall]);
|
||||||
@@ -100,6 +107,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
geometry={geometry}
|
geometry={geometry}
|
||||||
position={[centerX, centerY, centerZ]}
|
position={[centerX, centerY, centerZ]}
|
||||||
rotation={[0, -angle, 0]}
|
rotation={[0, -angle, 0]}
|
||||||
|
userData={wall}
|
||||||
>
|
>
|
||||||
{materials.map((material, index) => (
|
{materials.map((material, index) => (
|
||||||
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
|
<primitive key={index} visible={visible} object={material} attach={`material-${index}`} />
|
||||||
@@ -112,6 +120,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
position={[decal.decalPosition[0], decal.decalPosition[1], wall.wallThickness / 2]}
|
position={[decal.decalPosition[0], decal.decalPosition[1], wall.wallThickness / 2]}
|
||||||
rotation={[0, 0, decal.decalRotation]}
|
rotation={[0, 0, decal.decalRotation]}
|
||||||
scale={[decal.decalScale, decal.decalScale, 0.001]}
|
scale={[decal.decalScale, decal.decalScale, 0.001]}
|
||||||
|
userData={decal}
|
||||||
>
|
>
|
||||||
<meshBasicMaterial
|
<meshBasicMaterial
|
||||||
map={material1WallTexture}
|
map={material1WallTexture}
|
||||||
@@ -123,6 +132,22 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Base>
|
</Base>
|
||||||
|
<mesh
|
||||||
|
castShadow
|
||||||
|
geometry={geometry}
|
||||||
|
position={[centerX, centerY, centerZ]}
|
||||||
|
rotation={[0, -angle, 0]}
|
||||||
|
userData={wall}
|
||||||
|
name={`WallRaycastReference_${wall.wallUuid}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (visible && !togglView && activeModule === 'builder') {
|
||||||
|
setSelectedWall(e.object)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onPointerMissed={() => { setSelectedWall(null) }}
|
||||||
|
>
|
||||||
|
<MeshDiscardMaterial />
|
||||||
|
</mesh>
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import React, { useEffect, useMemo } from 'react';
|
|||||||
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three';
|
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three';
|
||||||
import { Geometry } from '@react-three/csg';
|
import { Geometry } from '@react-three/csg';
|
||||||
import { Html, Extrude } from '@react-three/drei';
|
import { Html, Extrude } from '@react-three/drei';
|
||||||
import { useWallStore } from '../../../../store/builder/useWallStore'
|
import { useLoader } from '@react-three/fiber';
|
||||||
import { useWallClassification } from './instance/helpers/useWallClassification';
|
import { useSceneContext } from '../../../scene/sceneContext';
|
||||||
import { useToggleView } from '../../../../store/builder/store';
|
import { useToggleView } from '../../../../store/builder/store';
|
||||||
|
import { useWallClassification } from './instance/helpers/useWallClassification';
|
||||||
import Line from '../../line/line';
|
import Line from '../../line/line';
|
||||||
import Point from '../../point/point';
|
import Point from '../../point/point';
|
||||||
import WallInstance from './instance/wallInstance';
|
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 texturePath from "../../../../assets/textures/floor/white.png";
|
||||||
import texturePathDark from "../../../../assets/textures/floor/black.png";
|
import texturePathDark from "../../../../assets/textures/floor/black.png";
|
||||||
import { useLoader } from '@react-three/fiber';
|
|
||||||
|
|
||||||
function WallInstances() {
|
function WallInstances() {
|
||||||
const { walls } = useWallStore();
|
const { wallStore } = useSceneContext();
|
||||||
|
const { walls } = wallStore();
|
||||||
const { rooms } = useWallClassification(walls)
|
const { rooms } = useWallClassification(walls)
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'
|
|||||||
import { useThree } from '@react-three/fiber';
|
import { useThree } from '@react-three/fiber';
|
||||||
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||||
import * as Constants from '../../../../types/world/worldConstants';
|
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 { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||||
import ReferencePoint from '../../point/reference/referencePoint';
|
import ReferencePoint from '../../point/reference/referencePoint';
|
||||||
import ReferenceWall from './referenceWall';
|
import ReferenceWall from './referenceWall';
|
||||||
@@ -16,7 +16,8 @@ function WallCreator() {
|
|||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { activeLayer } = useActiveLayer();
|
const { activeLayer } = useActiveLayer();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { addWall, getWallPointById, removeWall, getWallByPoints } = useWallStore();
|
const { wallStore } = useSceneContext();
|
||||||
|
const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore();
|
||||||
const drag = useRef(false);
|
const drag = useRef(false);
|
||||||
const isLeftMouseDown = useRef(false);
|
const isLeftMouseDown = useRef(false);
|
||||||
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint } = useBuilderStore();
|
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint } = useBuilderStore();
|
||||||
|
|||||||
@@ -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 WallCreator from './wallCreator/wallCreator'
|
||||||
import WallInstances from './Instances/wallInstances'
|
import WallInstances from './Instances/wallInstances'
|
||||||
|
import useModuleStore from '../../../store/useModuleStore';
|
||||||
|
|
||||||
function WallGroup() {
|
function WallGroup() {
|
||||||
|
const { togglView } = useToggleView();
|
||||||
|
const { setSelectedWall } = useBuilderStore();
|
||||||
|
const { activeModule } = useModuleStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (togglView || activeModule !== 'builder') {
|
||||||
|
setSelectedWall(null);
|
||||||
|
}
|
||||||
|
}, [togglView, activeModule])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function PostProcessing() {
|
|||||||
const { selectedWallItem } = useSelectedWallItem();
|
const { selectedWallItem } = useSelectedWallItem();
|
||||||
const { selectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem } = useSelectedFloorItem();
|
||||||
const { selectedEventSphere } = useSelectedEventSphere();
|
const { selectedEventSphere } = useSelectedEventSphere();
|
||||||
const { selectedAisle } = useBuilderStore();
|
const { selectedAisle, selectedWall } = useBuilderStore();
|
||||||
|
|
||||||
function flattenChildren(children: any[]) {
|
function flattenChildren(children: any[]) {
|
||||||
const allChildren: any[] = [];
|
const allChildren: any[] = [];
|
||||||
@@ -40,6 +40,10 @@ export default function PostProcessing() {
|
|||||||
// console.log('selectedAisle: ', selectedAisle);
|
// console.log('selectedAisle: ', selectedAisle);
|
||||||
}, [selectedAisle])
|
}, [selectedAisle])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// console.log('selectedWall: ', selectedWall);
|
||||||
|
}, [selectedWall])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EffectComposer autoClear={false}>
|
<EffectComposer autoClear={false}>
|
||||||
<N8AO
|
<N8AO
|
||||||
@@ -66,6 +70,21 @@ export default function PostProcessing() {
|
|||||||
xRay={false}
|
xRay={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{selectedWall && (
|
||||||
|
<Outline
|
||||||
|
selection={selectedWall}
|
||||||
|
selectionLayer={10}
|
||||||
|
width={2000}
|
||||||
|
blendFunction={BlendFunction.ALPHA}
|
||||||
|
edgeStrength={5}
|
||||||
|
resolutionScale={2}
|
||||||
|
pulseSpeed={0}
|
||||||
|
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
|
||||||
|
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
|
||||||
|
blur={true}
|
||||||
|
xRay={true}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{deletableFloorItem && (
|
{deletableFloorItem && (
|
||||||
<Outline
|
<Outline
|
||||||
selection={flattenChildren(deletableFloorItem.children)}
|
selection={flattenChildren(deletableFloorItem.children)}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { createContext, useContext, useMemo } from 'react';
|
import { createContext, useContext, useMemo } from 'react';
|
||||||
|
|
||||||
import { createAssetStore, AssetStoreType } from '../../store/builder/useAssetStore';
|
import { createAssetStore, AssetStoreType } from '../../store/builder/useAssetStore';
|
||||||
|
import { createWallAssetStore, WallAssetStoreType } from '../../store/builder/useWallAssetStore';
|
||||||
|
import { createWallStore, WallStoreType } from '../../store/builder/useWallStore';
|
||||||
import { createAisleStore, AisleStoreType } from '../../store/builder/useAisleStore';
|
import { createAisleStore, AisleStoreType } from '../../store/builder/useAisleStore';
|
||||||
|
import { createZoneStore, ZoneStoreType } from '../../store/builder/useZoneStore';
|
||||||
|
import { createFloorStore, FloorStoreType } from '../../store/builder/useFloorStore';
|
||||||
|
|
||||||
import { createEventStore, EventStoreType } from '../../store/simulation/useEventsStore';
|
import { createEventStore, EventStoreType } from '../../store/simulation/useEventsStore';
|
||||||
import { createProductStore, ProductStoreType } from '../../store/simulation/useProductStore';
|
import { createProductStore, ProductStoreType } from '../../store/simulation/useProductStore';
|
||||||
@@ -16,7 +20,11 @@ import { createStorageUnitStore, StorageUnitStoreType } from '../../store/simula
|
|||||||
type SceneContextValue = {
|
type SceneContextValue = {
|
||||||
|
|
||||||
assetStore: AssetStoreType,
|
assetStore: AssetStoreType,
|
||||||
|
wallAssetStore: WallAssetStoreType,
|
||||||
|
wallStore: WallStoreType,
|
||||||
aisleStore: AisleStoreType,
|
aisleStore: AisleStoreType,
|
||||||
|
zoneStore: ZoneStoreType,
|
||||||
|
floorStore: FloorStoreType,
|
||||||
|
|
||||||
eventStore: EventStoreType,
|
eventStore: EventStoreType,
|
||||||
productStore: ProductStoreType,
|
productStore: ProductStoreType,
|
||||||
@@ -44,7 +52,11 @@ export function SceneProvider({
|
|||||||
}) {
|
}) {
|
||||||
|
|
||||||
const assetStore = useMemo(() => createAssetStore(), []);
|
const assetStore = useMemo(() => createAssetStore(), []);
|
||||||
|
const wallAssetStore = useMemo(() => createWallAssetStore(), []);
|
||||||
|
const wallStore = useMemo(() => createWallStore(), []);
|
||||||
const aisleStore = useMemo(() => createAisleStore(), []);
|
const aisleStore = useMemo(() => createAisleStore(), []);
|
||||||
|
const zoneStore = useMemo(() => createZoneStore(), []);
|
||||||
|
const floorStore = useMemo(() => createFloorStore(), []);
|
||||||
|
|
||||||
const eventStore = useMemo(() => createEventStore(), []);
|
const eventStore = useMemo(() => createEventStore(), []);
|
||||||
const productStore = useMemo(() => createProductStore(), []);
|
const productStore = useMemo(() => createProductStore(), []);
|
||||||
@@ -58,7 +70,11 @@ export function SceneProvider({
|
|||||||
|
|
||||||
const clearStores = useMemo(() => () => {
|
const clearStores = useMemo(() => () => {
|
||||||
assetStore.getState().clearAssets();
|
assetStore.getState().clearAssets();
|
||||||
|
wallAssetStore.getState().clearWallAssets();
|
||||||
|
wallStore.getState().clearWalls();
|
||||||
aisleStore.getState().clearAisles();
|
aisleStore.getState().clearAisles();
|
||||||
|
zoneStore.getState().clearZones();
|
||||||
|
floorStore.getState().clearFloors();
|
||||||
eventStore.getState().clearEvents();
|
eventStore.getState().clearEvents();
|
||||||
productStore.getState().clearProducts();
|
productStore.getState().clearProducts();
|
||||||
materialStore.getState().clearMaterials();
|
materialStore.getState().clearMaterials();
|
||||||
@@ -67,12 +83,16 @@ export function SceneProvider({
|
|||||||
conveyorStore.getState().clearConveyors();
|
conveyorStore.getState().clearConveyors();
|
||||||
vehicleStore.getState().clearVehicles();
|
vehicleStore.getState().clearVehicles();
|
||||||
storageUnitStore.getState().clearStorageUnits();
|
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(() => (
|
const contextValue = useMemo(() => (
|
||||||
{
|
{
|
||||||
assetStore,
|
assetStore,
|
||||||
|
wallAssetStore,
|
||||||
|
wallStore,
|
||||||
aisleStore,
|
aisleStore,
|
||||||
|
zoneStore,
|
||||||
|
floorStore,
|
||||||
eventStore,
|
eventStore,
|
||||||
productStore,
|
productStore,
|
||||||
materialStore,
|
materialStore,
|
||||||
@@ -84,7 +104,7 @@ export function SceneProvider({
|
|||||||
clearStores,
|
clearStores,
|
||||||
layout
|
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 (
|
return (
|
||||||
<SceneContext.Provider value={contextValue}>
|
<SceneContext.Provider value={contextValue}>
|
||||||
|
|||||||
@@ -3,74 +3,58 @@ import { create } from 'zustand';
|
|||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
interface BuilderState {
|
interface BuilderState {
|
||||||
// Common properties
|
// Point & Line Interaction
|
||||||
|
|
||||||
hoveredPoint: Point | null;
|
hoveredPoint: Point | null;
|
||||||
snappedPoint: Point | null;
|
snappedPoint: Point | null;
|
||||||
snappedPosition: [number, number, number] | null;
|
snappedPosition: [number, number, number] | null;
|
||||||
|
|
||||||
hoveredLine: [Point, Point] | null;
|
hoveredLine: [Point, Point] | null;
|
||||||
|
|
||||||
// Wall
|
// Wall Settings
|
||||||
|
selectedWall: Object3D | null;
|
||||||
wallThickness: number;
|
wallThickness: number;
|
||||||
wallHeight: number;
|
wallHeight: number;
|
||||||
outsideMaterial: string;
|
outsideMaterial: string;
|
||||||
insideMaterial: string;
|
insideMaterial: string;
|
||||||
|
|
||||||
setWallThickness: (thickness: number) => void;
|
// Aisle General
|
||||||
setWallHeight: (height: number) => void;
|
|
||||||
setWallMaterial: (material: string, side: 'inside' | 'outside') => void;
|
|
||||||
|
|
||||||
// Aisle
|
|
||||||
|
|
||||||
selectedAisle: Object3D | null;
|
selectedAisle: Object3D | null;
|
||||||
|
|
||||||
aisleType: AisleTypes;
|
aisleType: AisleTypes;
|
||||||
aisleWidth: number;
|
aisleWidth: number;
|
||||||
aisleColor: AisleColors;
|
aisleColor: AisleColors;
|
||||||
|
|
||||||
// Dashed aisle properties
|
// Aisle Specific Styles
|
||||||
dashLength: number;
|
dashLength: number;
|
||||||
gapLength: number;
|
gapLength: number;
|
||||||
|
|
||||||
// Dotted aisle properties
|
|
||||||
dotRadius: number;
|
dotRadius: number;
|
||||||
|
|
||||||
// Arrows aisle properties
|
|
||||||
aisleLength: number;
|
aisleLength: number;
|
||||||
|
|
||||||
// Junction aisle properties
|
|
||||||
isFlipped: boolean;
|
isFlipped: boolean;
|
||||||
|
|
||||||
// Setters for common properties
|
// Setters - Point/Line
|
||||||
|
|
||||||
setHoveredPoint: (point: Point | null) => void;
|
setHoveredPoint: (point: Point | null) => void;
|
||||||
setSnappedPoint: (point: Point | null) => void;
|
setSnappedPoint: (point: Point | null) => void;
|
||||||
setSnappedPosition: (position: [number, number, number] | null) => void;
|
setSnappedPosition: (position: [number, number, number] | null) => void;
|
||||||
|
|
||||||
setHoveredLine: (line: [Point, Point] | 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;
|
setAisleType: (type: AisleTypes) => void;
|
||||||
setAisleWidth: (width: number) => void;
|
setAisleWidth: (width: number) => void;
|
||||||
setAisleColor: (color: AisleColors) => void;
|
setAisleColor: (color: AisleColors) => void;
|
||||||
|
|
||||||
// Setters for dashed aisle
|
// Setters - Aisle Specific
|
||||||
setDashLength: (length: number) => void;
|
setDashLength: (length: number) => void;
|
||||||
setGapLength: (length: number) => void;
|
setGapLength: (length: number) => void;
|
||||||
|
|
||||||
// Setters for dotted aisle
|
|
||||||
setDotRadius: (radius: number) => void;
|
setDotRadius: (radius: number) => void;
|
||||||
|
|
||||||
// Setters for arrows aisle
|
|
||||||
setAisleLength: (length: number) => void;
|
setAisleLength: (length: number) => void;
|
||||||
|
|
||||||
// Setters for junction aisle
|
|
||||||
setIsFlipped: (isFlipped: boolean) => void;
|
setIsFlipped: (isFlipped: boolean) => void;
|
||||||
|
|
||||||
// Batch setters
|
// Batch Setters
|
||||||
setDashedAisleProperties: (width: number, dashLength: number, gapLength: number) => void;
|
setDashedAisleProperties: (width: number, dashLength: number, gapLength: number) => void;
|
||||||
setDottedAisleProperties: (width: number, dotRadius: number, gapLength: number) => void;
|
setDottedAisleProperties: (width: number, dotRadius: number, gapLength: number) => void;
|
||||||
setArrowsAisleProperties: (width: number, aisleLength: number, gapLength: number) => void;
|
setArrowsAisleProperties: (width: number, aisleLength: number, gapLength: number) => void;
|
||||||
@@ -79,20 +63,62 @@ interface BuilderState {
|
|||||||
|
|
||||||
export const useBuilderStore = create<BuilderState>()(
|
export const useBuilderStore = create<BuilderState>()(
|
||||||
immer((set) => ({
|
immer((set) => ({
|
||||||
// Default values
|
// === Defaults ===
|
||||||
|
|
||||||
hoveredPoint: null,
|
hoveredPoint: null,
|
||||||
snappedPoint: null,
|
snappedPoint: null,
|
||||||
snappedPosition: null,
|
snappedPosition: null,
|
||||||
|
|
||||||
hoveredLine: null,
|
hoveredLine: null,
|
||||||
|
|
||||||
// Wall
|
selectedWall: null,
|
||||||
|
|
||||||
wallThickness: 0.5,
|
wallThickness: 0.5,
|
||||||
wallHeight: 7,
|
wallHeight: 7,
|
||||||
outsideMaterial: 'Default Material',
|
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) => {
|
setWallThickness: (thickness: number) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
@@ -113,44 +139,7 @@ export const useBuilderStore = create<BuilderState>()(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Aisle
|
// === Setters: Aisle General ===
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setSelectedAisle: (aisle: Object3D | null) => {
|
setSelectedAisle: (aisle: Object3D | null) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
@@ -163,73 +152,78 @@ export const useBuilderStore = create<BuilderState>()(
|
|||||||
state.aisleType = type;
|
state.aisleType = type;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setAisleWidth: (width) => {
|
setAisleWidth: (width) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.aisleWidth = width;
|
state.aisleWidth = width;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setAisleColor: (color) => {
|
setAisleColor: (color) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.aisleColor = color;
|
state.aisleColor = color;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// === Setters: Aisle Specific ===
|
||||||
|
|
||||||
setDashLength: (length) => {
|
setDashLength: (length) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.dashLength = length;
|
state.dashLength = length;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setGapLength: (length) => {
|
setGapLength: (length) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.gapLength = length;
|
state.gapLength = length;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setDotRadius: (radius) => {
|
setDotRadius: (radius) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.dotRadius = radius;
|
state.dotRadius = radius;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setAisleLength: (length) => {
|
setAisleLength: (length) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.aisleLength = length;
|
state.aisleLength = length;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setIsFlipped: (isFlipped) => {
|
setIsFlipped: (isFlipped) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.isFlipped = isFlipped;
|
state.isFlipped = isFlipped;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Batch setters
|
// === Batch Setters ===
|
||||||
setDashedAisleProperties: (width, dashLength, gapLength) => {
|
|
||||||
set((state) => {
|
setDashedAisleProperties: (width, dashLength, gapLength) => set((state) => {
|
||||||
state.aisleType = 'dashed-aisle';
|
state.aisleType = 'dashed-aisle';
|
||||||
state.aisleWidth = width;
|
state.aisleWidth = width;
|
||||||
state.dashLength = dashLength;
|
state.dashLength = dashLength;
|
||||||
state.gapLength = gapLength;
|
state.gapLength = gapLength;
|
||||||
});
|
}),
|
||||||
},
|
|
||||||
setDottedAisleProperties: (width, dotRadius, gapLength) => {
|
setDottedAisleProperties: (width, dotRadius, gapLength) => set((state) => {
|
||||||
set((state) => {
|
state.aisleType = 'dotted-aisle';
|
||||||
state.aisleType = 'dotted-aisle';
|
state.aisleWidth = width;
|
||||||
state.aisleWidth = width;
|
state.dotRadius = dotRadius;
|
||||||
state.dotRadius = dotRadius;
|
state.gapLength = gapLength;
|
||||||
state.gapLength = gapLength;
|
}),
|
||||||
});
|
|
||||||
},
|
setArrowsAisleProperties: (width, aisleLength, gapLength) => set((state) => {
|
||||||
setArrowsAisleProperties: (width, aisleLength, gapLength) => {
|
state.aisleType = 'arrows-aisle';
|
||||||
set((state) => {
|
state.aisleWidth = width;
|
||||||
state.aisleType = 'arrows-aisle';
|
state.aisleLength = aisleLength;
|
||||||
state.aisleWidth = width;
|
state.gapLength = gapLength;
|
||||||
state.aisleLength = aisleLength;
|
}),
|
||||||
state.gapLength = gapLength;
|
|
||||||
});
|
setAisleProperties: (type, width, color) => set((state) => {
|
||||||
},
|
state.aisleType = type;
|
||||||
setAisleProperties: (type, width, color) => {
|
state.aisleWidth = width;
|
||||||
set((state) => {
|
state.aisleColor = color;
|
||||||
state.aisleType = type;
|
})
|
||||||
state.aisleWidth = width;
|
|
||||||
state.aisleColor = color;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
90
app/src/store/builder/useFloorStore.ts
Normal file
90
app/src/store/builder/useFloorStore.ts
Normal file
@@ -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<Floor>) => 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<FloorStore>()(
|
||||||
|
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<typeof createFloorStore>;
|
||||||
82
app/src/store/builder/useWallAssetStore.ts
Normal file
82
app/src/store/builder/useWallAssetStore.ts
Normal file
@@ -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<WallAsset>) => 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<WallAssetStore>()(
|
||||||
|
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<typeof createWallAssetStore>;
|
||||||
211
app/src/store/builder/useWallStore.ts
Normal file
211
app/src/store/builder/useWallStore.ts
Normal file
@@ -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<Wall>) => 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<WallStore>()(
|
||||||
|
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<typeof createWallStore>;
|
||||||
@@ -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<Wall>) => 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<WallStore>()(
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
74
app/src/store/builder/useZoneStore.ts
Normal file
74
app/src/store/builder/useZoneStore.ts
Normal file
@@ -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<Zone>) => 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<ZoneStore>()(
|
||||||
|
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<typeof createZoneStore>;
|
||||||
@@ -1657,8 +1657,13 @@
|
|||||||
|
|
||||||
.sidebar-right-wrapper {
|
.sidebar-right-wrapper {
|
||||||
.wall-properties-container {
|
.wall-properties-container {
|
||||||
|
.wall-properties-section{
|
||||||
|
padding: 14px;
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
.header {
|
.header {
|
||||||
color: var(--background-color-button);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wall-properties {
|
.wall-properties {
|
||||||
@@ -1692,7 +1697,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
background: var(--Grays-Gray-6, #f2f2f7);
|
background: var(--background-color-secondary);
|
||||||
padding: 18px 25px;
|
padding: 18px 25px;
|
||||||
|
|
||||||
.sides-wrapper {
|
.sides-wrapper {
|
||||||
@@ -1736,8 +1741,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
width: 90%;
|
width: 100%;
|
||||||
height: 111px;
|
height: 111px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -1750,7 +1757,8 @@
|
|||||||
.materials {
|
.materials {
|
||||||
max-height: 250px;
|
max-height: 250px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin-bottom: 6px;
|
margin-top: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
|
|
||||||
.material-container {
|
.material-container {
|
||||||
|
|||||||
Reference in New Issue
Block a user