feat: enhance aisle management with new properties and types
This commit is contained in:
parent
13ec906fac
commit
cb414f2824
|
@ -16,6 +16,7 @@ import Simulations from "./simulation/Simulations";
|
||||||
import useVersionHistoryStore, {
|
import useVersionHistoryStore, {
|
||||||
useSaveVersion,
|
useSaveVersion,
|
||||||
useSelectedFloorItem,
|
useSelectedFloorItem,
|
||||||
|
useToolMode,
|
||||||
} from "../../../store/builder/store";
|
} from "../../../store/builder/store";
|
||||||
import {
|
import {
|
||||||
useSelectedEventData,
|
useSelectedEventData,
|
||||||
|
@ -26,10 +27,12 @@ import AsstePropertiies 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";
|
||||||
|
|
||||||
const SideBarRight: React.FC = () => {
|
const SideBarRight: React.FC = () => {
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { toggleUIRight } = useToggleStore();
|
const { toggleUIRight } = useToggleStore();
|
||||||
|
const { toolMode } = useToolMode();
|
||||||
const { subModule, setSubModule } = useSubModuleStore();
|
const { subModule, setSubModule } = useSubModuleStore();
|
||||||
const { selectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem } = useSelectedFloorItem();
|
||||||
const { selectedEventData } = useSelectedEventData();
|
const { selectedEventData } = useSelectedEventData();
|
||||||
|
@ -62,9 +65,8 @@ const SideBarRight: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`sidebar-right-wrapper ${
|
className={`sidebar-right-wrapper ${toggleUIRight && !isVersionSaved ? "open" : "closed"
|
||||||
toggleUIRight && !isVersionSaved ? "open" : "closed"
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<Header />
|
<Header />
|
||||||
{toggleUIRight && (
|
{toggleUIRight && (
|
||||||
|
@ -74,9 +76,8 @@ const SideBarRight: React.FC = () => {
|
||||||
{activeModule !== "simulation" && (
|
{activeModule !== "simulation" && (
|
||||||
<button
|
<button
|
||||||
id="sidebar-action-list-properties"
|
id="sidebar-action-list-properties"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
|
||||||
subModule === "properties" ? "active" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSubModule("properties");
|
setSubModule("properties");
|
||||||
setVersionHistory(false);
|
setVersionHistory(false);
|
||||||
|
@ -90,9 +91,8 @@ const SideBarRight: React.FC = () => {
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
id="sidebar-action-list-simulation"
|
id="sidebar-action-list-simulation"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
|
||||||
subModule === "simulations" ? "active" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSubModule("simulations");
|
setSubModule("simulations");
|
||||||
setVersionHistory(false);
|
setVersionHistory(false);
|
||||||
|
@ -103,9 +103,8 @@ const SideBarRight: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="sidebar-action-list-mechanics"
|
id="sidebar-action-list-mechanics"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
|
||||||
subModule === "mechanics" ? "active" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSubModule("mechanics");
|
setSubModule("mechanics");
|
||||||
setVersionHistory(false);
|
setVersionHistory(false);
|
||||||
|
@ -116,9 +115,8 @@ const SideBarRight: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="sidebar-action-list-analysis"
|
id="sidebar-action-list-analysis"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
|
||||||
subModule === "analysis" ? "active" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSubModule("analysis");
|
setSubModule("analysis");
|
||||||
setVersionHistory(false);
|
setVersionHistory(false);
|
||||||
|
@ -147,7 +145,10 @@ const SideBarRight: React.FC = () => {
|
||||||
!selectedFloorItem && (
|
!selectedFloorItem && (
|
||||||
<div className="sidebar-right-container">
|
<div className="sidebar-right-container">
|
||||||
<div className="sidebar-right-content-container">
|
<div className="sidebar-right-content-container">
|
||||||
<GlobalProperties />
|
{toolMode === "Aisle" ? (
|
||||||
|
<AisleProperties />) : (
|
||||||
|
<GlobalProperties />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -11,9 +11,10 @@ import Dashed from "../../../../assets/image/aisleTypes/Dashed.png";
|
||||||
import Directional from "../../../../assets/image/aisleTypes/Directional.png";
|
import Directional from "../../../../assets/image/aisleTypes/Directional.png";
|
||||||
import Dotted from "../../../../assets/image/aisleTypes/Dotted.png";
|
import Dotted from "../../../../assets/image/aisleTypes/Dotted.png";
|
||||||
import Solid from "../../../../assets/image/aisleTypes/Solid.png";
|
import Solid from "../../../../assets/image/aisleTypes/Solid.png";
|
||||||
|
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||||
|
|
||||||
interface TextureItem {
|
interface TextureItem {
|
||||||
color: string;
|
color: AisleColors;
|
||||||
id: string;
|
id: string;
|
||||||
brief: string;
|
brief: string;
|
||||||
texture: string;
|
texture: string;
|
||||||
|
@ -22,10 +23,7 @@ interface TextureItem {
|
||||||
const AisleProperties: React.FC = () => {
|
const AisleProperties: React.FC = () => {
|
||||||
const [collapsePresets, setCollapsePresets] = useState(false);
|
const [collapsePresets, setCollapsePresets] = useState(false);
|
||||||
const [collapseTexture, setCollapseTexture] = useState(true);
|
const [collapseTexture, setCollapseTexture] = useState(true);
|
||||||
const [selectedTexture, setSelectedTexture] = useState<string | null>(
|
const { aisleType, aisleWidth, aisleColor, setAisleType, setAisleColor, setAisleWidth } = useBuilderStore();
|
||||||
"yellow1"
|
|
||||||
);
|
|
||||||
const [selectedType, setSelectedType] = useState<string | null>("Solid");
|
|
||||||
|
|
||||||
const aisleTextureList: TextureItem[] = [
|
const aisleTextureList: TextureItem[] = [
|
||||||
{ color: "gray", id: "gray", brief: "basic", texture: "" },
|
{ color: "gray", id: "gray", brief: "basic", texture: "" },
|
||||||
|
@ -60,58 +58,82 @@ const AisleProperties: React.FC = () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const aisleTypes = [
|
const aisleTypes: {
|
||||||
{
|
name: string;
|
||||||
name: "Solid",
|
type: AisleTypes;
|
||||||
id: "1",
|
id: string;
|
||||||
thumbnail: Solid,
|
thumbnail: string;
|
||||||
},
|
}[] = [
|
||||||
{
|
{
|
||||||
name: "Dotted",
|
name: "Solid",
|
||||||
id: "2",
|
type: "solid-aisle",
|
||||||
thumbnail: Dotted,
|
id: "1",
|
||||||
},
|
thumbnail: Solid,
|
||||||
{
|
},
|
||||||
name: "Dashed",
|
{
|
||||||
id: "3",
|
name: "Dotted",
|
||||||
thumbnail: Dashed,
|
type: "dotted-aisle",
|
||||||
},
|
id: "2",
|
||||||
{
|
thumbnail: Dotted,
|
||||||
name: "Arrow",
|
},
|
||||||
id: "4",
|
{
|
||||||
thumbnail: Arrow,
|
name: "Dashed",
|
||||||
},
|
type: "dashed-aisle",
|
||||||
{
|
id: "3",
|
||||||
name: "Contiuous Arrows",
|
thumbnail: Dashed,
|
||||||
id: "5",
|
},
|
||||||
thumbnail: Arrows,
|
{
|
||||||
},
|
name: "Arrow",
|
||||||
{
|
type: "arrow-aisle",
|
||||||
name: "Directional",
|
id: "4",
|
||||||
id: "6",
|
thumbnail: Arrow,
|
||||||
thumbnail: Directional,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: "Contiuous Arrows",
|
||||||
name: "Arc",
|
type: "arrows-aisle",
|
||||||
id: "7",
|
id: "5",
|
||||||
thumbnail: Arc,
|
thumbnail: Arrows,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Circle",
|
name: "Directional",
|
||||||
id: "8",
|
type: "junction-aisle",
|
||||||
thumbnail: Circle,
|
id: "6",
|
||||||
},
|
thumbnail: Directional,
|
||||||
];
|
},
|
||||||
|
{
|
||||||
|
name: "Arc",
|
||||||
|
type: "arc-aisle",
|
||||||
|
id: "7",
|
||||||
|
thumbnail: Arc,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Circle",
|
||||||
|
type: "circle-aisle",
|
||||||
|
id: "8",
|
||||||
|
thumbnail: Circle,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleAisleWidthChange = (value: string) => {
|
||||||
|
const width = parseFloat(value);
|
||||||
|
if (isNaN(width)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAisleWidth(width);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="aisle-properties-container">
|
<div className="aisle-properties-container">
|
||||||
<div className="header">Properties</div>
|
<div className="header">Properties</div>
|
||||||
<section>
|
<section>
|
||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
label="Width"
|
label="Aisle Width"
|
||||||
value="1"
|
value={`${aisleWidth}`}
|
||||||
editableLabel
|
min={0.1}
|
||||||
onChange={() => {}}
|
step={0.1}
|
||||||
|
max={2}
|
||||||
|
defaultValue="0.1"
|
||||||
|
onChange={handleAisleWidthChange}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -132,12 +154,11 @@ const AisleProperties: React.FC = () => {
|
||||||
{aisleTypes.map((val) => (
|
{aisleTypes.map((val) => (
|
||||||
<div className="preset-list" key={val.id}>
|
<div className="preset-list" key={val.id}>
|
||||||
<button
|
<button
|
||||||
className={`thumbnail ${
|
className={`thumbnail ${aisleType === val.type ? "selected" : ""
|
||||||
selectedType === val.name ? "selected" : ""
|
}`}
|
||||||
}`}
|
|
||||||
title={val.name}
|
title={val.name}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedType(val.name);
|
setAisleType(val.type);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img src={val.thumbnail} alt="" />
|
<img src={val.thumbnail} alt="" />
|
||||||
|
@ -170,11 +191,10 @@ const AisleProperties: React.FC = () => {
|
||||||
<button
|
<button
|
||||||
key={val.id}
|
key={val.id}
|
||||||
title={val.brief || val.id}
|
title={val.brief || val.id}
|
||||||
className={`aisle-list ${
|
className={`aisle-list ${aisleColor === val.color ? "selected" : ""
|
||||||
selectedTexture === val.id ? "selected" : ""
|
}`}
|
||||||
}`}
|
onClick={() => setAisleColor(val.color)}
|
||||||
onClick={() => setSelectedTexture(val.id)}
|
aria-pressed={aisleColor === val.id}
|
||||||
aria-pressed={selectedTexture === val.id}
|
|
||||||
>
|
>
|
||||||
<div className="texture-display">{val.texture}</div>
|
<div className="texture-display">{val.texture}</div>
|
||||||
<div className="aisle-color">{val.color}</div>
|
<div className="aisle-color">{val.color}</div>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import DashedAisle from './aisleTypes/dashedAisle';
|
import DashedAisle from './aisleTypes/dashedAisle';
|
||||||
|
import DottedAisle from './aisleTypes/dottedAisle';
|
||||||
import SolidAisle from './aisleTypes/solidAisle';
|
import SolidAisle from './aisleTypes/solidAisle';
|
||||||
|
|
||||||
function AisleInstance({ aisle }: { readonly aisle: Aisle }) {
|
function AisleInstance({ aisle }: { readonly aisle: Aisle }) {
|
||||||
|
@ -12,6 +13,10 @@ function AisleInstance({ aisle }: { readonly aisle: Aisle }) {
|
||||||
{aisle.type.aisleType === 'dashed-aisle' && (
|
{aisle.type.aisleType === 'dashed-aisle' && (
|
||||||
<DashedAisle aisle={aisle} />
|
<DashedAisle aisle={aisle} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{aisle.type.aisleType === 'dotted-aisle' && (
|
||||||
|
<DottedAisle aisle={aisle} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
const width = aisle.type.width || 0.1;
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
const dashLength = 0.5;
|
const dashLength = 0.5;
|
||||||
const gapLength = 0.3;
|
const gapLength = 0.3;
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
castShadow
|
castShadow
|
||||||
>
|
>
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={aisle.type.color || '#ffffff'}
|
color={aisle.type.aisleColor || '#ffffff'}
|
||||||
side={THREE.DoubleSide}
|
side={THREE.DoubleSide}
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
</Extrude>
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { Extrude } from '@react-three/drei';
|
||||||
|
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||||
|
|
||||||
|
function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
|
const shapes = useMemo(() => {
|
||||||
|
if (aisle.points.length < 2) return [];
|
||||||
|
|
||||||
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
|
const dotSpacing = 0.5;
|
||||||
|
const dotRadius = width * 0.4;
|
||||||
|
|
||||||
|
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||||
|
const dotCount = Math.floor(totalLength / dotSpacing);
|
||||||
|
|
||||||
|
const shapes = [];
|
||||||
|
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
||||||
|
|
||||||
|
for (let i = 0; i < dotCount; i++) {
|
||||||
|
const dotCenter = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
||||||
|
|
||||||
|
const shape = new THREE.Shape();
|
||||||
|
shape.absarc(dotCenter.x, dotCenter.z, dotRadius, 0, Math.PI * 2, false);
|
||||||
|
|
||||||
|
shapes.push(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shapes;
|
||||||
|
}, [aisle]);
|
||||||
|
|
||||||
|
if (shapes.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
|
rotation={[Math.PI / 2, 0, 0]}
|
||||||
|
>
|
||||||
|
{shapes.map((shape, index) => (
|
||||||
|
<Extrude
|
||||||
|
key={index}
|
||||||
|
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
||||||
|
receiveShadow
|
||||||
|
castShadow
|
||||||
|
>
|
||||||
|
<meshStandardMaterial
|
||||||
|
color={aisle.type.aisleColor || '#ffffff'}
|
||||||
|
side={THREE.DoubleSide}
|
||||||
|
/>
|
||||||
|
</Extrude>
|
||||||
|
))}
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DottedAisle;
|
|
@ -9,7 +9,7 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
const width = aisle.type.width || 0.1;
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
|
|
||||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
||||||
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||||
|
@ -42,7 +42,7 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
castShadow
|
castShadow
|
||||||
>
|
>
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={aisle.type.color || '#ffffff'}
|
color={aisle.type.aisleColor || '#ffffff'}
|
||||||
side={THREE.DoubleSide}
|
side={THREE.DoubleSide}
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
</Extrude>
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { useEffect, useMemo, 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 { useAisleStore } from '../../../../store/builder/useAisleStore';
|
import { useAisleStore } from '../../../../store/builder/useAisleStore';
|
||||||
import * as Constants from '../../../../types/world/worldConstants';
|
|
||||||
import ReferenceAisle from './referenceAisle';
|
import ReferenceAisle from './referenceAisle';
|
||||||
|
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||||
|
|
||||||
function AisleCreator() {
|
function AisleCreator() {
|
||||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||||
|
@ -17,14 +17,14 @@ function AisleCreator() {
|
||||||
|
|
||||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
const [aisleType, setAisleType] = useState<'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle'>('dashed-aisle');
|
const { aisleType, aisleWidth, aisleColor } = useBuilderStore();
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (tempPoints.length > 0) {
|
// if (tempPoints.length > 0) {
|
||||||
setTempPoints([]);
|
// setTempPoints([]);
|
||||||
setIsCreating(false);
|
// setIsCreating(false);
|
||||||
}
|
// }
|
||||||
}, [aisleType]);
|
// }, [aisleType]);
|
||||||
|
|
||||||
const allPoints = useMemo(() => {
|
const allPoints = useMemo(() => {
|
||||||
const points: Point[] = [];
|
const points: Point[] = [];
|
||||||
|
@ -102,10 +102,9 @@ function AisleCreator() {
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
typeName: 'Aisle',
|
typeName: 'Aisle',
|
||||||
material: 'default',
|
|
||||||
aisleType: aisleType,
|
aisleType: aisleType,
|
||||||
color: Constants.aisleConfig.defaultColor,
|
aisleColor: aisleColor,
|
||||||
width: Constants.aisleConfig.width
|
aisleWidth: aisleWidth
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,33 +113,7 @@ function AisleCreator() {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
} else if (['arc-aisle', 'circle-aisle', 'arrow-aisle', 'junction-aisle'].includes(aisleType)) {
|
} else if (['arc-aisle', 'circle-aisle', 'arrow-aisle', 'junction-aisle'].includes(aisleType)) {
|
||||||
const newPoint: Point = {
|
console.log();
|
||||||
uuid: THREE.MathUtils.generateUUID(),
|
|
||||||
position: [point.x, point.y, point.z],
|
|
||||||
layer: activeLayer
|
|
||||||
};
|
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
|
||||||
setTempPoints([newPoint]);
|
|
||||||
setIsCreating(true);
|
|
||||||
} else {
|
|
||||||
const aisle: Aisle = {
|
|
||||||
uuid: THREE.MathUtils.generateUUID(),
|
|
||||||
points: [tempPoints[0], newPoint],
|
|
||||||
type: {
|
|
||||||
typeName: 'Aisle',
|
|
||||||
material: 'default',
|
|
||||||
aisleType: aisleType,
|
|
||||||
color: Constants.aisleConfig.defaultColor,
|
|
||||||
width: Constants.aisleConfig.width
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
addAisle(aisle);
|
|
||||||
|
|
||||||
setTempPoints([]);
|
|
||||||
setIsCreating(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -170,7 +143,7 @@ function AisleCreator() {
|
||||||
canvasElement.removeEventListener("click", onMouseClick);
|
canvasElement.removeEventListener("click", onMouseClick);
|
||||||
canvasElement.removeEventListener("contextmenu", onContext);
|
canvasElement.removeEventListener("contextmenu", onContext);
|
||||||
};
|
};
|
||||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType]);
|
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType, aisleColor, aisleWidth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -187,8 +160,8 @@ function AisleCreator() {
|
||||||
</mesh>
|
</mesh>
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<ReferenceAisle tempPoints={tempPoints} aisleType={aisleType} />
|
<ReferenceAisle tempPoints={tempPoints} aisleType={aisleType} aisleWidth={aisleWidth} aisleColor={aisleColor} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,12 @@ import { Extrude } from '@react-three/drei';
|
||||||
|
|
||||||
interface ReferenceAisleProps {
|
interface ReferenceAisleProps {
|
||||||
tempPoints: Point[];
|
tempPoints: Point[];
|
||||||
aisleType: 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle';
|
aisleType: AisleTypes;
|
||||||
|
aisleWidth: number;
|
||||||
|
aisleColor: AisleColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReferenceAisle({ tempPoints, aisleType }: Readonly<ReferenceAisleProps>) {
|
function ReferenceAisle({ tempPoints, aisleType, aisleWidth, aisleColor }: Readonly<ReferenceAisleProps>) {
|
||||||
const { pointer, raycaster, camera } = useThree();
|
const { pointer, raycaster, camera } = useThree();
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
|
@ -41,10 +43,9 @@ function ReferenceAisle({ tempPoints, aisleType }: Readonly<ReferenceAisleProps>
|
||||||
],
|
],
|
||||||
type: {
|
type: {
|
||||||
typeName: 'Aisle',
|
typeName: 'Aisle',
|
||||||
material: 'default',
|
|
||||||
aisleType: aisleType,
|
aisleType: aisleType,
|
||||||
color: Constants.aisleConfig.defaultColor,
|
aisleColor: aisleColor,
|
||||||
width: Constants.aisleConfig.width
|
aisleWidth: aisleWidth
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,7 @@ function ReferenceAisle({ tempPoints, aisleType }: Readonly<ReferenceAisleProps>
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTempAisle(null);
|
setTempAisle(null);
|
||||||
}, [toolMode, toggleView, tempPoints.length, aisleType]);
|
}, [toolMode, toggleView, tempPoints.length, aisleType, aisleWidth, aisleColor]);
|
||||||
|
|
||||||
if (!tempAisle) return null;
|
if (!tempAisle) return null;
|
||||||
|
|
||||||
|
@ -65,6 +66,8 @@ function ReferenceAisle({ tempPoints, aisleType }: Readonly<ReferenceAisleProps>
|
||||||
return <SolidAisle aisle={tempAisle} />;
|
return <SolidAisle aisle={tempAisle} />;
|
||||||
case 'dashed-aisle':
|
case 'dashed-aisle':
|
||||||
return <DashedAisle aisle={tempAisle} />;
|
return <DashedAisle aisle={tempAisle} />;
|
||||||
|
case 'dotted-aisle':
|
||||||
|
return <DottedAisle aisle={tempAisle} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -79,14 +82,13 @@ function ReferenceAisle({ tempPoints, aisleType }: Readonly<ReferenceAisleProps>
|
||||||
|
|
||||||
export default ReferenceAisle;
|
export default ReferenceAisle;
|
||||||
|
|
||||||
|
|
||||||
function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
const shape = useMemo(() => {
|
const shape = useMemo(() => {
|
||||||
if (aisle.points.length < 2) return null;
|
if (aisle.points.length < 2) return null;
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
const width = aisle.type.width || 0.1;
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
|
|
||||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
||||||
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||||
|
@ -119,7 +121,7 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
castShadow
|
castShadow
|
||||||
>
|
>
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={aisle.type.color || '#ffffff'}
|
color={aisle.type.aisleColor || '#ffffff'}
|
||||||
side={THREE.DoubleSide}
|
side={THREE.DoubleSide}
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
</Extrude>
|
||||||
|
@ -133,7 +135,7 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
|
|
||||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
const width = aisle.type.width || 0.1;
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
const dashLength = 0.5;
|
const dashLength = 0.5;
|
||||||
const gapLength = 0.3;
|
const gapLength = 0.3;
|
||||||
|
|
||||||
|
@ -183,7 +185,7 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
castShadow
|
castShadow
|
||||||
>
|
>
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={aisle.type.color || '#ffffff'}
|
color={aisle.type.aisleColor || '#ffffff'}
|
||||||
side={THREE.DoubleSide}
|
side={THREE.DoubleSide}
|
||||||
/>
|
/>
|
||||||
</Extrude>
|
</Extrude>
|
||||||
|
@ -191,3 +193,55 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||||
|
const shapes = useMemo(() => {
|
||||||
|
if (aisle.points.length < 2) return [];
|
||||||
|
|
||||||
|
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||||
|
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||||
|
const width = aisle.type.aisleWidth || 0.1;
|
||||||
|
const dotSpacing = 0.5;
|
||||||
|
const dotRadius = width * 0.4;
|
||||||
|
|
||||||
|
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||||
|
const dotCount = Math.floor(totalLength / dotSpacing);
|
||||||
|
|
||||||
|
const shapes = [];
|
||||||
|
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
||||||
|
|
||||||
|
for (let i = 0; i < dotCount; i++) {
|
||||||
|
const dotCenter = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
||||||
|
|
||||||
|
const shape = new THREE.Shape();
|
||||||
|
shape.absarc(dotCenter.x, dotCenter.z, dotRadius, 0, Math.PI * 2, false);
|
||||||
|
|
||||||
|
shapes.push(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shapes;
|
||||||
|
}, [aisle]);
|
||||||
|
|
||||||
|
if (shapes.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||||
|
rotation={[Math.PI / 2, 0, 0]}
|
||||||
|
>
|
||||||
|
{shapes.map((shape, index) => (
|
||||||
|
<Extrude
|
||||||
|
key={index}
|
||||||
|
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
||||||
|
receiveShadow
|
||||||
|
castShadow
|
||||||
|
>
|
||||||
|
<meshStandardMaterial
|
||||||
|
color={aisle.type.aisleColor || '#ffffff'}
|
||||||
|
side={THREE.DoubleSide}
|
||||||
|
/>
|
||||||
|
</Extrude>
|
||||||
|
))}
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
|
@ -9,8 +9,7 @@ interface AisleStore {
|
||||||
removeAisle: (uuid: string) => void;
|
removeAisle: (uuid: string) => void;
|
||||||
setPosition: (pointUuid: string, position: [number, number, number]) => void;
|
setPosition: (pointUuid: string, position: [number, number, number]) => void;
|
||||||
setLayer: (pointUuid: string, layer: number) => void;
|
setLayer: (pointUuid: string, layer: number) => void;
|
||||||
setMaterial: (aisleUuid: string, material: string) => void;
|
setColor: (aisleUuid: string, color: AisleColors) => void;
|
||||||
setColor: (aisleUuid: string, color: string) => void;
|
|
||||||
setWidth: (aisleUuid: string, width: number) => void;
|
setWidth: (aisleUuid: string, width: number) => void;
|
||||||
getAisleById: (uuid: string) => Aisle | undefined;
|
getAisleById: (uuid: string) => Aisle | undefined;
|
||||||
}
|
}
|
||||||
|
@ -58,24 +57,17 @@ export const useAisleStore = create<AisleStore>()(
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setMaterial: (aisleUuid: string, material: string) => set((state) => {
|
setColor: (aisleUuid: string, color: AisleColors) => set((state) => {
|
||||||
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
|
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
|
||||||
if (aisle) {
|
if (aisle) {
|
||||||
aisle.type.material = material;
|
aisle.type.aisleColor = color;
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
setColor: (aisleUuid: string, color: string) => set((state) => {
|
|
||||||
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
|
|
||||||
if (aisle) {
|
|
||||||
aisle.type.color = color;
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setWidth: (aisleUuid: string, width: number) => set((state) => {
|
setWidth: (aisleUuid: string, width: number) => set((state) => {
|
||||||
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
|
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
|
||||||
if (aisle) {
|
if (aisle) {
|
||||||
aisle.type.width = width;
|
aisle.type.aisleWidth = width;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
interface BuilderState {
|
||||||
|
aisleType: AisleTypes;
|
||||||
|
aisleWidth: number;
|
||||||
|
aisleColor: AisleColors;
|
||||||
|
setAisleType: (type: AisleTypes) => void;
|
||||||
|
setAisleWidth: (width: number) => void;
|
||||||
|
setAisleColor: (color: AisleColors) => void;
|
||||||
|
setAisleProperties: (type: AisleTypes, width: number, color: AisleColors) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useBuilderStore = create<BuilderState>()(
|
||||||
|
immer((set) => ({
|
||||||
|
aisleType: 'solid-aisle',
|
||||||
|
aisleWidth: 0.1,
|
||||||
|
aisleColor: 'gray',
|
||||||
|
setAisleType: (type) => {
|
||||||
|
set((state) => {
|
||||||
|
state.aisleType = type;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setAisleWidth: (width) => {
|
||||||
|
set((state) => {
|
||||||
|
state.aisleWidth = width;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setAisleColor: (color) => {
|
||||||
|
set((state) => {
|
||||||
|
state.aisleColor = color;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setAisleProperties: (type, width, color) => {
|
||||||
|
set((state) => {
|
||||||
|
state.aisleType = type;
|
||||||
|
state.aisleWidth = width;
|
||||||
|
state.aisleColor = color;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
);
|
|
@ -37,12 +37,15 @@ interface Point {
|
||||||
layer: number;
|
layer: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AisleTypes = | 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle';
|
||||||
|
|
||||||
|
type AisleColors = | 'gray' | 'yellow' | 'green' | 'orange' | 'blue' | 'purple' | 'red' | 'bright green' | 'yellow-black' | 'white-black'
|
||||||
|
|
||||||
interface AisleType {
|
interface AisleType {
|
||||||
typeName: 'Aisle';
|
typeName: 'Aisle';
|
||||||
material: string;
|
aisleType: AisleTypes;
|
||||||
aisleType: 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle'| 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle';
|
aisleColor: AisleColors;
|
||||||
color: string;
|
aisleWidth: number;
|
||||||
width: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Aisle {
|
interface Aisle {
|
||||||
|
|
Loading…
Reference in New Issue