refactor: Update aisle types and properties, integrate arc-aisle handling
This commit is contained in:
parent
da741ed6df
commit
cba9edd7c4
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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) {
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
|
@ -706,3 +705,82 @@ 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>
|
||||
);
|
||||
}
|
|
@ -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 />
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}),
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue