Refactor wall classification logic for improved performance and readability; optimize wall merging and polygon generation. Update wall and zone creators to handle snapping and point connections more effectively. Enhance camera switch view functionality and clean up shortcut key handling.
This commit is contained in:
@@ -1,13 +1,5 @@
|
||||
import React from "react";
|
||||
import {
|
||||
DocumentationIcon,
|
||||
HelpIcon,
|
||||
HomeIcon,
|
||||
LogoutIcon,
|
||||
NotificationIcon,
|
||||
ProjectsIcon,
|
||||
TutorialsIcon,
|
||||
} from "../icons/DashboardIcon";
|
||||
import { DocumentationIcon, HelpIcon, HomeIcon, LogoutIcon, NotificationIcon, ProjectsIcon, TutorialsIcon } from "../icons/DashboardIcon";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import darkThemeImage from "../../assets/image/darkThemeProject.png";
|
||||
import lightThemeImage from "../../assets/image/lightThemeProject.png";
|
||||
@@ -54,7 +46,6 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
projectUuid: projectId,
|
||||
};
|
||||
|
||||
console.log('addProject: ', addProject);
|
||||
projectSocket.emit("v1:project:add", addProject);
|
||||
} else {
|
||||
// API
|
||||
@@ -72,11 +63,7 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
<div className="side-pannel-header">
|
||||
<div className="user-container">
|
||||
<div className="user-profile">{userName?.charAt(0).toUpperCase()}</div>
|
||||
<div className="user-name">
|
||||
{userName
|
||||
? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()
|
||||
: "Anonymous"}
|
||||
</div>
|
||||
<div className="user-name">{userName ? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase() : "Anonymous"}</div>
|
||||
</div>
|
||||
<div className="notifications-container">
|
||||
<NotificationIcon />
|
||||
@@ -87,26 +74,15 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
</div>
|
||||
<div className="side-bar-content-container">
|
||||
<div className="side-bar-options-container">
|
||||
<button
|
||||
className={activeTab === "Home" ? "option-list active" : "option-list"}
|
||||
onClick={() => setActiveTab("Home")}
|
||||
>
|
||||
<button className={activeTab === "Home" ? "option-list active" : "option-list"} onClick={() => setActiveTab("Home")}>
|
||||
<HomeIcon />
|
||||
Home
|
||||
</button>
|
||||
<button
|
||||
className={activeTab === "Projects" ? "option-list active" : "option-list"}
|
||||
title="Projects"
|
||||
onClick={() => setActiveTab("Projects")}
|
||||
>
|
||||
<button className={activeTab === "Projects" ? "option-list active" : "option-list"} title="Projects" onClick={() => setActiveTab("Projects")}>
|
||||
<ProjectsIcon />
|
||||
Projects
|
||||
</button>
|
||||
<button
|
||||
className={activeTab === "Trash" ? "option-list active" : "option-list"}
|
||||
title="Trash"
|
||||
onClick={() => setActiveTab("Trash")}
|
||||
>
|
||||
<button className={activeTab === "Trash" ? "option-list active" : "option-list"} title="Trash" onClick={() => setActiveTab("Trash")}>
|
||||
<TrashIcon />
|
||||
Trash
|
||||
</button>
|
||||
@@ -123,9 +99,7 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
Tutorials
|
||||
</button>
|
||||
<button
|
||||
className={
|
||||
activeTab === "Documentation" ? "option-list active" : "option-list"
|
||||
}
|
||||
className={activeTab === "Documentation" ? "option-list active" : "option-list"}
|
||||
title="coming soon"
|
||||
disabled
|
||||
onClick={() => {
|
||||
|
||||
@@ -19,7 +19,7 @@ function AisleCreator() {
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { aisleStore, undoRedo2DStore, versionStore } = useSceneContext();
|
||||
const { addAisle, getAislePointById } = aisleStore();
|
||||
const { addAisle, getAislePointById, getConnectedPoints } = aisleStore();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
@@ -76,7 +76,9 @@ function AisleCreator() {
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) {
|
||||
const connectedPoints = getConnectedPoints(tempPoints[0]?.pointUuid || "");
|
||||
|
||||
if (snappedPoint && (snappedPoint.pointUuid === tempPoints[0]?.pointUuid || connectedPoints.some((point) => point.pointUuid === snappedPoint.pointUuid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,7 +196,32 @@ function AisleCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, getAislePointById, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint, selectedVersion?.versionId]);
|
||||
}, [
|
||||
gl,
|
||||
camera,
|
||||
scene,
|
||||
raycaster,
|
||||
pointer,
|
||||
plane,
|
||||
toggleView,
|
||||
toolMode,
|
||||
activeLayer,
|
||||
socket,
|
||||
tempPoints,
|
||||
isCreating,
|
||||
addAisle,
|
||||
getAislePointById,
|
||||
aisleType,
|
||||
aisleWidth,
|
||||
aisleColor,
|
||||
dashLength,
|
||||
gapLength,
|
||||
dotRadius,
|
||||
aisleLength,
|
||||
snappedPosition,
|
||||
snappedPoint,
|
||||
selectedVersion?.versionId,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -86,6 +86,10 @@ function FloorCreator() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tempPoints.length <= 2 && pointIntersects && pointIntersects.object.uuid === tempPoints[0]?.pointUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
}
|
||||
@@ -288,7 +292,7 @@ function FloorCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
|
||||
}, [camera, raycaster, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import * as THREE from 'three';
|
||||
import { useCallback } from 'react';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import * as THREE from "three";
|
||||
import { useCallback } from "react";
|
||||
import { useSceneContext } from "../../../scene/sceneContext";
|
||||
|
||||
const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
||||
|
||||
const CAN_POINT_SNAP = true; // Whether snapping is enabled or not
|
||||
|
||||
const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
||||
const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
||||
|
||||
const CAN_ANGLE_SNAP = true; // Whether snapping is enabled or not
|
||||
|
||||
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
|
||||
export const usePointSnapping = (currentPoint: { uuid: string; pointType: string; position: [number, number, number] } | null) => {
|
||||
const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext();
|
||||
const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore();
|
||||
const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore();
|
||||
@@ -21,350 +21,359 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
|
||||
|
||||
const getAllOtherWallPoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
return walls.flatMap(wall =>
|
||||
wall.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
return walls.flatMap((wall) => wall.points.filter((point) => point.pointUuid !== currentPoint.uuid));
|
||||
}, [walls, currentPoint]);
|
||||
|
||||
const snapWallPoint = useCallback((position: [number, number, number], tempPoints?: Point) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
const snapWallPoint = useCallback(
|
||||
(position: [number, number, number]) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = getAllOtherWallPoints();
|
||||
if (tempPoints) {
|
||||
otherPoints.push(tempPoints);
|
||||
}
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Wall') {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
const otherPoints = getAllOtherWallPoints();
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Wall") {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
}
|
||||
}
|
||||
}
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherWallPoints]);
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
},
|
||||
[currentPoint, getAllOtherWallPoints]
|
||||
);
|
||||
|
||||
const snapWallAngle = useCallback((newPosition: [number, number, number]): {
|
||||
position: [number, number, number],
|
||||
isSnapped: boolean,
|
||||
snapSources: THREE.Vector3[]
|
||||
} => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
const snapWallAngle = useCallback(
|
||||
(newPosition: [number, number, number]): { position: [number, number, number]; isSnapped: boolean; snapSources: THREE.Vector3[] } => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
|
||||
const connectedPoints = getConnectedWallPoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return {
|
||||
position: newPosition,
|
||||
isSnapped: false,
|
||||
snapSources: [],
|
||||
};
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
|
||||
const isSnapped = snapSources.length > 0;
|
||||
|
||||
const connectedPoints = getConnectedWallPoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return {
|
||||
position: newPosition,
|
||||
isSnapped: false,
|
||||
snapSources: []
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources,
|
||||
};
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
|
||||
const isSnapped = snapSources.length > 0;
|
||||
|
||||
return {
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources
|
||||
};
|
||||
}, [currentPoint, getConnectedAislePoints]);
|
||||
},
|
||||
[currentPoint, getConnectedAislePoints]
|
||||
);
|
||||
|
||||
// Aisle Snapping
|
||||
|
||||
const getAllOtherAislePoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
|
||||
return aisles.flatMap(aisle =>
|
||||
aisle.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
return aisles.flatMap((aisle) => aisle.points.filter((point) => point.pointUuid !== currentPoint.uuid));
|
||||
}, [aisles, currentPoint]);
|
||||
|
||||
const snapAislePoint = useCallback((position: [number, number, number]) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
const snapAislePoint = useCallback(
|
||||
(position: [number, number, number]) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
const otherPoints = getAllOtherAislePoints();
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Aisle') {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
const otherPoints = getAllOtherAislePoints();
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Aisle") {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherAislePoints]);
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
},
|
||||
[currentPoint, getAllOtherAislePoints]
|
||||
);
|
||||
|
||||
const snapAisleAngle = useCallback((newPosition: [number, number, number]): {
|
||||
position: [number, number, number],
|
||||
isSnapped: boolean,
|
||||
snapSources: THREE.Vector3[]
|
||||
} => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
const snapAisleAngle = useCallback(
|
||||
(newPosition: [number, number, number]): { position: [number, number, number]; isSnapped: boolean; snapSources: THREE.Vector3[] } => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
|
||||
const connectedPoints = getConnectedAislePoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return {
|
||||
position: newPosition,
|
||||
isSnapped: false,
|
||||
snapSources: [],
|
||||
};
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
|
||||
const isSnapped = snapSources.length > 0;
|
||||
|
||||
const connectedPoints = getConnectedAislePoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return {
|
||||
position: newPosition,
|
||||
isSnapped: false,
|
||||
snapSources: []
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources,
|
||||
};
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
|
||||
const isSnapped = snapSources.length > 0;
|
||||
|
||||
return {
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources
|
||||
};
|
||||
}, [currentPoint, getConnectedAislePoints]);
|
||||
},
|
||||
[currentPoint, getConnectedAislePoints]
|
||||
);
|
||||
|
||||
// Floor Snapping
|
||||
|
||||
const getAllOtherFloorPoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
|
||||
return floors.flatMap(floor =>
|
||||
floor.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
return floors.flatMap((floor) => floor.points.filter((point) => point.pointUuid !== currentPoint.uuid));
|
||||
}, [floors, currentPoint]);
|
||||
|
||||
const snapFloorPoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
const snapFloorPoint = useCallback(
|
||||
(position: [number, number, number], tempPoints?: Point[] | []) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
let otherPoints = getAllOtherFloorPoints();
|
||||
if (tempPoints) {
|
||||
otherPoints = [...otherPoints, ...tempPoints];
|
||||
}
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Floor') {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
let otherPoints = getAllOtherFloorPoints();
|
||||
if (tempPoints) {
|
||||
otherPoints = [...otherPoints, ...tempPoints];
|
||||
}
|
||||
}
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherFloorPoints]);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
|
||||
const snapFloorAngle = useCallback((newPosition: [number, number, number]): {
|
||||
position: [number, number, number],
|
||||
isSnapped: boolean,
|
||||
snapSources: THREE.Vector3[]
|
||||
} => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
|
||||
const connectedPoints = getConnectedFloorPoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Floor") {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
},
|
||||
[currentPoint, getAllOtherFloorPoints]
|
||||
);
|
||||
|
||||
const snapFloorAngle = useCallback(
|
||||
(
|
||||
newPosition: [number, number, number]
|
||||
): {
|
||||
position: [number, number, number];
|
||||
isSnapped: boolean;
|
||||
snapSources: THREE.Vector3[];
|
||||
} => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
|
||||
const connectedPoints = getConnectedFloorPoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
|
||||
const isSnapped = snapSources.length > 0;
|
||||
const isSnapped = snapSources.length > 0;
|
||||
|
||||
return {
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources
|
||||
};
|
||||
}, [currentPoint, getConnectedFloorPoints]);
|
||||
return {
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources,
|
||||
};
|
||||
},
|
||||
[currentPoint, getConnectedFloorPoints]
|
||||
);
|
||||
|
||||
// Zone Snapping
|
||||
|
||||
const getAllOtherZonePoints = useCallback(() => {
|
||||
if (!currentPoint) return [];
|
||||
|
||||
return zones.flatMap(zone =>
|
||||
zone.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
||||
);
|
||||
return zones.flatMap((zone) => zone.points.filter((point) => point.pointUuid !== currentPoint.uuid));
|
||||
}, [zones, currentPoint]);
|
||||
|
||||
const snapZonePoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
const snapZonePoint = useCallback(
|
||||
(position: [number, number, number], tempPoints?: Point[] | []) => {
|
||||
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||
|
||||
let otherPoints = getAllOtherZonePoints();
|
||||
if (tempPoints) {
|
||||
otherPoints = [...otherPoints, ...tempPoints];
|
||||
}
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Zone') {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
let otherPoints = getAllOtherZonePoints();
|
||||
if (tempPoints) {
|
||||
otherPoints = [...otherPoints, ...tempPoints];
|
||||
}
|
||||
}
|
||||
const currentVec = new THREE.Vector3(...position);
|
||||
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
}, [currentPoint, getAllOtherZonePoints]);
|
||||
for (const point of otherPoints) {
|
||||
const pointVec = new THREE.Vector3(...point.position);
|
||||
const distance = currentVec.distanceTo(pointVec);
|
||||
|
||||
const snapZoneAngle = useCallback((newPosition: [number, number, number]): {
|
||||
position: [number, number, number],
|
||||
isSnapped: boolean,
|
||||
snapSources: THREE.Vector3[]
|
||||
} => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
|
||||
const connectedPoints = getConnectedZonePoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Zone") {
|
||||
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
return { position: position, isSnapped: false, snappedPoint: null };
|
||||
},
|
||||
[currentPoint, getAllOtherZonePoints]
|
||||
);
|
||||
|
||||
const snapZoneAngle = useCallback(
|
||||
(
|
||||
newPosition: [number, number, number]
|
||||
): {
|
||||
position: [number, number, number];
|
||||
isSnapped: boolean;
|
||||
snapSources: THREE.Vector3[];
|
||||
} => {
|
||||
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
|
||||
const connectedPoints = getConnectedZonePoints(currentPoint.uuid);
|
||||
if (connectedPoints.length === 0) {
|
||||
return { position: newPosition, isSnapped: false, snapSources: [] };
|
||||
}
|
||||
|
||||
const newPos = new THREE.Vector3(...newPosition);
|
||||
let closestX: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
let closestZ: { pos: THREE.Vector3; dist: number } | null = null;
|
||||
|
||||
for (const connectedPoint of connectedPoints) {
|
||||
const cPos = new THREE.Vector3(...connectedPoint.position);
|
||||
const xDist = Math.abs(newPos.x - cPos.x);
|
||||
const zDist = Math.abs(newPos.z - cPos.z);
|
||||
|
||||
if (xDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestX || xDist < closestX.dist) {
|
||||
closestX = { pos: cPos, dist: xDist };
|
||||
}
|
||||
}
|
||||
|
||||
if (zDist < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
||||
if (!closestZ || zDist < closestZ.dist) {
|
||||
closestZ = { pos: cPos, dist: zDist };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
const snappedPos = newPos.clone();
|
||||
const snapSources: THREE.Vector3[] = [];
|
||||
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
if (closestX) {
|
||||
snappedPos.x = closestX.pos.x;
|
||||
snapSources.push(closestX.pos.clone());
|
||||
}
|
||||
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
if (closestZ) {
|
||||
snappedPos.z = closestZ.pos.z;
|
||||
snapSources.push(closestZ.pos.clone());
|
||||
}
|
||||
|
||||
const isSnapped = snapSources.length > 0;
|
||||
const isSnapped = snapSources.length > 0;
|
||||
|
||||
return {
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources
|
||||
};
|
||||
}, [currentPoint, getConnectedZonePoints]);
|
||||
return {
|
||||
position: [snappedPos.x, snappedPos.y, snappedPos.z],
|
||||
isSnapped,
|
||||
snapSources,
|
||||
};
|
||||
},
|
||||
[currentPoint, getConnectedZonePoints]
|
||||
);
|
||||
|
||||
return {
|
||||
snapAislePoint,
|
||||
@@ -376,4 +385,4 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
|
||||
snapZonePoint,
|
||||
snapZoneAngle,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,151 +1,55 @@
|
||||
import { useMemo } from 'react';
|
||||
import * as turf from '@turf/turf';
|
||||
import { useMemo } from "react";
|
||||
import * as turf from "@turf/turf";
|
||||
|
||||
export function useWallClassification(walls: Walls) {
|
||||
|
||||
const findRooms = () => {
|
||||
if (walls.length < 3) return [];
|
||||
|
||||
// Map pointUuid to list of connected line segments
|
||||
const pointMap = new Map<string, Wall[]>();
|
||||
const wallSet = new Map<string, Wall>();
|
||||
|
||||
const makeKey = (p1: Point, p2: Point) => {
|
||||
return [p1.pointUuid, p2.pointUuid].sort().join("-");
|
||||
};
|
||||
|
||||
for (const wall of walls) {
|
||||
for (const point of wall.points) {
|
||||
const list = pointMap.get(point.pointUuid) || [];
|
||||
list.push(wall);
|
||||
pointMap.set(point.pointUuid, list);
|
||||
const [p1, p2] = wall.points;
|
||||
|
||||
if (p1.pointUuid === p2.pointUuid || (p1.position[0] === p2.position[0] && p1.position[1] === p2.position[1] && p1.position[2] === p2.position[2])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = makeKey(p1, p2);
|
||||
if (!wallSet.has(key)) {
|
||||
wallSet.set(key, wall);
|
||||
}
|
||||
}
|
||||
|
||||
// Create graph of connected walls using pointUuid
|
||||
const visited = new Set<string>();
|
||||
const mergedLineStrings = [];
|
||||
const uniqueWalls = Array.from(wallSet.values());
|
||||
|
||||
const wallKey = (p1: Point, p2: Point) => `${p1.pointUuid}-${p2.pointUuid}`;
|
||||
const lineStrings = uniqueWalls.map((wall) => {
|
||||
const coords: [number, number][] = wall.points.map((p) => [p.position[0], p.position[2]]);
|
||||
return turf.lineString(coords, { wallUuid: wall.wallUuid });
|
||||
});
|
||||
|
||||
for (const wall of walls) {
|
||||
const key = wallKey(wall.points[0], wall.points[1]);
|
||||
if (visited.has(key) || visited.has(wallKey(wall.points[1], wall.points[0]))) continue;
|
||||
const collection = turf.featureCollection(lineStrings);
|
||||
|
||||
let line: Point[] = [...wall.points];
|
||||
visited.add(key);
|
||||
|
||||
// Try to extend the line forward and backward by matching endpoints
|
||||
let extended = true;
|
||||
while (extended) {
|
||||
extended = false;
|
||||
|
||||
const last = line[line.length - 1];
|
||||
const nextWalls = pointMap.get(last.pointUuid) || [];
|
||||
|
||||
for (const next of nextWalls) {
|
||||
const [n1, n2] = next.points;
|
||||
const nextKey = wallKey(n1, n2);
|
||||
if (visited.has(nextKey) || visited.has(wallKey(n2, n1))) continue;
|
||||
|
||||
if (n1.pointUuid === last.pointUuid && n2.pointUuid !== line[line.length - 2]?.pointUuid) {
|
||||
line.push(n2);
|
||||
visited.add(nextKey);
|
||||
extended = true;
|
||||
break;
|
||||
} else if (n2.pointUuid === last.pointUuid && n1.pointUuid !== line[line.length - 2]?.pointUuid) {
|
||||
line.push(n1);
|
||||
visited.add(nextKey);
|
||||
extended = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const first = line[0];
|
||||
const prevWalls = pointMap.get(first.pointUuid) || [];
|
||||
|
||||
for (const prev of prevWalls) {
|
||||
const [p1, p2] = prev.points;
|
||||
const prevKey = wallKey(p1, p2);
|
||||
if (visited.has(prevKey) || visited.has(wallKey(p2, p1))) continue;
|
||||
|
||||
if (p1.pointUuid === first.pointUuid && p2.pointUuid !== line[1]?.pointUuid) {
|
||||
line.unshift(p2);
|
||||
visited.add(prevKey);
|
||||
extended = true;
|
||||
break;
|
||||
} else if (p2.pointUuid === first.pointUuid && p1.pointUuid !== line[1]?.pointUuid) {
|
||||
line.unshift(p1);
|
||||
visited.add(prevKey);
|
||||
extended = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create merged LineString
|
||||
const coords = line.map(p => [p.position[0], p.position[2]]);
|
||||
mergedLineStrings.push(turf.lineString(coords, {
|
||||
pointUuids: line.map(p => p.pointUuid)
|
||||
}));
|
||||
}
|
||||
|
||||
const validLineStrings = mergedLineStrings.map(ls => {
|
||||
const coords = ls.geometry.coordinates.map(coord => coord.join(','));
|
||||
|
||||
if (coords.length < 2) return null;
|
||||
|
||||
const start = coords[0];
|
||||
const end = coords[coords.length - 1];
|
||||
const middle = coords.slice(1, -1);
|
||||
|
||||
const seen = new Set<string>([start, end]);
|
||||
const filteredMiddle: string[] = [];
|
||||
|
||||
for (const point of middle) {
|
||||
if (!seen.has(point)) {
|
||||
seen.add(point);
|
||||
filteredMiddle.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
const newCoords = [start, ...filteredMiddle, end];
|
||||
|
||||
if (newCoords.length >= 4) {
|
||||
const resultCoords = newCoords.map(str => str.split(',').map(Number));
|
||||
return {
|
||||
...ls,
|
||||
geometry: {
|
||||
...ls.geometry,
|
||||
coordinates: resultCoords,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
|
||||
if (validLineStrings.length === 0) return [];
|
||||
|
||||
const lineStrings = turf.featureCollection(validLineStrings as any);
|
||||
const polygons = turf.polygonize(lineStrings as any);
|
||||
const polygons = turf.polygonize(collection);
|
||||
|
||||
const rooms: Point[][] = [];
|
||||
|
||||
polygons.features.forEach(feature => {
|
||||
if (feature.geometry.type === 'Polygon') {
|
||||
const coordinates = feature.geometry.coordinates[0];
|
||||
polygons.features.forEach((feature) => {
|
||||
if (feature.geometry.type === "Polygon") {
|
||||
const coords = feature.geometry.coordinates[0];
|
||||
const roomPoints: Point[] = [];
|
||||
|
||||
for (const [x, z] of coordinates) {
|
||||
const matchingPoint = walls.flatMap(wall => wall.points)
|
||||
.find(p =>
|
||||
p.position[0].toFixed(10) === x.toFixed(10) &&
|
||||
p.position[2].toFixed(10) === z.toFixed(10)
|
||||
);
|
||||
|
||||
for (const [x, z] of coords) {
|
||||
const matchingPoint = uniqueWalls.flatMap((w) => w.points).find((p) => p.position[0].toFixed(6) === x.toFixed(6) && p.position[2].toFixed(6) === z.toFixed(6));
|
||||
if (matchingPoint) {
|
||||
roomPoints.push(matchingPoint);
|
||||
}
|
||||
}
|
||||
|
||||
if (roomPoints.length > 0 &&
|
||||
roomPoints[0].pointUuid !== roomPoints[roomPoints.length - 1].pointUuid) {
|
||||
if (roomPoints.length > 0 && roomPoints[0].pointUuid !== roomPoints[roomPoints.length - 1].pointUuid) {
|
||||
roomPoints.push(roomPoints[0]);
|
||||
}
|
||||
|
||||
@@ -161,12 +65,14 @@ export function useWallClassification(walls: Walls) {
|
||||
const rooms = useMemo(() => findRooms(), [walls]);
|
||||
|
||||
const findWallType = (wall: Wall) => {
|
||||
const containingRooms = rooms.filter(room => {
|
||||
const containingRooms = rooms.filter((room) => {
|
||||
for (let i = 0; i < room.length - 1; i++) {
|
||||
const p1 = room[i];
|
||||
const p2 = room[i + 1];
|
||||
if ((wall.points[0].pointUuid === p1.pointUuid && wall.points[1].pointUuid === p2.pointUuid) ||
|
||||
(wall.points[0].pointUuid === p2.pointUuid && wall.points[1].pointUuid === p1.pointUuid)) {
|
||||
if (
|
||||
(wall.points[0].pointUuid === p1.pointUuid && wall.points[1].pointUuid === p2.pointUuid) ||
|
||||
(wall.points[0].pointUuid === p2.pointUuid && wall.points[1].pointUuid === p1.pointUuid)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -175,18 +81,18 @@ export function useWallClassification(walls: Walls) {
|
||||
|
||||
if (containingRooms.length === 0) {
|
||||
return {
|
||||
type: 'segment',
|
||||
rooms: []
|
||||
type: "segment",
|
||||
rooms: [],
|
||||
};
|
||||
} else if (containingRooms.length === 1) {
|
||||
return {
|
||||
type: 'room',
|
||||
rooms: containingRooms
|
||||
type: "room",
|
||||
rooms: containingRooms,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'rooms',
|
||||
rooms: containingRooms
|
||||
type: "rooms",
|
||||
rooms: containingRooms,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -197,16 +103,16 @@ export function useWallClassification(walls: Walls) {
|
||||
|
||||
const isRoomWall = (wall: Wall): boolean => {
|
||||
const type = findWallType(wall).type;
|
||||
return type === 'room' || type === 'rooms';
|
||||
return type === "room" || type === "rooms";
|
||||
};
|
||||
|
||||
const isSegmentWall = (wall: Wall): boolean => {
|
||||
return findWallType(wall).type === 'segment';
|
||||
return findWallType(wall).type === "segment";
|
||||
};
|
||||
|
||||
const isWallFlipped = (wall: Wall): boolean => {
|
||||
const wallType = findWallType(wall);
|
||||
if (wallType.type === 'segment') return false;
|
||||
if (wallType.type === "segment") return false;
|
||||
|
||||
for (const room of wallType.rooms) {
|
||||
for (let i = 0; i < room.length - 1; i++) {
|
||||
@@ -223,13 +129,12 @@ export function useWallClassification(walls: Walls) {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
rooms,
|
||||
getWallType,
|
||||
isRoomWall,
|
||||
isSegmentWall,
|
||||
findRooms,
|
||||
isWallFlipped
|
||||
isWallFlipped,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ function WallInstances() {
|
||||
const { rooms } = useWallClassification(walls);
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('walls: ', walls);
|
||||
// console.log("walls: ", walls);
|
||||
}, [walls]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store';
|
||||
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
||||
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
||||
import ReferenceLine from '../../line/reference/referenceLine';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import * as THREE from "three";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { Html } from "@react-three/drei";
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import { useActiveLayer, useToolMode, useToggleView } from "../../../../store/builder/store";
|
||||
import { useDirectionalSnapping } from "../../point/helpers/useDirectionalSnapping";
|
||||
import { usePointSnapping } from "../../point/helpers/usePointSnapping";
|
||||
import ReferenceLine from "../../line/reference/referenceLine";
|
||||
|
||||
interface ReferenceWallProps {
|
||||
tempPoints: Point[];
|
||||
@@ -26,10 +26,10 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
|
||||
|
||||
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null);
|
||||
const { snapWallPoint } = usePointSnapping({ uuid: 'temp-wall', pointType: 'Wall', position: directionalSnap.position || [0, 0, 0] });
|
||||
const { snapWallPoint } = usePointSnapping({ uuid: "temp-wall", pointType: "Wall", position: directionalSnap.position || [0, 0, 0] });
|
||||
|
||||
useFrame(() => {
|
||||
if (toolMode === 'Wall' && toggleView && tempPoints.length === 1) {
|
||||
if (toolMode === "Wall" && toggleView && tempPoints.length === 1) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
@@ -37,7 +37,7 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (!intersectionPoint) return;
|
||||
const snapped = snapWallPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints[0]);
|
||||
const snapped = snapWallPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||
|
||||
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||
finalPosition.current = snapped.position;
|
||||
@@ -58,23 +58,22 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
const wallPoints: [Point, Point] = [
|
||||
tempPoints[0],
|
||||
{
|
||||
pointUuid: 'temp-point',
|
||||
pointType: 'Wall',
|
||||
pointUuid: "temp-point",
|
||||
pointType: "Wall",
|
||||
position: finalPosition.current,
|
||||
layer: activeLayer,
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
setTempWall({
|
||||
wallUuid: 'temp-wall',
|
||||
wallUuid: "temp-wall",
|
||||
points: wallPoints,
|
||||
outsideMaterial: 'default',
|
||||
insideMaterial: 'default',
|
||||
outsideMaterial: "default",
|
||||
insideMaterial: "default",
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
})
|
||||
|
||||
decals: [],
|
||||
});
|
||||
} else if (tempWall !== null) {
|
||||
setTempWall(null);
|
||||
}
|
||||
@@ -87,9 +86,7 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
if (!tempWall) return null;
|
||||
|
||||
const renderWall = () => {
|
||||
return (
|
||||
<ReferenceLine points={tempWall.points} />
|
||||
)
|
||||
return <ReferenceLine points={tempWall.points} />;
|
||||
};
|
||||
|
||||
const textPosition = new THREE.Vector3().addVectors(new THREE.Vector3(...tempWall.points[0].position), new THREE.Vector3(...tempWall.points[1].position)).divideScalar(2);
|
||||
@@ -113,8 +110,8 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
</Html>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<group name="Wall-Reference-Group">
|
||||
@@ -124,4 +121,4 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
|
||||
);
|
||||
}
|
||||
|
||||
export default ReferenceWall;
|
||||
export default ReferenceWall;
|
||||
|
||||
@@ -22,7 +22,7 @@ function WallCreator() {
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { wallStore, undoRedo2DStore, versionStore } = useSceneContext();
|
||||
const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore();
|
||||
const { addWall, getWallPointById, removeWall, getWallByPoints, getConnectedPoints } = wallStore();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
@@ -379,7 +379,9 @@ function WallCreator() {
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) {
|
||||
const connectedPoints = getConnectedPoints(tempPoints[0]?.pointUuid || "");
|
||||
|
||||
if (snappedPoint && (snappedPoint.pointUuid === tempPoints[0]?.pointUuid || connectedPoints.some((point) => point.pointUuid === snappedPoint.pointUuid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -485,7 +487,29 @@ function WallCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addWall, getWallPointById, wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint, selectedVersion?.versionId]);
|
||||
}, [
|
||||
gl,
|
||||
camera,
|
||||
scene,
|
||||
raycaster,
|
||||
pointer,
|
||||
plane,
|
||||
toggleView,
|
||||
toolMode,
|
||||
activeLayer,
|
||||
socket,
|
||||
tempPoints,
|
||||
isCreating,
|
||||
addWall,
|
||||
getWallPointById,
|
||||
wallThickness,
|
||||
wallHeight,
|
||||
insideMaterial,
|
||||
outsideMaterial,
|
||||
snappedPosition,
|
||||
snappedPoint,
|
||||
selectedVersion?.versionId,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -86,6 +86,10 @@ function ZoneCreator() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tempPoints.length <= 2 && pointIntersects && pointIntersects.object.uuid === tempPoints[0]?.pointUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
}
|
||||
@@ -282,7 +286,7 @@ function ZoneCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, zoneColor, zoneHeight, snappedPosition, snappedPoint]);
|
||||
}, [gl, camera, scene, raycaster, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, zoneColor, zoneHeight, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from 'three';
|
||||
import { PerspectiveCamera, OrthographicCamera, CameraControls } from '@react-three/drei';
|
||||
import * as THREE from "three";
|
||||
import { PerspectiveCamera, OrthographicCamera, CameraControls } from "@react-three/drei";
|
||||
import { useParams } from "react-router-dom";
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import { getCameraApi } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||
import { useToggleView } from "../../../store/builder/store";
|
||||
|
||||
@@ -18,19 +18,21 @@ export default function SwitchView() {
|
||||
(controls as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
|
||||
} else {
|
||||
if (!projectId) return;
|
||||
getCameraApi(projectId).then((data) => {
|
||||
if (data?.position && data?.target) {
|
||||
(controls as CameraControls)?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||
(controls as CameraControls)?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||
} else {
|
||||
getCameraApi(projectId)
|
||||
.then((data) => {
|
||||
if (data?.position && data?.target) {
|
||||
(controls as CameraControls)?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||
(controls as CameraControls)?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||
} else {
|
||||
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
echo.error("Failed to retrieve camera position or target");
|
||||
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||
}
|
||||
}).catch(() => {
|
||||
echo.error("Failed to retrieve camera position or target");
|
||||
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||
})
|
||||
});
|
||||
|
||||
if (controls) {
|
||||
(controls as any).mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
|
||||
@@ -62,4 +64,4 @@ export default function SwitchView() {
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,12 @@ export default function Controls() {
|
||||
controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth);
|
||||
|
||||
if (!socket?.connected) {
|
||||
setCameraApi(projectId, new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition), new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget), new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation));
|
||||
setCameraApi(
|
||||
projectId,
|
||||
new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition),
|
||||
new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget),
|
||||
new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation)
|
||||
);
|
||||
} else {
|
||||
const camData = {
|
||||
organization,
|
||||
@@ -143,7 +148,6 @@ export default function Controls() {
|
||||
boundaryEnclosesCamera={true}
|
||||
dollyDragInverted
|
||||
>
|
||||
|
||||
<SwitchView />
|
||||
|
||||
<CamMode />
|
||||
|
||||
@@ -77,7 +77,7 @@ export const createVersionStore = () => {
|
||||
|
||||
setVersionName: (versionId: string, versionName: string) => {
|
||||
set((state) => {
|
||||
const version = state.versionHistory.find((v) => v.versionId === versionId);
|
||||
const version = get().getVersionById(versionId);
|
||||
if (version) {
|
||||
version.versionName = versionName;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export const createVersionStore = () => {
|
||||
|
||||
updateVersion: (versionId: string, versionName: string, versionDescription: string) => {
|
||||
set((state) => {
|
||||
const version = state.versionHistory.find((v) => v.versionId === versionId);
|
||||
const version = get().getVersionById(versionId);
|
||||
if (version) {
|
||||
version.versionName = versionName;
|
||||
version.versionDescription = versionDescription;
|
||||
|
||||
@@ -230,10 +230,6 @@ const KeyPressListener: React.FC = () => {
|
||||
setShowShortcuts(!showShortcuts);
|
||||
}
|
||||
|
||||
// if (keyCombination === "Ctrl+Shift+P") {
|
||||
// pref
|
||||
// }
|
||||
|
||||
if (keyCombination === "U") {
|
||||
setViewSceneLabels((prev) => !prev);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user