feat: Refactor aisle rendering components to use Instances for improved performance and rendering efficiency
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import AisleInstance from './instance/aisleInstance';
|
||||
import Point from '../../point/point';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import { Instances, Instance } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||
@@ -10,8 +10,16 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const arrows = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return [];
|
||||
const { arrowGeometry, arrowInstances } = useMemo(() => {
|
||||
const result = {
|
||||
arrowGeometry: null as THREE.ExtrudeGeometry | null,
|
||||
arrowInstances: [] as {
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
}[],
|
||||
};
|
||||
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return result;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
@@ -24,69 +32,67 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
direction.normalize();
|
||||
|
||||
const count = Math.floor((length + spacing) / (arrowLength + spacing));
|
||||
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
||||
|
||||
const arrowShapes: { shape: THREE.Shape; position: THREE.Vector3; rotationY: number }[] = [];
|
||||
const shape = new THREE.Shape();
|
||||
const w = width * 0.8;
|
||||
const h = arrowLength;
|
||||
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(w, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h * 0.6);
|
||||
shape.lineTo(-w, h * 0.6);
|
||||
shape.lineTo(0, 0);
|
||||
|
||||
result.arrowGeometry = new THREE.ExtrudeGeometry(shape, {
|
||||
depth: 0.01,
|
||||
bevelEnabled: false,
|
||||
});
|
||||
|
||||
result.arrowGeometry.rotateX(Math.PI / 2);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const initialOffset = arrowLength;
|
||||
const center = new THREE.Vector3().copy(start).addScaledVector(direction, initialOffset + i * (arrowLength + spacing));
|
||||
const offset = arrowLength + i * (arrowLength + spacing);
|
||||
const center = new THREE.Vector3().copy(start).addScaledVector(direction, offset);
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
const w = width * 0.8;
|
||||
const h = arrowLength;
|
||||
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(w, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h * 0.6);
|
||||
shape.lineTo(-w, h * 0.6);
|
||||
shape.lineTo(0, 0);
|
||||
|
||||
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
||||
|
||||
arrowShapes.push({ shape, position: center, rotationY: angle });
|
||||
result.arrowInstances.push({
|
||||
position: [center.x, 0, center.z],
|
||||
rotation: [0, angle, 0],
|
||||
});
|
||||
}
|
||||
|
||||
return arrowShapes;
|
||||
return result;
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (arrows.length === 0) return null;
|
||||
if (!arrowGeometry || arrowInstances.length === 0) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
name='Arrows-Aisle'
|
||||
name="Arrows-Aisle"
|
||||
uuid={aisle.aisleUuid}
|
||||
ref={aisleRef}
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
userData={aisle}
|
||||
onDoubleClick={handleClick}
|
||||
onPointerMissed={() => {
|
||||
setSelectedAisle(null);
|
||||
}}
|
||||
>
|
||||
{arrows.map(({ shape, position, rotationY }, index) => (
|
||||
<group key={index} position={[position.x, position.z, 0]} rotation={[0, 0, -rotationY]}>
|
||||
<Extrude
|
||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
))}
|
||||
<Instances geometry={arrowGeometry}>
|
||||
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} side={THREE.DoubleSide} />
|
||||
{arrowInstances.map(({ position, rotation }, i) => (
|
||||
<Instance key={i} position={position} rotation={rotation} />
|
||||
))}
|
||||
</Instances>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import { Instances, Instance } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||
@@ -10,82 +10,71 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const shapes = useMemo(() => {
|
||||
const dashInstances = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') 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 dashLength = aisle.type.dashLength || 0.5;
|
||||
const gapLength = aisle.type.gapLength || 0.3;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||
|
||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||
const totalVec = new THREE.Vector3().subVectors(end, start);
|
||||
const totalLength = totalVec.length();
|
||||
const direction = totalVec.clone().normalize();
|
||||
const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength));
|
||||
|
||||
const shapes = [];
|
||||
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const instances = [];
|
||||
|
||||
for (let i = 0; i < segmentCount; i++) {
|
||||
const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength));
|
||||
const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength);
|
||||
const center = start.clone().addScaledVector(direction, i * (dashLength + gapLength) + dashLength / 2);
|
||||
|
||||
const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2);
|
||||
const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2);
|
||||
const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2);
|
||||
const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2);
|
||||
const rotationY = Math.atan2(direction.x, direction.z);
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.moveTo(leftStart.x, leftStart.z);
|
||||
shape.lineTo(leftEnd.x, leftEnd.z);
|
||||
shape.lineTo(rightEnd.x, rightEnd.z);
|
||||
shape.lineTo(rightStart.x, rightStart.z);
|
||||
shape.closePath();
|
||||
|
||||
shapes.push(shape);
|
||||
instances.push({
|
||||
position: [center.x, 0, center.z] as [number, number, number],
|
||||
scale: [width, 0.001, dashLength] as [number, number, number],
|
||||
rotation: [0, rotationY, 0] as [number, number, number],
|
||||
});
|
||||
}
|
||||
|
||||
return shapes;
|
||||
return instances;
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (shapes.length === 0) return null;
|
||||
if (dashInstances.length === 0) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
name='Dashed-Aisle'
|
||||
name="Dashed-Aisle"
|
||||
uuid={aisle.aisleUuid}
|
||||
ref={aisleRef}
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
userData={aisle}
|
||||
onDoubleClick={handleClick}
|
||||
onPointerMissed={() => {
|
||||
setSelectedAisle(null);
|
||||
}}
|
||||
>
|
||||
{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}
|
||||
<Instances frustumCulled={false}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} />
|
||||
{dashInstances.map((inst, i) => (
|
||||
<Instance
|
||||
key={i}
|
||||
position={inst.position}
|
||||
scale={inst.scale}
|
||||
rotation={inst.rotation}
|
||||
/>
|
||||
</Extrude>
|
||||
))}
|
||||
))}
|
||||
</Instances>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashedAisle;
|
||||
export default DashedAisle;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import { Instance, Instances } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
|
||||
@@ -10,31 +10,20 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const shapes = useMemo(() => {
|
||||
const dotPositions = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.dotRadius || 0.1;
|
||||
const dotSpacing = aisle.type.gapLength || 0.5;
|
||||
const dotRadius = width * 0.6;
|
||||
|
||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||
const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / 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;
|
||||
return Array.from({ length: dotCount }, (_, i) => {
|
||||
return new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
||||
});
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
@@ -43,7 +32,14 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}
|
||||
}
|
||||
|
||||
if (shapes.length === 0) return null;
|
||||
const { dotRadius, color } = useMemo(() => {
|
||||
return {
|
||||
dotRadius: aisle.type.aisleType === 'dotted-aisle' ? ((aisle.type as any).dotRadius || 0.1) * 0.6 : 0.06,
|
||||
color: aisle.type.aisleColor || '#ffffff'
|
||||
};
|
||||
}, [aisle]);
|
||||
|
||||
if (dotPositions.length === 0) return null;
|
||||
|
||||
return (
|
||||
<group
|
||||
@@ -51,26 +47,24 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
uuid={aisle.aisleUuid}
|
||||
ref={aisleRef}
|
||||
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
rotation={[0, 0, 0]}
|
||||
userData={aisle}
|
||||
onDoubleClick={handleClick}
|
||||
onPointerMissed={() => {
|
||||
setSelectedAisle(null);
|
||||
}}
|
||||
>
|
||||
{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}
|
||||
<Instances frustumCulled={false}>
|
||||
<circleGeometry args={[dotRadius, 32]} />
|
||||
<meshStandardMaterial color={color} side={THREE.DoubleSide} />
|
||||
{dotPositions.map((position, index) => (
|
||||
<Instance
|
||||
key={index}
|
||||
position={[position.x, 0, position.z]}
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
/>
|
||||
</Extrude>
|
||||
))}
|
||||
))}
|
||||
</Instances>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as THREE from 'three';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
|
||||
import * as Constants from '../../../../types/world/worldConstants';
|
||||
import { Extrude, Html } from '@react-three/drei';
|
||||
import { Extrude, Html, Instance, Instances } from '@react-three/drei';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
||||
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
||||
@@ -289,125 +289,119 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}
|
||||
|
||||
function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const shapes = useMemo(() => {
|
||||
|
||||
const dashInstances = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') 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 dashLength = aisle.type.dashLength || 0.5;
|
||||
const gapLength = aisle.type.gapLength || 0.3;
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
|
||||
|
||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||
const totalVec = new THREE.Vector3().subVectors(end, start);
|
||||
const totalLength = totalVec.length();
|
||||
const direction = totalVec.clone().normalize();
|
||||
const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength));
|
||||
|
||||
const shapes = [];
|
||||
const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const instances = [];
|
||||
|
||||
for (let i = 0; i < segmentCount; i++) {
|
||||
const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength));
|
||||
const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength);
|
||||
const center = start.clone().addScaledVector(direction, i * (dashLength + gapLength) + dashLength / 2);
|
||||
|
||||
const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2);
|
||||
const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2);
|
||||
const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2);
|
||||
const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2);
|
||||
const rotationY = Math.atan2(direction.x, direction.z);
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
shape.moveTo(leftStart.x, leftStart.z);
|
||||
shape.lineTo(leftEnd.x, leftEnd.z);
|
||||
shape.lineTo(rightEnd.x, rightEnd.z);
|
||||
shape.lineTo(rightStart.x, rightStart.z);
|
||||
shape.closePath();
|
||||
|
||||
shapes.push(shape);
|
||||
instances.push({
|
||||
position: [center.x, 0, center.z] as [number, number, number],
|
||||
scale: [width, 0.001, dashLength] as [number, number, number],
|
||||
rotation: [0, rotationY, 0] as [number, number, number],
|
||||
});
|
||||
}
|
||||
|
||||
return shapes;
|
||||
return instances;
|
||||
}, [aisle]);
|
||||
|
||||
if (shapes.length === 0) return null;
|
||||
if (dashInstances.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}
|
||||
<Instances frustumCulled={false}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} />
|
||||
{dashInstances.map((inst, i) => (
|
||||
<Instance
|
||||
key={i}
|
||||
position={inst.position}
|
||||
scale={inst.scale}
|
||||
rotation={inst.rotation}
|
||||
/>
|
||||
</Extrude>
|
||||
))}
|
||||
))}
|
||||
</Instances>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const shapes = useMemo(() => {
|
||||
|
||||
const dotPositions = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
const width = aisle.type.dotRadius || 0.1;
|
||||
const dotSpacing = aisle.type.gapLength || 0.5;
|
||||
const dotRadius = width * 0.6;
|
||||
|
||||
const totalLength = new THREE.Vector3().subVectors(end, start).length();
|
||||
const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / 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;
|
||||
return Array.from({ length: dotCount }, (_, i) => {
|
||||
return new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2);
|
||||
});
|
||||
}, [aisle]);
|
||||
|
||||
if (shapes.length === 0) return null;
|
||||
const { dotRadius, color } = useMemo(() => {
|
||||
return {
|
||||
dotRadius: aisle.type.aisleType === 'dotted-aisle' ? ((aisle.type as any).dotRadius || 0.1) * 0.6 : 0.06,
|
||||
color: aisle.type.aisleColor || '#ffffff'
|
||||
};
|
||||
}, [aisle]);
|
||||
|
||||
if (dotPositions.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}
|
||||
<Instances frustumCulled={false}>
|
||||
<circleGeometry args={[dotRadius, 32]} />
|
||||
<meshStandardMaterial color={color} side={THREE.DoubleSide} />
|
||||
{dotPositions.map((position, index) => (
|
||||
<Instance
|
||||
key={index}
|
||||
position={[position.x, 0, position.z]}
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
/>
|
||||
</Extrude>
|
||||
))}
|
||||
))}
|
||||
</Instances>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const arrows = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return [];
|
||||
|
||||
const { arrowGeometry, arrowInstances } = useMemo(() => {
|
||||
const result = {
|
||||
arrowGeometry: null as THREE.ExtrudeGeometry | null,
|
||||
arrowInstances: [] as {
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
}[],
|
||||
};
|
||||
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return result;
|
||||
|
||||
const start = new THREE.Vector3(...aisle.points[0].position);
|
||||
const end = new THREE.Vector3(...aisle.points[1].position);
|
||||
@@ -420,55 +414,53 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
direction.normalize();
|
||||
|
||||
const count = Math.floor((length + spacing) / (arrowLength + spacing));
|
||||
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
||||
|
||||
const arrowShapes: { shape: THREE.Shape; position: THREE.Vector3; rotationY: number }[] = [];
|
||||
const shape = new THREE.Shape();
|
||||
const w = width * 0.8;
|
||||
const h = arrowLength;
|
||||
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(w, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h * 0.6);
|
||||
shape.lineTo(-w, h * 0.6);
|
||||
shape.lineTo(0, 0);
|
||||
|
||||
result.arrowGeometry = new THREE.ExtrudeGeometry(shape, {
|
||||
depth: 0.01,
|
||||
bevelEnabled: false,
|
||||
});
|
||||
|
||||
result.arrowGeometry.rotateX(Math.PI / 2);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const initialOffset = arrowLength;
|
||||
const center = new THREE.Vector3().copy(start).addScaledVector(direction, initialOffset + i * (arrowLength + spacing));
|
||||
const offset = arrowLength + i * (arrowLength + spacing);
|
||||
const center = new THREE.Vector3().copy(start).addScaledVector(direction, offset);
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
const w = width * 0.8;
|
||||
const h = arrowLength;
|
||||
|
||||
shape.moveTo(0, 0);
|
||||
shape.lineTo(w, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h * 0.6);
|
||||
shape.lineTo(w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h);
|
||||
shape.lineTo(-w * 0.4, h * 0.6);
|
||||
shape.lineTo(-w, h * 0.6);
|
||||
shape.lineTo(0, 0);
|
||||
|
||||
const angle = Math.atan2(direction.x, direction.z) + Math.PI;
|
||||
|
||||
arrowShapes.push({ shape, position: center, rotationY: angle });
|
||||
result.arrowInstances.push({
|
||||
position: [center.x, 0, center.z],
|
||||
rotation: [0, angle, 0],
|
||||
});
|
||||
}
|
||||
|
||||
return arrowShapes;
|
||||
return result;
|
||||
}, [aisle]);
|
||||
|
||||
if (arrows.length === 0) return null;
|
||||
if (!arrowGeometry || arrowInstances.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]}
|
||||
>
|
||||
{arrows.map(({ shape, position, rotationY }, index) => (
|
||||
<group key={index} position={[position.x, position.z, 0]} rotation={[0, 0, -rotationY]}>
|
||||
<Extrude
|
||||
args={[shape, { depth: 0.01, bevelEnabled: false }]}
|
||||
receiveShadow
|
||||
castShadow
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={aisle.type.aisleColor || '#ffffff'}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</group>
|
||||
))}
|
||||
<Instances geometry={arrowGeometry} limit={100}>
|
||||
<meshStandardMaterial color={aisle.type.aisleColor || '#ffffff'} side={THREE.DoubleSide} />
|
||||
{arrowInstances.map(({ position, rotation }, i) => (
|
||||
<Instance key={i} position={position} rotation={rotation} />
|
||||
))}
|
||||
</Instances>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ export default function PolygonGenerator({
|
||||
aisle.type.aisleType === "arc-aisle"
|
||||
)
|
||||
|
||||
|
||||
arcAndCircleResult.forEach((arc) => {
|
||||
const arcGroup = scene.getObjectByProperty("uuid", arc.aisleUuid);
|
||||
if (!arcGroup) return;
|
||||
@@ -60,7 +59,6 @@ export default function PolygonGenerator({
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const wallPoints: THREE.Vector3[][] = walls
|
||||
.map((wall) =>
|
||||
wall.points.map((pt) => new THREE.Vector3(pt.position[0], pt.position[1], pt.position[2]))
|
||||
|
||||
Reference in New Issue
Block a user