1. Integerated DashBoard, #97

Merged
Marikannan merged 31 commits from v3-wall into main 2025-06-04 12:09:09 +00:00
19 changed files with 1424 additions and 1107 deletions
Showing only changes of commit da741ed6df - Show all commits

View File

@ -12,6 +12,7 @@ import Directional from "../../../../assets/image/aisleTypes/Directional.png";
import Dotted from "../../../../assets/image/aisleTypes/Dotted.png";
import Solid from "../../../../assets/image/aisleTypes/Solid.png";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import InputToggle from "../../../ui/inputs/InputToggle";
interface TextureItem {
color: AisleColors;
@ -24,7 +25,7 @@ const AisleProperties: React.FC = () => {
const [collapsePresets, setCollapsePresets] = useState(false);
const [collapseTexture, setCollapseTexture] = useState(true);
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength } = useBuilderStore();
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength, setIsFlipped } = useBuilderStore();
const aisleTextureList: TextureItem[] = [
{ color: "yellow", id: "yellow1", brief: "pedestrian walkways", texture: "" },
@ -90,6 +91,10 @@ const AisleProperties: React.FC = () => {
}
};
const handleIsFlippedChange = () => {
setIsFlipped(!aisleIsFlipped)
};
const dashLengthValue = useMemo(() => {
return dashLength.toString();
}, [aisleType, dashLength]);
@ -110,6 +115,10 @@ const AisleProperties: React.FC = () => {
return aisleLength.toString();
}, [aisleType, aisleLength]);
const aisleIsFlipped = useMemo(() => {
return isFlipped;
}, [aisleType, isFlipped]);
const renderAdvancedProperties = () => {
switch (aisleType) {
case 'dashed-aisle':
@ -185,6 +194,19 @@ const AisleProperties: React.FC = () => {
}
</>
);
case 'junction-aisle':
return (
<>
{aisleType &&
<InputToggle
inputKey="Flip Ailse"
label="Flip Aisle"
value={aisleIsFlipped}
onClick={handleIsFlippedChange}
/>
}
</>
)
default:
return null;
}

View File

