refactor: Update aisle types and properties, integrate arc-aisle handling

This commit is contained in:
Jerald-Golden-B 2025-06-03 14:45:17 +05:30
parent da741ed6df
commit cba9edd7c4
9 changed files with 235 additions and 50 deletions

View File

@ -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;
}

View File

@ -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' && (
<JunctionAisle aisle={aisle} />
)}
{aisle.type.aisleType === 'arc-aisle' && (
<ArcAisle aisle={aisle} />
)}
</>
);
}

View File

@ -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<THREE.Group>(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 (
<group
name='Arc-Aisle'
ref={aisleRef}
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
userData={aisle}
onClick={handleClick}
onPointerMissed={() => {
setSelectedAisle(null);
}}
>
<Extrude
args={[arc.shape, {
depth: 0.01,
bevelEnabled: false,
curveSegments: 32
}]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
);
}
export default ArcAisle;

View File

@ -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) {

View File

@ -58,7 +58,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
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<ReferenceAisleProps>) {
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<ReferenceAisleProps>) {
return <CircleAisle aisle={tempAisle} />;
case 'junction-aisle':
return <JunctionAisle aisle={tempAisle} />;
case 'arc-aisle':
return <ArcAisle aisle={tempAisle} />
default:
return null;
}
@ -211,7 +211,6 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
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 }) {
))}
</group>
);
}
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 (
<group
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
>
<Extrude
args={[arc.shape, {
depth: 0.01,
bevelEnabled: false,
curveSegments: 32
}]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
);
}

View File

@ -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) => (
<group key={index} name="SelectionGroupBoundingBoxLine">
<group
key={index}
name="SelectionGroupBoundingBoxLine"
>
<Line
name="SelectionGroupBoundingBox"
depthWrite={false}
@ -83,12 +93,15 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) =>
color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"}
lineWidth={2.7}
segments
position={[box.position[0], 0, box.position[2]]}
quaternion={new THREE.Quaternion(...box.rotation)}
/>
<mesh
name="SelectionGroupBoundingLine"
ref={index === 0 ? boundingBoxRef : null}
visible={false}
position={box.position}
quaternion={new THREE.Quaternion(...box.rotation)}
>
<boxGeometry args={box.size} />
<meshBasicMaterial />

View File

@ -309,7 +309,7 @@ const SelectionControls: React.FC = () => {
<>
<group name="SelectionGroup">
<group ref={selectionGroup} name="selectionAssetGroup">
<BoundingBox boundingBoxRef={boundingBoxRef} />
<BoundingBox boundingBoxRef={boundingBoxRef} isPerAsset />
</group>
</group>

View File

@ -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<AisleStore>()(
return true;
});
});
console.log('removedAisles: ', removedAisles);
return removedAisles;
},
@ -119,13 +117,6 @@ export const useAisleStore = create<AisleStore>()(
}
}),
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<AisleStore>()(
}
}),
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;
}
}),

View File

@ -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;