diff --git a/app/src/modules/builder/aisle/Instances/aisleInstances.tsx b/app/src/modules/builder/aisle/Instances/aisleInstances.tsx index 7683457..cb4ca35 100644 --- a/app/src/modules/builder/aisle/Instances/aisleInstances.tsx +++ b/app/src/modules/builder/aisle/Instances/aisleInstances.tsx @@ -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'; diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx index 8bc6fd5..c49388a 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx @@ -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 ( { setSelectedAisle(null); }} > - {arrows.map(({ shape, position, rotationY }, index) => ( - - - - - - ))} + + + {arrowInstances.map(({ position, rotation }, i) => ( + + ))} + ); } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx index bbdad90..72fb3c7 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx @@ -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 ( { setSelectedAisle(null); }} > - {shapes.map((shape, index) => ( - - + + + {dashInstances.map((inst, i) => ( + - - ))} + ))} + ); } -export default DashedAisle; \ No newline at end of file +export default DashedAisle; diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx index bf17d08..e411269 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx @@ -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 ( { setSelectedAisle(null); }} > - {shapes.map((shape, index) => ( - - + + + {dotPositions.map((position, index) => ( + - - ))} + ))} + ); } diff --git a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx index 6ce66fe..38f0dbc 100644 --- a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx +++ b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx @@ -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 ( - {shapes.map((shape, index) => ( - - + + + {dashInstances.map((inst, i) => ( + - - ))} + ))} + ); } 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 ( - {shapes.map((shape, index) => ( - - + + + {dotPositions.map((position, index) => ( + - - ))} + ))} + ); } 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 ( - {arrows.map(({ shape, position, rotationY }, index) => ( - - - - - - ))} + + + {arrowInstances.map(({ position, rotation }, i) => ( + + ))} + ); } diff --git a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx index 19e9482..57f8ab2 100644 --- a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx +++ b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx @@ -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]))