@ -16,8 +16,8 @@ function AisleInstances() {
aisles.forEach(aisle => {
aisle.points.forEach(point => {
if (!seenUuids.has(point.uuid)) {
seenUuids.add(point.uuid);
if (!seenUuids.has(point.pointUuid)) {
seenUuids.add(point.pointUuid);
points.push(point);
}
});
@ -27,7 +27,7 @@ function AisleInstances() {
}, [aisles]);
useEffect(() => {
console.log('aisles: ', aisles);
// console.log('aisles: ', aisles);
}, [aisles]);
return (
@ -35,7 +35,7 @@ function AisleInstances() {
{toggleView &&
<group name='Aisle-Points-Group'>
{allPoints.map((point) => (
<Point key={point.uuid} point={point} />
<Point key={point.pointUuid} point={point} />
))}
</group>
}
@ -46,13 +46,13 @@ function AisleInstances() {
const distance = new Vector3(...aisle.points[0].position).distanceTo(new Vector3(...aisle.points[1].position));
return (
< React.Fragment key={aisle.uuid}>
<AisleInstance aisle={aisle} key={aisle.uuid} />
< React.Fragment key={aisle.aisleUuid}>
<AisleInstance aisle={aisle} key={aisle.aisleUuid} />
{toggleView &&
<Html
// data
key={`${aisle.points[0].uuid}_${aisle.points[1].uuid}`}
key={`${aisle.points[0].pointUuid}_${aisle.points[1].pointUuid}`}
userData={aisle}
position={[textPosition.x, 1, textPosition.z]}
// class
@ -64,8 +64,8 @@ function AisleInstances() {
sprite
>
<div
key={aisle.uuid}
className={`distance ${aisle.uuid}`}
key={aisle.aisleUuid}
className={`distance ${aisle.aisleUuid}`}
>
{distance.toFixed(2)} m
</div>

View File

@ -1,6 +1,9 @@
import ArrowAisle from './aisleTypes/arrowAisle';
import ArrowsAisle from './aisleTypes/arrowsAisle';
import CircleAisle from './aisleTypes/circleAisle';
import DashedAisle from './aisleTypes/dashedAisle';
import DottedAisle from './aisleTypes/dottedAisle';
import JunctionAisle from './aisleTypes/junctionAisle';
import SolidAisle from './aisleTypes/solidAisle';
function AisleInstance({ aisle }: { readonly aisle: Aisle }) {
@ -22,6 +25,18 @@ function AisleInstance({ aisle }: { readonly aisle: Aisle }) {
{aisle.type.aisleType === 'arrows-aisle' && (
<ArrowsAisle aisle={aisle} />
)}
{aisle.type.aisleType === 'arrow-aisle' && (
<ArrowAisle aisle={aisle} />
)}
{aisle.type.aisleType === 'circle-aisle' && (
<CircleAisle aisle={aisle} />
)}
{aisle.type.aisleType === 'junction-aisle' && (
<JunctionAisle aisle={aisle} />
)}
</>
);
}

View File

@ -0,0 +1,88 @@
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 ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
const aisleRef = useRef<THREE.Group>(null);
const { toolMode } = useToolMode();
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
const arrow = useMemo(() => {
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrow-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.1;
const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length();
direction.normalize();
const shape = new THREE.Shape();
const arrowHeadLength = width * 2;
const shaftLength = length - arrowHeadLength;
if (shaftLength > 0) {
shape.moveTo(0, 0);
shape.lineTo(width, -arrowHeadLength);
shape.lineTo(width / 2, -arrowHeadLength);
shape.lineTo(width / 2, -length);
shape.lineTo(-width / 2, -length);
shape.lineTo(-width / 2, -arrowHeadLength);
shape.lineTo(-width, -arrowHeadLength);
shape.lineTo(0, 0);
} else {
shape.moveTo(0, 0);
shape.lineTo(width, -length);
shape.lineTo(-width, -length);
shape.lineTo(0, 0);
}
shape.closePath();
const position = end;
const angle = Math.atan2(direction.x, direction.z);
return { shape, position, rotationY: angle };
}, [aisle]);
const handleClick = () => {
if (toolMode === 'move' && !hoveredPoint) {
setSelectedAisle(aisleRef.current);
}
}
if (!arrow) return null;
return (
<group
name='Arrow-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);
}}
>
<group position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
<Extrude
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
</group>
);
}
export default ArrowAisle;

View File

@ -0,0 +1,82 @@
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 CircleAisle({ aisle }: { readonly aisle: Aisle }) {
const aisleRef = useRef<THREE.Group>(null);
const { toolMode } = useToolMode();
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
const circle = useMemo(() => {
if (aisle.points.length < 2 || aisle.type.aisleType !== 'circle-aisle') return null;
const center = new THREE.Vector3(...aisle.points[0].position);
const widthCenter = new THREE.Vector3(...aisle.points[1].position);
const width = aisle.type.aisleWidth || 0.1;
const middleRadius = center.distanceTo(widthCenter);
const innerRadius = Math.max(0, middleRadius - width / 2);
const outerRadius = middleRadius + width / 2;
const shape = new THREE.Shape();
shape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);
if (innerRadius > 0) {
const hole = new THREE.Path();
hole.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);
shape.holes.push(hole);
}
return {
shape,
position: center,
rotationY: 0
};
}, [aisle]);
const handleClick = () => {
if (toolMode === 'move' && !hoveredPoint) {
setSelectedAisle(aisleRef.current);
}
}
if (!circle) return null;
return (
<group
name='Circle-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);
}}
>
<group position={[circle.position.x, circle.position.z, 0]}>
<Extrude
args={[circle.shape, {
depth: 0.01,
bevelEnabled: false,
steps: 1,
curveSegments: 64
}]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
</group>
);
}
export default CircleAisle;

View File

@ -0,0 +1,125 @@
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 JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
const aisleRef = useRef<THREE.Group>(null);
const { toolMode } = useToolMode();
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
const arrows = useMemo(() => {
if (aisle.points.length < 2 || aisle.type.aisleType !== 'junction-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.1;
const isFlipped = aisle.type.isFlipped || false;
const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length();
direction.normalize();
const mainShape = new THREE.Shape();
const arrowHeadLength = width * 2;
const shaftLength = length - arrowHeadLength;
if (shaftLength > 0) {
mainShape.moveTo(0, 0);
mainShape.lineTo(width, -arrowHeadLength);
mainShape.lineTo(width / 2, -arrowHeadLength);
mainShape.lineTo(width / 2, -length);
mainShape.lineTo(-width / 2, -length);
mainShape.lineTo(-width / 2, -arrowHeadLength);
mainShape.lineTo(-width, -arrowHeadLength);
mainShape.lineTo(0, 0);
} else {
mainShape.moveTo(0, 0);
mainShape.lineTo(width, -length);
mainShape.lineTo(-width, -length);
mainShape.lineTo(0, 0);
}
mainShape.closePath();
const secondaryLength = length / 4;
const secondaryShape = new THREE.Shape();
const secondaryHeadLength = width * 2;
const secondaryShaftLength = secondaryLength - secondaryHeadLength;
if (secondaryShaftLength > 0) {
secondaryShape.moveTo(0, 0);
secondaryShape.lineTo(width / 2, 0);
secondaryShape.lineTo(width / 2, secondaryShaftLength);
secondaryShape.lineTo(width, secondaryShaftLength);
secondaryShape.lineTo(0, secondaryLength);
secondaryShape.lineTo(-width, secondaryShaftLength);
secondaryShape.lineTo(-width / 2, secondaryShaftLength);
secondaryShape.lineTo(-width / 2, 0);
secondaryShape.lineTo(0, 0);
} else {
secondaryShape.moveTo(0, 0);
secondaryShape.lineTo(width, 0);
secondaryShape.lineTo(0, secondaryLength);
secondaryShape.lineTo(-width, 0);
secondaryShape.lineTo(0, 0);
}
secondaryShape.closePath();
const mainPosition = end;
const mainAngle = Math.atan2(direction.x, direction.z);
const perpendicularDirection = isFlipped
? new THREE.Vector3(direction.z, 0, -direction.x).normalize()
: new THREE.Vector3(-direction.z, 0, direction.x).normalize();
const secondaryAngle = Math.atan2(perpendicularDirection.x, perpendicularDirection.z);
const secondaryPosition = new THREE.Vector3().lerpVectors(start, end, 0.75);
return [
{ shape: mainShape, position: mainPosition, rotationY: mainAngle },
{ shape: secondaryShape, position: secondaryPosition, rotationY: secondaryAngle + Math.PI }
];
}, [aisle]);
const handleClick = () => {
if (toolMode === 'move' && !hoveredPoint) {
setSelectedAisle(aisleRef.current);
}
}
if (!arrows) return null;
return (
<group
name='Junction-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);
}}
>
{arrows.map((arrow, index) => (
<group key={index} position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
<Extrude
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
))}
</group>
);
}
export default JunctionAisle;

View File

@ -1,5 +1,5 @@
import * as THREE from 'three'
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useAisleStore } from '../../../../store/builder/useAisleStore';
@ -15,45 +15,37 @@ function AisleCreator() {
const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore();
const { addAisle, getAislePointById } = useAisleStore();
const drag = useRef(false);
const isLeftMouseDown = useRef(false);
const [tempPoints, setTempPoints] = useState<Point[]>([]);
const [isCreating, setIsCreating] = useState(false);
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint } = useBuilderStore();
// useEffect(() => {
// if (tempPoints.length > 0) {
// setTempPoints([]);
// setIsCreating(false);
// }
// }, [aisleType]);
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, snappedPosition, snappedPoint } = useBuilderStore();
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
let isLeftMouseDown = false;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
isLeftMouseDown.current = true;
drag.current = false;
}
};
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
isLeftMouseDown.current = false;
}
};
const onMouseMove = () => {
if (isLeftMouseDown) {
drag = true;
drag.current = true;
}
};
const onMouseClick = () => {
if (drag || !toggleView) return;
if (drag.current || !toggleView) return;
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
@ -63,14 +55,14 @@ function AisleCreator() {
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point');
const newPoint: Point = {
uuid: THREE.MathUtils.generateUUID(),
pointUuid: THREE.MathUtils.generateUUID(),
pointType: 'Aisle',
position: [position.x, position.y, position.z],
layer: activeLayer
};
if (snappedPosition && snappedPoint) {
newPoint.uuid = snappedPoint.uuid;
newPoint.pointUuid = snappedPoint.pointUuid;
newPoint.position = snappedPosition;
newPoint.layer = snappedPoint.layer;
}
@ -82,7 +74,7 @@ function AisleCreator() {
if (intersects && !snappedPoint) {
const point = getAislePointById(intersects.object.uuid);
if (point) {
newPoint.uuid = point.uuid;
newPoint.pointUuid = point.pointUuid;
newPoint.position = point.position;
newPoint.layer = point.layer;
}
@ -95,7 +87,7 @@ function AisleCreator() {
setIsCreating(true);
} else {
const aisle: Aisle = {
uuid: THREE.MathUtils.generateUUID(),
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'solid-aisle',
@ -113,7 +105,7 @@ function AisleCreator() {
setIsCreating(true);
} else {
const aisle: Aisle = {
uuid: THREE.MathUtils.generateUUID(),
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'dashed-aisle',
@ -133,7 +125,7 @@ function AisleCreator() {
setIsCreating(true);
} else {
const aisle: Aisle = {
uuid: THREE.MathUtils.generateUUID(),
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'stripped-aisle',
@ -151,7 +143,7 @@ function AisleCreator() {
setIsCreating(true);
} else {
const aisle: Aisle = {
uuid: THREE.MathUtils.generateUUID(),
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'dotted-aisle',
@ -164,7 +156,23 @@ function AisleCreator() {
setTempPoints([newPoint]);
}
} else if (aisleType === 'arrow-aisle') {
console.log('Creating arrow-aisle');
if (tempPoints.length === 0) {
setTempPoints([newPoint]);
setIsCreating(true);
} else {
const aisle: Aisle = {
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'arrow-aisle',
aisleColor: aisleColor,
aisleWidth: aisleWidth
}
};
addAisle(aisle);
setTempPoints([newPoint]);
}
} else if (aisleType === 'arrows-aisle') {
if (tempPoints.length === 0) {
@ -172,7 +180,7 @@ function AisleCreator() {
setIsCreating(true);
} else {
const aisle: Aisle = {
uuid: THREE.MathUtils.generateUUID(),
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'arrows-aisle',
@ -188,9 +196,42 @@ function AisleCreator() {
} else if (aisleType === 'arc-aisle') {
console.log('Creating arc-aisle');
} else if (aisleType === 'circle-aisle') {
console.log('Creating circle-aisle');
if (tempPoints.length === 0) {
setTempPoints([newPoint]);
setIsCreating(true);
} else {
const aisle: Aisle = {
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'circle-aisle',
aisleColor: aisleColor,
aisleWidth: aisleWidth
}
};
addAisle(aisle);
setTempPoints([newPoint]);
}
} else if (aisleType === 'junction-aisle') {
console.log('Creating junction-aisle');
if (tempPoints.length === 0) {
setTempPoints([newPoint]);
setIsCreating(true);
} else {
const aisle: Aisle = {
aisleUuid: THREE.MathUtils.generateUUID(),
points: [tempPoints[0], newPoint],
type: {
aisleType: 'junction-aisle',
aisleColor: aisleColor,
aisleWidth: aisleWidth,
isFlipped: isFlipped
}
};
addAisle(aisle);
setTempPoints([newPoint]);
}
}
};
@ -211,6 +252,11 @@ function AisleCreator() {
} else {
setTempPoints([]);
setIsCreating(false);
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext);
}
return () => {
@ -228,7 +274,7 @@ function AisleCreator() {
<>
<group name='Aisle-Reference-Points-Group'>
{tempPoints.map((point) => (
<ReferencePoint key={point.uuid} point={point} />
<ReferencePoint key={point.pointUuid} point={point} />
))}
</group>

View File

@ -13,7 +13,7 @@ interface ReferenceAisleProps {
}
function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setSnappedPosition, setSnappedPoint } = useBuilderStore();
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setSnappedPosition, setSnappedPoint } = useBuilderStore();
const { pointer, raycaster, camera } = useThree();
const { toolMode } = useToolMode();
const { toggleView } = useToggleView();
@ -24,7 +24,6 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
const [tempAisle, setTempAisle] = useState<Aisle | null>(null);
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
// Calculate directional snap based on current and previous points
const directionalSnap = useDirectionalSnapping(
currentPosition,
tempPoints[0]?.position || null
@ -61,11 +60,11 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
if (aisleType === 'solid-aisle' || aisleType === 'stripped-aisle') {
setTempAisle({
uuid: 'temp-aisle',
aisleUuid: 'temp-aisle',
points: [
tempPoints[0],
{
uuid: 'temp-point',
pointUuid: 'temp-point',
pointType: 'Aisle',
position: finalPosition.current,
layer: activeLayer
@ -79,11 +78,11 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
});
} else if (aisleType === 'dashed-aisle') {
setTempAisle({
uuid: 'temp-aisle',
aisleUuid: 'temp-aisle',
points: [
tempPoints[0],
{
uuid: 'temp-point',
pointUuid: 'temp-point',
pointType: 'Aisle',
position: finalPosition.current,
layer: activeLayer
@ -99,11 +98,11 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
});
} else if (aisleType === 'dotted-aisle') {
setTempAisle({
uuid: 'temp-aisle',
aisleUuid: 'temp-aisle',
points: [
tempPoints[0],
{
uuid: 'temp-point',
pointUuid: 'temp-point',
pointType: 'Aisle',
position: finalPosition.current,
layer: activeLayer
@ -116,15 +115,31 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
gapLength: gapLength
}
});
} else if (aisleType === 'arrow-aisle') {
console.log();
} else if (aisleType === 'arrows-aisle') {
} else if (aisleType === 'arrow-aisle' || aisleType === 'circle-aisle') {
setTempAisle({
uuid: 'temp-aisle',
aisleUuid: 'temp-aisle',
points: [
tempPoints[0],
{
uuid: 'temp-point',
pointUuid: 'temp-point',
pointType: 'Aisle',
position: finalPosition.current,
layer: activeLayer
}
],
type: {
aisleType: aisleType,
aisleColor: aisleColor,
aisleWidth: aisleWidth,
}
});
} else if (aisleType === 'arrows-aisle') {
setTempAisle({
aisleUuid: 'temp-aisle',
points: [
tempPoints[0],
{
pointUuid: 'temp-point',
pointType: 'Aisle',
position: finalPosition.current,
layer: activeLayer
@ -140,10 +155,25 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
});
} else if (aisleType === 'arc-aisle') {
console.log();
} else if (aisleType === 'circle-aisle') {
console.log();
} else if (aisleType === 'junction-aisle') {
console.log();
setTempAisle({
aisleUuid: 'temp-aisle',
points: [
tempPoints[0],
{
pointUuid: 'temp-point',
pointType: 'Aisle',
position: finalPosition.current,
layer: activeLayer
}
],
type: {
aisleType: aisleType,
aisleColor: aisleColor,
aisleWidth: aisleWidth,
isFlipped: isFlipped,
}
});
}
}
} else if (tempAisle !== null) {
@ -165,8 +195,14 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
return <DashedAisle aisle={tempAisle} />;
case 'dotted-aisle':
return <DottedAisle aisle={tempAisle} />;
case 'arrow-aisle':
return <ArrowAisle aisle={tempAisle} />;
case 'arrows-aisle':
return <ArrowsAisle aisle={tempAisle} />
return <ArrowsAisle aisle={tempAisle} />;
case 'circle-aisle':
return <CircleAisle aisle={tempAisle} />;
case 'junction-aisle':
return <JunctionAisle aisle={tempAisle} />;
default:
return null;
}
@ -182,7 +218,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
{toggleView &&
<Html
// data
key={tempAisle.uuid}
key={tempAisle.aisleUuid}
userData={tempAisle}
position={[textPosition.x, 1, textPosition.z]}
// class
@ -194,8 +230,8 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
sprite
>
<div
key={tempAisle.uuid}
className={`distance ${tempAisle.uuid}`}
key={tempAisle.aisleUuid}
className={`distance ${tempAisle.aisleUuid}`}
>
{distance.toFixed(2)} m
</div>
@ -446,3 +482,227 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
</group>
);
}
function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
const arrow = useMemo(() => {
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrow-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.1;
const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length();
direction.normalize();
const shape = new THREE.Shape();
const arrowHeadLength = width * 2;
const shaftLength = length - arrowHeadLength;
if (shaftLength > 0) {
shape.moveTo(0, 0);
shape.lineTo(width, -arrowHeadLength);
shape.lineTo(width / 2, -arrowHeadLength);
shape.lineTo(width / 2, -length);
shape.lineTo(-width / 2, -length);
shape.lineTo(-width / 2, -arrowHeadLength);
shape.lineTo(-width, -arrowHeadLength);
shape.lineTo(0, 0);
} else {
shape.moveTo(0, 0);
shape.lineTo(width, -length);
shape.lineTo(-width, -length);
shape.lineTo(0, 0);
}
shape.closePath();
const position = end;
const angle = Math.atan2(direction.x, direction.z);
return { shape, position, rotationY: angle };
}, [aisle]);
if (!arrow) return null;
return (
<group
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
>
<group position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
<Extrude
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
</group>
);
}
function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
const circle = useMemo(() => {
if (aisle.points.length < 2 || aisle.type.aisleType !== 'circle-aisle') return null;
const center = new THREE.Vector3(...aisle.points[0].position);
const widthCenter = new THREE.Vector3(...aisle.points[1].position);
const width = aisle.type.aisleWidth || 0.1;
const middleRadius = center.distanceTo(widthCenter);
const innerRadius = Math.max(0, middleRadius - width / 2);
const outerRadius = middleRadius + width / 2;
const shape = new THREE.Shape();
shape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);
if (innerRadius > 0) {
const hole = new THREE.Path();
hole.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);
shape.holes.push(hole);
}
return {
shape,
position: center,
rotationY: 0
};
}, [aisle]);
if (!circle) return null;
return (
<group
position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]}
rotation={[Math.PI / 2, 0, 0]}
>
<group position={[circle.position.x, circle.position.z, 0]}>
<Extrude
args={[circle.shape, {
depth: 0.01,
bevelEnabled: false,
steps: 1,
curveSegments: 64
}]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
</group>
);
}
function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
const arrows = useMemo(() => {
if (aisle.points.length < 2 || aisle.type.aisleType !== 'junction-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.1;
const isFlipped = aisle.type.isFlipped || false;
const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length();
direction.normalize();
const mainShape = new THREE.Shape();
const arrowHeadLength = width * 2;
const shaftLength = length - arrowHeadLength;
if (shaftLength > 0) {
mainShape.moveTo(0, 0);
mainShape.lineTo(width, -arrowHeadLength);
mainShape.lineTo(width / 2, -arrowHeadLength);
mainShape.lineTo(width / 2, -length);
mainShape.lineTo(-width / 2, -length);
mainShape.lineTo(-width / 2, -arrowHeadLength);
mainShape.lineTo(-width, -arrowHeadLength);
mainShape.lineTo(0, 0);
} else {
mainShape.moveTo(0, 0);
mainShape.lineTo(width, -length);
mainShape.lineTo(-width, -length);
mainShape.lineTo(0, 0);
}
mainShape.closePath();
const secondaryLength = length / 4;
const secondaryShape = new THREE.Shape();
const secondaryHeadLength = width * 2;
const secondaryShaftLength = secondaryLength - secondaryHeadLength;
if (secondaryShaftLength > 0) {
secondaryShape.moveTo(0, 0);
secondaryShape.lineTo(width / 2, 0);
secondaryShape.lineTo(width / 2, secondaryShaftLength);
secondaryShape.lineTo(width, secondaryShaftLength);
secondaryShape.lineTo(0, secondaryLength);
secondaryShape.lineTo(-width, secondaryShaftLength);
secondaryShape.lineTo(-width / 2, secondaryShaftLength);
secondaryShape.lineTo(-width / 2, 0);
secondaryShape.lineTo(0, 0);
} else {
secondaryShape.moveTo(0, 0);
secondaryShape.lineTo(width, 0);
secondaryShape.lineTo(0, secondaryLength);
secondaryShape.lineTo(-width, 0);
secondaryShape.lineTo(0, 0);
}
secondaryShape.closePath();
const mainPosition = end;
const mainAngle = Math.atan2(direction.x, direction.z);
const perpendicularDirection = isFlipped
? new THREE.Vector3(direction.z, 0, -direction.x).normalize()
: new THREE.Vector3(-direction.z, 0, direction.x).normalize();
const secondaryAngle = Math.atan2(perpendicularDirection.x, perpendicularDirection.z);
const secondaryPosition = new THREE.Vector3().lerpVectors(start, end, 0.75);
return [
{ shape: mainShape, position: mainPosition, rotationY: mainAngle },
{
shape: secondaryShape,
position: secondaryPosition,
rotationY: secondaryAngle + Math.PI
}
];
}, [aisle]);
if (!arrows) 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((arrow, index) => (
<group key={index} position={[arrow.position.x, arrow.position.z, 0]} rotation={[0, 0, -arrow.rotationY]}>
<Extrude
args={[arrow.shape, { depth: 0.01, bevelEnabled: false }]}
receiveShadow
castShadow
>
<meshStandardMaterial
color={aisle.type.aisleColor || '#ffffff'}
side={THREE.DoubleSide}
/>
</Extrude>
</group>
))}
</group>
);
}

View File

@ -35,7 +35,6 @@ import * as Types from "../../types/world/worldTypes";
import SocketResponses from "../collaboration/socket/socketResponses.dev";
import FloorPlanGroup from "./groups/floorPlanGroup";
import FloorGroup from "./groups/floorGroup";
import FloorGroupAilse from "./groups/floorGroupAisle";
import Draw from "./functions/draw";
import WallsAndWallItems from "./groups/wallsAndWallItems";
import Ground from "../scene/environment/ground";
@ -275,26 +274,6 @@ export default function Builder() {
<ZoneGroup />
{/* <FloorGroupAilse
floorGroupAisle={floorGroupAisle}
plane={plane}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
line={line}
lines={lines}
currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls}
floorPlanGroup={floorPlanGroup}
ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated}
isSnapped={isSnapped}
ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint}
/> */}
<AssetsGroup
floorGroup={floorGroup}
plane={plane}

View File

@ -1,56 +0,0 @@
import * as THREE from 'three';
import * as Types from '../../../../types/world/worldTypes';
import * as CONSTANTS from '../../../../types/world/worldConstants';
export default async function addAisleToScene(
aisle: Types.Line,
floorGroupAisle: Types.RefGroup,
): Promise<void> {
if (aisle.length >= 2 && aisle[0] && aisle[1]) {
const start: Types.Vector3 = aisle[0][0];
const end: Types.Vector3 = aisle[1][0];
const direction = new THREE.Vector3(
end.x - start.x,
end.y - start.y,
end.z - start.z
).normalize();
const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
const offsetDistance = CONSTANTS.aisleConfig.width;
const leftStart = new THREE.Vector3().copy(start).addScaledVector(perp, offsetDistance);
const rightStart = new THREE.Vector3().copy(start).addScaledVector(perp, -offsetDistance);
const leftEnd = new THREE.Vector3().copy(end).addScaledVector(perp, offsetDistance);
const rightEnd = new THREE.Vector3().copy(end).addScaledVector(perp, -offsetDistance);
const stripShape = new THREE.Shape();
stripShape.moveTo(leftStart.x, leftStart.z);
stripShape.lineTo(leftEnd.x, leftEnd.z);
stripShape.lineTo(rightEnd.x, rightEnd.z);
stripShape.lineTo(rightStart.x, rightStart.z);
stripShape.lineTo(leftStart.x, leftStart.z);
const extrudeSettings = {
depth: CONSTANTS.aisleConfig.height,
bevelEnabled: false,
};
const stripGeometry = new THREE.ExtrudeGeometry(stripShape, extrudeSettings);
const stripMaterial = new THREE.MeshStandardMaterial({
color: CONSTANTS.aisleConfig.defaultColor,
polygonOffset: true,
polygonOffsetFactor: -1,
polygonOffsetUnits: -1,
});
const stripMesh = new THREE.Mesh(stripGeometry, stripMaterial);
stripMesh.receiveShadow = true;
stripMesh.castShadow = true;
stripMesh.position.y = (aisle[0][2] - 1) * CONSTANTS.wallConfig.height + 0.01;
stripMesh.rotateX(Math.PI / 2);
floorGroupAisle.current.add(stripMesh);
}
}

View File

@ -1,19 +0,0 @@
import * as Types from '../../../../types/world/worldTypes';
import addAisleToScene from './addAilseToScene';
import * as CONSTANTS from '../../../../types/world/worldConstants';
export default async function loadAisles(
lines: Types.RefLines,
floorGroupAisle: Types.RefGroup
) {
// console.log('lines: ', lines.current[0][0][0]);
if (!floorGroupAisle.current) return
floorGroupAisle.current.children = [];
const aisles = lines.current.filter((line) => line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName);
if (aisles.length > 0) {
aisles.forEach((aisle: Types.Line) => {
addAisleToScene(aisle, floorGroupAisle)
})
}
}

View File

@ -1,242 +0,0 @@
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import * as CONSTANTS from '../../../types/world/worldConstants';
import { useThree } from "@react-three/fiber";
import { useToggleView, useActiveLayer, useSocketStore, useDeletePointOrLine, useUpdateScene, useNewLines, useToolMode } from "../../../store/builder/store";
import { useEffect } from "react";
import removeSoloPoint from "../geomentries/points/removeSoloPoint";
import removeReferenceLine from "../geomentries/lines/removeReferenceLine";
import getClosestIntersection from "../geomentries/lines/getClosestIntersection";
import addPointToScene from "../geomentries/points/addPointToScene";
import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject';
import addLineToScene from "../geomentries/lines/addLineToScene";
import loadAisles from '../geomentries/aisles/loadAisles';
const FloorGroupAilse = ({ floorGroupAisle, plane, floorPlanGroupLine, floorPlanGroupPoint, line, lines, currentLayerPoint, dragPointControls, floorPlanGroup, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
const { toggleView } = useToggleView();
const { setDeletePointOrLine } = useDeletePointOrLine();
const { toolMode } = useToolMode();
const { socket } = useSocketStore();
const { activeLayer } = useActiveLayer();
const { gl, raycaster } = useThree();
const { updateScene, setUpdateScene } = useUpdateScene();
const { setNewLines } = useNewLines();
useEffect(() => {
if (updateScene) {
loadAisles(lines, floorGroupAisle);
setUpdateScene(false);
}
}, [updateScene])
useEffect(() => {
if (toolMode === "Aisle") {
setDeletePointOrLine(false);
} else {
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
}, [toolMode]);
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
let isLeftMouseDown = false;
const onMouseDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = true;
drag = false;
}
};
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
}
}
const onMouseMove = () => {
if (isLeftMouseDown) {
drag = true;
}
};
const onContextMenu = (e: any) => {
e.preventDefault();
if (toolMode === "Aisle") {
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);
}
};
const onMouseClick = (evt: any) => {
if (!plane.current || drag) return;
const intersects = raycaster.intersectObject(plane.current, true);
let intersectionPoint = intersects[0].point;
const points = floorPlanGroupPoint.current?.children ?? [];
const intersectsPoint = raycaster.intersectObjects(points, true).find(intersect => intersect.object.visible);
let intersectsLines: any = raycaster.intersectObjects(floorPlanGroupLine.current.children, true);
if (intersectsLines.length > 0 && intersects && intersects.length > 0 && !intersectsPoint) {
const lineType = intersectsLines[0].object.userData.linePoints[0][3];
if (lineType === CONSTANTS.lineConfig.aisleName) {
// console.log("intersected a aisle line");
const ThroughPoint = (intersectsLines[0].object.geometry.parameters.path).getPoints(CONSTANTS.lineConfig.lineIntersectionPoints);
let intersection = getClosestIntersection(ThroughPoint, intersectionPoint);
if (!intersection) return;
const point = addPointToScene(intersection, CONSTANTS.pointConfig.aisleOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.aisleName);
(line.current as Types.Line).push([new THREE.Vector3(intersection.x, 0.01, intersection.z), point.uuid, activeLayer, CONSTANTS.lineConfig.aisleName,]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization: organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id
}
socket.emit('v1:Line:create', input);
setNewLines([line.current]);
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.pointConfig.aisleOuterColor, line.current, floorPlanGroupLine);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
}
}
} else if (intersectsPoint && intersects && intersects.length > 0) {
if (intersectsPoint.object.userData.type === CONSTANTS.lineConfig.aisleName) {
// console.log("intersected a aisle point");
intersectionPoint = intersectsPoint.object.position;
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), intersectsPoint.object.uuid, activeLayer, CONSTANTS.lineConfig.aisleName,]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization: organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id
}
socket.emit('v1:Line:create', input);
setNewLines([line.current]);
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.pointConfig.aisleOuterColor, line.current, floorPlanGroupLine);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
ispreSnapped.current = false;
isSnapped.current = false;
}
}
} else if (intersects && intersects.length > 0) {
// console.log("intersected a empty area");
let uuid: string = "";
if (isAngleSnapped.current && anglesnappedPoint.current && line.current.length > 0) {
intersectionPoint = anglesnappedPoint.current;
const point = addPointToScene(intersectionPoint, CONSTANTS.pointConfig.aisleOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.aisleName);
uuid = point.uuid;
} else if (isSnapped.current && snappedPoint.current && line.current.length > 0) {
intersectionPoint = snappedPoint.current;
uuid = isSnappedUUID.current!;
} else if (ispreSnapped.current && snappedPoint.current) {
intersectionPoint = snappedPoint.current;
uuid = isSnappedUUID.current!;
} else {
const point = addPointToScene(intersectionPoint, CONSTANTS.pointConfig.aisleOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.aisleName);
uuid = point.uuid;
}
(line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), uuid, activeLayer, CONSTANTS.lineConfig.aisleName,]);
if (line.current.length >= 2 && line.current[0] && line.current[1]) {
lines.current.push(line.current as Types.Line);
const data = arrayLineToObject(line.current as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
//REST
// setLine(organization, data.layer!, data.line!, data.type!);
//SOCKET
const input = {
organization: organization,
layer: data.layer,
line: data.line,
type: data.type,
socketId: socket.id
}
socket.emit('v1:Line:create', input);
setNewLines([line.current]);
addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.pointConfig.aisleOuterColor, line.current, floorPlanGroupLine);
let lastPoint = line.current[line.current.length - 1];
line.current = [lastPoint];
ispreSnapped.current = false;
isSnapped.current = false;
}
}
}
if (toolMode === 'Aisle') {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("click", onMouseClick);
canvasElement.addEventListener("contextmenu", onContextMenu);
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContextMenu);
};
}, [toolMode])
return (
<group ref={floorGroupAisle} visible={!toggleView} name="floorGroupAisle">
</group>
)
}
export default FloorGroupAilse;

