From cba9edd7c47e8164cb7f43493efa5c70a4e70501 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 3 Jun 2025 14:45:17 +0530 Subject: [PATCH] refactor: Update aisle types and properties, integrate arc-aisle handling --- .../properties/AisleProperties.tsx | 4 +- .../Instances/instance/aisleInstance.tsx | 5 + .../instance/aisleTypes/arcAisle.tsx | 103 ++++++++++++++++++ .../aisle/aisleCreator/aisleCreator.tsx | 37 +++---- .../aisle/aisleCreator/referenceAisle.tsx | 88 ++++++++++++++- .../selectionControls/boundingBoxHelper.tsx | 21 +++- .../selectionControls/selectionControls.tsx | 2 +- app/src/store/builder/useAisleStore.ts | 16 +-- app/src/types/builderTypes.d.ts | 9 +- 9 files changed, 235 insertions(+), 50 deletions(-) create mode 100644 app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx diff --git a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx index a613699..644aad2 100644 --- a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx @@ -194,7 +194,7 @@ const AisleProperties: React.FC = () => { } ); - case 'junction-aisle': + case 'junction-aisle': case 'arc-aisle': return ( <> {aisleType && @@ -206,7 +206,7 @@ const AisleProperties: React.FC = () => { /> } - ) + ); default: return null; } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx index c8ecdd7..181d926 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx @@ -1,3 +1,4 @@ +import ArcAisle from './aisleTypes/arcAisle'; import ArrowAisle from './aisleTypes/arrowAisle'; import ArrowsAisle from './aisleTypes/arrowsAisle'; import CircleAisle from './aisleTypes/circleAisle'; @@ -37,6 +38,10 @@ function AisleInstance({ aisle }: { readonly aisle: Aisle }) { {aisle.type.aisleType === 'junction-aisle' && ( )} + + {aisle.type.aisleType === 'arc-aisle' && ( + + )} ); } diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx new file mode 100644 index 0000000..824938d --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx @@ -0,0 +1,103 @@ +import * as THREE from 'three'; +import { useMemo, useRef } from 'react'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../../types/world/worldConstants'; +import { useToolMode } from '../../../../../../store/builder/store'; +import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore'; + +function ArcAisle({ aisle }: { readonly aisle: Aisle }) { + const aisleRef = useRef(null); + const { toolMode } = useToolMode(); + const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + + const arc = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'arc-aisle') return null; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.5; + const isFlipped = aisle.type.isFlipped || false; + + const direction = new THREE.Vector3().subVectors(end, start); + const length = direction.length(); + direction.normalize(); + + const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + if (!isFlipped) perpendicular.negate(); + + const arcHeight = length * 0.25; + const midPoint = new THREE.Vector3().lerpVectors(start, end, 0.5); + const controlPoint = new THREE.Vector3().copy(midPoint).addScaledVector(perpendicular, arcHeight); + + const widthOffset = perpendicular.clone().multiplyScalar(width / 2); + + const p1 = new THREE.Vector3().copy(start).add(widthOffset); + const p2 = new THREE.Vector3().copy(end).add(widthOffset); + const p3 = new THREE.Vector3().copy(end).sub(widthOffset); + const p4 = new THREE.Vector3().copy(start).sub(widthOffset); + + const shape = new THREE.Shape(); + shape.moveTo(p1.x, p1.z); + + shape.quadraticCurveTo( + controlPoint.x + widthOffset.x, + controlPoint.z + widthOffset.z, + p2.x, p2.z + ); + + shape.lineTo(p3.x, p3.z); + + shape.quadraticCurveTo( + controlPoint.x - widthOffset.x, + controlPoint.z - widthOffset.z, + p4.x, p4.z + ); + + shape.lineTo(p1.x, p1.z); + + return { + shape, + position: new THREE.Vector3(0, 0, 0), + rotationY: 0 + }; + }, [aisle]); + + const handleClick = () => { + if (toolMode === 'move' && !hoveredPoint) { + setSelectedAisle(aisleRef.current); + } + } + + if (!arc) return null; + + return ( + { + setSelectedAisle(null); + }} + > + + + + + ); +} + +export default ArcAisle; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx index dca5654..d4a4007 100644 --- a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx +++ b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx @@ -118,24 +118,6 @@ function AisleCreator() { addAisle(aisle); setTempPoints([newPoint]); } - } else if (aisleType === 'stripped-aisle') { - - if (tempPoints.length === 0) { - setTempPoints([newPoint]); - setIsCreating(true); - } else { - const aisle: Aisle = { - aisleUuid: THREE.MathUtils.generateUUID(), - points: [tempPoints[0], newPoint], - type: { - aisleType: 'stripped-aisle', - aisleColor: aisleColor, - aisleWidth: aisleWidth - } - }; - addAisle(aisle); - setTempPoints([newPoint]); - } } else if (aisleType === 'dotted-aisle') { if (tempPoints.length === 0) { @@ -194,7 +176,24 @@ function AisleCreator() { setTempPoints([newPoint]); } } else if (aisleType === 'arc-aisle') { - console.log('Creating arc-aisle'); + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + aisleUuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + aisleType: 'arc-aisle', + aisleColor: aisleColor, + aisleWidth: aisleWidth, + isFlipped: isFlipped + } + }; + addAisle(aisle); + setTempPoints([newPoint]); + } } else if (aisleType === 'circle-aisle') { if (tempPoints.length === 0) { diff --git a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx index 5bb18fd..5f40ca4 100644 --- a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx +++ b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx @@ -58,7 +58,7 @@ function ReferenceAisle({ tempPoints }: Readonly) { if (!finalPosition.current) return; - if (aisleType === 'solid-aisle' || aisleType === 'stripped-aisle') { + if (aisleType === 'solid-aisle') { setTempAisle({ aisleUuid: 'temp-aisle', points: [ @@ -153,9 +153,7 @@ function ReferenceAisle({ tempPoints }: Readonly) { gapLength: gapLength } }); - } else if (aisleType === 'arc-aisle') { - console.log(); - } else if (aisleType === 'junction-aisle') { + } else if (aisleType === 'junction-aisle' || aisleType === 'arc-aisle') { setTempAisle({ aisleUuid: 'temp-aisle', points: [ @@ -203,6 +201,8 @@ function ReferenceAisle({ tempPoints }: Readonly) { return ; case 'junction-aisle': return ; + case 'arc-aisle': + return default: return null; } @@ -211,7 +211,6 @@ function ReferenceAisle({ tempPoints }: Readonly) { const textPosition = new THREE.Vector3().addVectors(new THREE.Vector3(...tempAisle.points[0].position), new THREE.Vector3(...tempAisle.points[1].position)).divideScalar(2); const distance = new THREE.Vector3(...tempAisle.points[0].position).distanceTo(new THREE.Vector3(...tempAisle.points[1].position)); - const rendertext = () => { return ( <> @@ -705,4 +704,83 @@ function JunctionAisle({ aisle }: { readonly aisle: Aisle }) { ))} ); +} + +function ArcAisle({ aisle }: { readonly aisle: Aisle }) { + const arc = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'arc-aisle') return null; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.5; + const isFlipped = aisle.type.isFlipped || false; + + const direction = new THREE.Vector3().subVectors(end, start); + const length = direction.length(); + direction.normalize(); + + const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + if (!isFlipped) perpendicular.negate(); + + const arcHeight = length * 0.25; + const midPoint = new THREE.Vector3().lerpVectors(start, end, 0.5); + const controlPoint = new THREE.Vector3().copy(midPoint).addScaledVector(perpendicular, arcHeight); + + const widthOffset = perpendicular.clone().multiplyScalar(width / 2); + + const p1 = new THREE.Vector3().copy(start).add(widthOffset); + const p2 = new THREE.Vector3().copy(end).add(widthOffset); + const p3 = new THREE.Vector3().copy(end).sub(widthOffset); + const p4 = new THREE.Vector3().copy(start).sub(widthOffset); + + const shape = new THREE.Shape(); + shape.moveTo(p1.x, p1.z); + + shape.quadraticCurveTo( + controlPoint.x + widthOffset.x, + controlPoint.z + widthOffset.z, + p2.x, p2.z + ); + + shape.lineTo(p3.x, p3.z); + + shape.quadraticCurveTo( + controlPoint.x - widthOffset.x, + controlPoint.z - widthOffset.z, + p4.x, p4.z + ); + + shape.lineTo(p1.x, p1.z); + + return { + shape, + position: new THREE.Vector3(0, 0, 0), + rotationY: 0 + }; + }, [aisle]); + + + if (!arc) return null; + + return ( + + + + + + ); } \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx index 32fcce7..05b43c6 100644 --- a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx +++ b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx @@ -33,8 +33,13 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => if (selectedAssets.length === 0) return []; if (isPerAsset) { - return selectedAssets.map((obj: any) => { - const box = new THREE.Box3().setFromObject(obj.clone()); + return selectedAssets.map((obj: THREE.Object3D) => { + const position = obj.position; + const rotation = obj.getWorldQuaternion(new THREE.Quaternion()); + const clone = obj.clone(); + clone.position.set(0, 0, 0); + clone.rotation.set(0, 0, 0); + const box = new THREE.Box3().setFromObject(clone); const size = new THREE.Vector3(); const center = new THREE.Vector3(); box.getSize(size); @@ -46,7 +51,8 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => return { points: getBoxLines(min, max), - position: center.toArray(), + position: [position.x, center.y, position.z], + rotation: rotation.toArray(), size: size.toArray(), }; }); @@ -66,6 +72,7 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => { points: getBoxLines(min, max), position: center.toArray(), + rotation: [0, 0, 0, 1], size: size.toArray(), }, ]; @@ -75,7 +82,10 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => return ( <> {boxes.map((box: any, index: number) => ( - + color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} segments + position={[box.position[0], 0, box.position[2]]} + quaternion={new THREE.Quaternion(...box.rotation)} /> diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 0c158a9..d0954e4 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -309,7 +309,7 @@ const SelectionControls: React.FC = () => { <> - + diff --git a/app/src/store/builder/useAisleStore.ts b/app/src/store/builder/useAisleStore.ts index f379e35..bf05851 100644 --- a/app/src/store/builder/useAisleStore.ts +++ b/app/src/store/builder/useAisleStore.ts @@ -18,7 +18,6 @@ interface AisleStore { aisleUuid: string, props: { aisleWidth?: number; dashLength?: number; gapLength?: number } ) => void; - setStrippedAisleWidth: (aisleUuid: string, width: number) => void; setDottedAisleProperties: ( aisleUuid: string, props: { dotRadius?: number; gapLength?: number } @@ -28,7 +27,7 @@ interface AisleStore { aisleUuid: string, props: { aisleWidth?: number; aisleLength?: number; gapLength?: number } ) => void; - setArcAisleWidth: (aisleUuid: string, width: number) => void; + setArcAisleWidth: (aisleUuid: string, props: { aisleWidth?: number; isFlipped: boolean; }) => void; setCircleAisleWidth: (aisleUuid: string, width: number) => void; setJunctionAisleProperties: (aisleUuid: string, props: { aisleWidth?: number; isFlipped: boolean; }) => void; @@ -73,7 +72,6 @@ export const useAisleStore = create()( return true; }); }); - console.log('removedAisles: ', removedAisles); return removedAisles; }, @@ -119,13 +117,6 @@ export const useAisleStore = create()( } }), - setStrippedAisleWidth: (aisleUuid, width) => set((state) => { - const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid); - if (aisle && aisle.type.aisleType === 'stripped-aisle') { - aisle.type.aisleWidth = width; - } - }), - setDottedAisleProperties: (aisleUuid, props) => set((state) => { const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === 'dotted-aisle') { @@ -150,10 +141,11 @@ export const useAisleStore = create()( } }), - setArcAisleWidth: (aisleUuid, width) => set((state) => { + setArcAisleWidth: (aisleUuid, props) => set((state) => { const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid); if (aisle && aisle.type.aisleType === 'arc-aisle') { - aisle.type.aisleWidth = width; + if (props.aisleWidth !== undefined) aisle.type.aisleWidth = props.aisleWidth; + if (props.isFlipped !== undefined) aisle.type.isFlipped = props.isFlipped; } }), diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index c43805c..c796069 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -59,12 +59,6 @@ interface DashedAisle { gapLength: number; } -interface StrippedAisle { - aisleType: 'stripped-aisle'; - aisleColor: AisleColors; - aisleWidth: number; -} - interface DottedAisle { aisleType: 'dotted-aisle'; aisleColor: AisleColors; @@ -90,6 +84,7 @@ interface ArcAisle { aisleType: 'arc-aisle'; aisleColor: AisleColors; aisleWidth: number; + isFlipped: boolean; } interface CircleAisle { @@ -105,7 +100,7 @@ interface JunctionAisle { isFlipped: boolean; } -type AisleType = SolidAisle | DashedAisle | StrippedAisle | DottedAisle | ArrowAisle | ArrowsAisle | ArcAisle | CircleAisle | JunctionAisle; +type AisleType = SolidAisle | DashedAisle | DottedAisle | ArrowAisle | ArrowsAisle | ArcAisle | CircleAisle | JunctionAisle; interface Aisle { aisleUuid: string;