View File

@ -91,6 +91,7 @@ const ZoneGroup: React.FC = () => {
layer: zone.layer,
}));
console.log('fetchedZones: ', fetchedZones);
setZones(fetchedZones);
const fetchedPoints = data.data.flatMap((zone: any) =>
@ -109,6 +110,7 @@ const ZoneGroup: React.FC = () => {
}, []);
useEffect(() => {
console.log('zones: ', zones);
localStorage.setItem("zones", JSON.stringify(zones));
}, [zones]);
@ -156,6 +158,7 @@ const ZoneGroup: React.FC = () => {
points: [number, number, number][];
layer: string;
}) => {
console.log('zoneId: ', zone);
const email = localStorage.getItem("email");
const userId = localStorage.getItem("userId");
const organization = email!.split("@")[1].split(".")[0];

View File

@ -16,7 +16,7 @@ export function useAislePointSnapping(point: Point) {
} => {
if (!CAN_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
const connectedPoints = getConnectedPoints(point.uuid);
const connectedPoints = getConnectedPoints(point.pointUuid);
if (connectedPoints.length === 0) {
return {
position: newPosition,
@ -68,7 +68,7 @@ export function useAislePointSnapping(point: Point) {
isSnapped,
snapSources
};
}, [point.uuid, getConnectedPoints]);
}, [point.pointUuid, getConnectedPoints]);
return { snapPosition };
}

View File

@ -13,7 +13,7 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
if (!currentPoint) return [];
return aisles.flatMap(aisle =>
aisle.points.filter(point => point.uuid !== currentPoint.uuid)
aisle.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [aisles, currentPoint]);

View File

@ -17,7 +17,7 @@ function Point({ point }: { readonly point: Point }) {
const { toolMode } = useToolMode();
const { setPosition, removePoint } = useAisleStore();
const { snapPosition } = useAislePointSnapping(point);
const { checkSnapForAisle } = usePointSnapping({ uuid: point.uuid, pointType: point.pointType, position: point.position });
const { checkSnapForAisle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
const { deletePointOrLine } = useDeletePointOrLine();
@ -63,7 +63,7 @@ function Point({ point }: { readonly point: Point }) {
const aisleSnappedPosition = snapPosition(newPosition);
const finalSnappedPosition = checkSnapForAisle(aisleSnappedPosition.position);
setPosition(point.uuid, finalSnappedPosition.position);
setPosition(point.pointUuid, finalSnappedPosition.position);
}
}
}
@ -76,7 +76,7 @@ function Point({ point }: { readonly point: Point }) {
const handlePointClick = (point: Point) => {
if (deletePointOrLine) {
const removedAisles = removePoint(point.uuid);
const removedAisles = removePoint(point.pointUuid);
if (removedAisles.length > 0) {
setHoveredPoint(null);
console.log(removedAisles);
@ -85,7 +85,7 @@ function Point({ point }: { readonly point: Point }) {
}
useEffect(() => {
if (hoveredPoint && hoveredPoint.uuid !== point.uuid) {
if (hoveredPoint && hoveredPoint.pointUuid !== point.pointUuid) {
setIsHovered(false);
}
}, [hoveredPoint])
@ -102,8 +102,8 @@ function Point({ point }: { readonly point: Point }) {
onDragEnd={() => { handleDragEnd(point) }}
>
<mesh
key={point.uuid}
uuid={point.uuid}
key={point.pointUuid}
uuid={point.pointUuid}
name='Aisle-Point'
position={new THREE.Vector3(...point.position)}
onClick={() => {
@ -116,7 +116,7 @@ function Point({ point }: { readonly point: Point }) {
}
}}
onPointerOut={() => {
if (hoveredPoint && hoveredPoint.uuid === point.uuid) {
if (hoveredPoint && hoveredPoint.pointUuid === point.pointUuid) {
setHoveredPoint(null);
}
setIsHovered(false)

View File

@ -30,7 +30,7 @@ interface AisleStore {
) => void;
setArcAisleWidth: (aisleUuid: string, width: number) => void;
setCircleAisleWidth: (aisleUuid: string, width: number) => void;
setJunctionAisleWidth: (aisleUuid: string, width: number) => void;
setJunctionAisleProperties: (aisleUuid: string, props: { aisleWidth?: number; isFlipped: boolean; }) => void;
getAisleById: (uuid: string) => Aisle | undefined;
getAislePointById: (uuid: string) => Point | undefined;
@ -51,21 +51,21 @@ export const useAisleStore = create<AisleStore>()(
}),
updateAisle: (uuid, updated) => set((state) => {
const aisle = state.aisles.find((a) => a.uuid === uuid);
const aisle = state.aisles.find((a) => a.aisleUuid === uuid);
if (aisle) {
Object.assign(aisle, updated);
}
}),
removeAisle: (uuid) => set((state) => {
state.aisles = state.aisles.filter((a) => a.uuid !== uuid);
state.aisles = state.aisles.filter((a) => a.aisleUuid !== uuid);
}),
removePoint: (uuid) => {
const removedAisles: Aisle[] = [];
set((state) => {
state.aisles = state.aisles.filter((aisle) => {
const hasPoint = aisle.points.some((point) => point.uuid === uuid);
const hasPoint = aisle.points.some((point) => point.pointUuid === uuid);
if (hasPoint) {
removedAisles.push(JSON.parse(JSON.stringify(aisle)));
return false;
@ -79,7 +79,7 @@ export const useAisleStore = create<AisleStore>()(
setPosition: (pointUuid, position) => set((state) => {
for (const aisle of state.aisles) {
const point = aisle.points.find(p => p.uuid === pointUuid);
const point = aisle.points.find(p => p.pointUuid === pointUuid);
if (point) {
point.position = position;
}
@ -88,7 +88,7 @@ export const useAisleStore = create<AisleStore>()(
setLayer: (pointUuid, layer) => set((state) => {
for (const aisle of state.aisles) {
const point = aisle.points.find(p => p.uuid === pointUuid);
const point = aisle.points.find(p => p.pointUuid === pointUuid);
if (point) {
point.layer = layer;
}
@ -96,7 +96,7 @@ export const useAisleStore = create<AisleStore>()(
}),
setColor: (aisleUuid, color) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle) {
aisle.type.aisleColor = color;
}
@ -104,14 +104,14 @@ export const useAisleStore = create<AisleStore>()(
// Type-specific property setters
setSolidAisleWidth: (aisleUuid, width) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'solid-aisle') {
aisle.type.aisleWidth = width;
}
}),
setDashedAisleProperties: (aisleUuid, props) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'dashed-aisle') {
if (props.aisleWidth !== undefined) aisle.type.aisleWidth = props.aisleWidth;
if (props.dashLength !== undefined) aisle.type.dashLength = props.dashLength;
@ -120,14 +120,14 @@ export const useAisleStore = create<AisleStore>()(
}),
setStrippedAisleWidth: (aisleUuid, width) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
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.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'dotted-aisle') {
if (props.dotRadius !== undefined) aisle.type.dotRadius = props.dotRadius;
if (props.gapLength !== undefined) aisle.type.gapLength = props.gapLength;
@ -135,14 +135,14 @@ export const useAisleStore = create<AisleStore>()(
}),
setArrowAisleWidth: (aisleUuid, width) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'arrow-aisle') {
aisle.type.aisleWidth = width;
}
}),
setArrowsAisleProperties: (aisleUuid, props) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'arrows-aisle') {
if (props.aisleWidth !== undefined) aisle.type.aisleWidth = props.aisleWidth;
if (props.aisleLength !== undefined) aisle.type.aisleLength = props.aisleLength;
@ -151,33 +151,34 @@ export const useAisleStore = create<AisleStore>()(
}),
setArcAisleWidth: (aisleUuid, width) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'arc-aisle') {
aisle.type.aisleWidth = width;
}
}),
setCircleAisleWidth: (aisleUuid, width) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'circle-aisle') {
aisle.type.aisleWidth = width;
}
}),
setJunctionAisleWidth: (aisleUuid, width) => set((state) => {
const aisle = state.aisles.find(a => a.uuid === aisleUuid);
setJunctionAisleProperties: (aisleUuid, props) => set((state) => {
const aisle = state.aisles.find(a => a.aisleUuid === aisleUuid);
if (aisle && aisle.type.aisleType === 'junction-aisle') {
aisle.type.aisleWidth = width;
if (props.aisleWidth !== undefined) aisle.type.aisleWidth = props.aisleWidth;
if (props.isFlipped !== undefined) aisle.type.isFlipped = props.isFlipped;
}
}),
getAisleById: (uuid) => {
return get().aisles.find((a) => a.uuid === uuid);
return get().aisles.find((a) => a.aisleUuid === uuid);
},
getAislePointById: (uuid) => {
for (const aisle of get().aisles) {
const point = aisle.points.find(p => p.uuid === uuid);
const point = aisle.points.find(p => p.pointUuid === uuid);
if (point) {
return point;
}
@ -189,8 +190,8 @@ export const useAisleStore = create<AisleStore>()(
const connected: Point[] = [];
for (const aisle of get().aisles) {
for (const point of aisle.points) {
if (point.uuid === uuid) {
connected.push(...aisle.points.filter(p => p.uuid !== uuid));
if (point.pointUuid === uuid) {
connected.push(...aisle.points.filter(p => p.pointUuid !== uuid));
}
}
}
@ -198,7 +199,7 @@ export const useAisleStore = create<AisleStore>()(
},
getAisleType: <T extends AisleType>(uuid: string) => {
const aisle = get().aisles.find(a => a.uuid === uuid);
const aisle = get().aisles.find(a => a.aisleUuid === uuid);
return aisle?.type as T | undefined;
},
}))

View File

@ -25,6 +25,9 @@ interface BuilderState {
// Arrows aisle properties
aisleLength: number;
// Junction aisle properties
isFlipped: boolean;
// Setters for common properties
setHoveredPoint: (point: Point | null) => void;
@ -47,6 +50,9 @@ interface BuilderState {
// Setters for arrows aisle
setAisleLength: (length: number) => void;
// Setters for junction aisle
setIsFlipped: (isFlipped: boolean) => void;
// Batch setters
setDashedAisleProperties: (width: number, dashLength: number, gapLength: number) => void;
setDottedAisleProperties: (width: number, dotRadius: number, gapLength: number) => void;
@ -71,6 +77,7 @@ export const useBuilderStore = create<BuilderState>()(
gapLength: 0.3,
dotRadius: 0.1,
aisleLength: 0.6,
isFlipped: false,
// Individual setters
@ -133,6 +140,11 @@ export const useBuilderStore = create<BuilderState>()(
state.aisleLength = length;
});
},
setIsFlipped: (isFlipped) => {
set((state) => {
state.isFlipped = isFlipped;
});
},
// Batch setters
setDashedAisleProperties: (width, dashLength, gapLength) => {

View File

@ -34,7 +34,7 @@ type Assets = Asset[];
type PointTypes = 'Aisle' | 'Wall' | 'Floor' | 'Zone';
interface Point {
uuid: string;
pointUuid: string;
pointType: PointTypes;
position: [number, number, number];
layer: number;
@ -102,12 +102,13 @@ interface JunctionAisle {
aisleType: 'junction-aisle';
aisleColor: AisleColors;
aisleWidth: number;
isFlipped: boolean;
}
type AisleType = SolidAisle | DashedAisle | StrippedAisle | DottedAisle | ArrowAisle | ArrowsAisle | ArcAisle | CircleAisle | JunctionAisle;
interface Aisle {
uuid: string;
aisleUuid: string;
points: [Point, Point];
type: AisleType;
}