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:
2025-09-08 15:07:51 +05:30
parent 23e7ba39e8
commit da6400ea67
13 changed files with 460 additions and 514 deletions

View File

@@ -1,13 +1,5 @@
import React from "react"; import React from "react";
import { import { DocumentationIcon, HelpIcon, HomeIcon, LogoutIcon, NotificationIcon, ProjectsIcon, TutorialsIcon } from "../icons/DashboardIcon";
DocumentationIcon,
HelpIcon,
HomeIcon,
LogoutIcon,
NotificationIcon,
ProjectsIcon,
TutorialsIcon,
} from "../icons/DashboardIcon";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import darkThemeImage from "../../assets/image/darkThemeProject.png"; import darkThemeImage from "../../assets/image/darkThemeProject.png";
import lightThemeImage from "../../assets/image/lightThemeProject.png"; import lightThemeImage from "../../assets/image/lightThemeProject.png";
@@ -54,7 +46,6 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
projectUuid: projectId, projectUuid: projectId,
}; };
console.log('addProject: ', addProject);
projectSocket.emit("v1:project:add", addProject); projectSocket.emit("v1:project:add", addProject);
} else { } else {
// API // API
@@ -72,11 +63,7 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
<div className="side-pannel-header"> <div className="side-pannel-header">
<div className="user-container"> <div className="user-container">
<div className="user-profile">{userName?.charAt(0).toUpperCase()}</div> <div className="user-profile">{userName?.charAt(0).toUpperCase()}</div>
<div className="user-name"> <div className="user-name">{userName ? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase() : "Anonymous"}</div>
{userName
? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()
: "Anonymous"}
</div>
</div> </div>
<div className="notifications-container"> <div className="notifications-container">
<NotificationIcon /> <NotificationIcon />
@@ -87,26 +74,15 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
</div> </div>
<div className="side-bar-content-container"> <div className="side-bar-content-container">
<div className="side-bar-options-container"> <div className="side-bar-options-container">
<button <button className={activeTab === "Home" ? "option-list active" : "option-list"} onClick={() => setActiveTab("Home")}>
className={activeTab === "Home" ? "option-list active" : "option-list"}
onClick={() => setActiveTab("Home")}
>
<HomeIcon /> <HomeIcon />
Home Home
</button> </button>
<button <button className={activeTab === "Projects" ? "option-list active" : "option-list"} title="Projects" onClick={() => setActiveTab("Projects")}>
className={activeTab === "Projects" ? "option-list active" : "option-list"}
title="Projects"
onClick={() => setActiveTab("Projects")}
>
<ProjectsIcon /> <ProjectsIcon />
Projects Projects
</button> </button>
<button <button className={activeTab === "Trash" ? "option-list active" : "option-list"} title="Trash" onClick={() => setActiveTab("Trash")}>
className={activeTab === "Trash" ? "option-list active" : "option-list"}
title="Trash"
onClick={() => setActiveTab("Trash")}
>
<TrashIcon /> <TrashIcon />
Trash Trash
</button> </button>
@@ -123,9 +99,7 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
Tutorials Tutorials
</button> </button>
<button <button
className={ className={activeTab === "Documentation" ? "option-list active" : "option-list"}
activeTab === "Documentation" ? "option-list active" : "option-list"
}
title="coming soon" title="coming soon"
disabled disabled
onClick={() => { onClick={() => {

View File

@@ -19,7 +19,7 @@ function AisleCreator() {
const { activeLayer } = useActiveLayer(); const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { aisleStore, undoRedo2DStore, versionStore } = useSceneContext(); const { aisleStore, undoRedo2DStore, versionStore } = useSceneContext();
const { addAisle, getAislePointById } = aisleStore(); const { addAisle, getAislePointById, getConnectedPoints } = aisleStore();
const { push2D } = undoRedo2DStore(); const { push2D } = undoRedo2DStore();
const drag = useRef(false); const drag = useRef(false);
const isLeftMouseDown = useRef(false); const isLeftMouseDown = useRef(false);
@@ -76,7 +76,9 @@ function AisleCreator() {
newPoint.layer = snappedPoint.layer; 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; return;
} }
@@ -194,7 +196,32 @@ function AisleCreator() {
canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext); 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 ( return (
<> <>

View File

@@ -86,6 +86,10 @@ function FloorCreator() {
return; return;
} }
if (tempPoints.length <= 2 && pointIntersects && pointIntersects.object.uuid === tempPoints[0]?.pointUuid) {
return;
}
if (snappedPosition && !snappedPoint) { if (snappedPosition && !snappedPoint) {
newPoint.position = snappedPosition; newPoint.position = snappedPosition;
} }
@@ -288,7 +292,7 @@ function FloorCreator() {
canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext); 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 ( return (
<> <>

View File

@@ -1,16 +1,16 @@
import * as THREE from 'three'; import * as THREE from "three";
import { useCallback } from 'react'; import { useCallback } from "react";
import { useSceneContext } from '../../../scene/sceneContext'; import { useSceneContext } from "../../../scene/sceneContext";
const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
const CAN_POINT_SNAP = true; // Whether snapping is enabled or not 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 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 { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext();
const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore(); const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore();
const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore(); const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore();
@@ -21,350 +21,359 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
const getAllOtherWallPoints = useCallback(() => { const getAllOtherWallPoints = useCallback(() => {
if (!currentPoint) return []; if (!currentPoint) return [];
return walls.flatMap(wall => return walls.flatMap((wall) => wall.points.filter((point) => point.pointUuid !== currentPoint.uuid));
wall.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [walls, currentPoint]); }, [walls, currentPoint]);
const snapWallPoint = useCallback((position: [number, number, number], tempPoints?: Point) => { const snapWallPoint = useCallback(
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; (position: [number, number, number]) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const otherPoints = getAllOtherWallPoints(); const otherPoints = getAllOtherWallPoints();
if (tempPoints) { const currentVec = new THREE.Vector3(...position);
otherPoints.push(tempPoints); for (const point of otherPoints) {
} const pointVec = new THREE.Vector3(...point.position);
const currentVec = new THREE.Vector3(...position); const distance = currentVec.distanceTo(pointVec);
for (const point of otherPoints) { if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Wall") {
const pointVec = new THREE.Vector3(...point.position); return { position: point.position, isSnapped: true, snappedPoint: point };
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 };
return { position: position, isSnapped: false, snappedPoint: null }; },
}, [currentPoint, getAllOtherWallPoints]); [currentPoint, getAllOtherWallPoints]
);
const snapWallAngle = useCallback((newPosition: [number, number, number]): { const snapWallAngle = useCallback(
position: [number, number, number], (newPosition: [number, number, number]): { position: [number, number, number]; isSnapped: boolean; snapSources: THREE.Vector3[] } => {
isSnapped: boolean, if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
snapSources: THREE.Vector3[]
} => { const connectedPoints = getConnectedWallPoints(currentPoint.uuid);
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] }; 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 { return {
position: newPosition, position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped: false, isSnapped,
snapSources: [] snapSources,
}; };
} },
[currentPoint, getConnectedAislePoints]
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]);
// Aisle Snapping // Aisle Snapping
const getAllOtherAislePoints = useCallback(() => { const getAllOtherAislePoints = useCallback(() => {
if (!currentPoint) return []; if (!currentPoint) return [];
return aisles.flatMap(aisle => return aisles.flatMap((aisle) => aisle.points.filter((point) => point.pointUuid !== currentPoint.uuid));
aisle.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [aisles, currentPoint]); }, [aisles, currentPoint]);
const snapAislePoint = useCallback((position: [number, number, number]) => { const snapAislePoint = useCallback(
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; (position: [number, number, number]) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const otherPoints = getAllOtherAislePoints(); const otherPoints = getAllOtherAislePoints();
const currentVec = new THREE.Vector3(...position); const currentVec = new THREE.Vector3(...position);
for (const point of otherPoints) { for (const point of otherPoints) {
const pointVec = new THREE.Vector3(...point.position); const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec); const distance = currentVec.distanceTo(pointVec);
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Aisle') { if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Aisle") {
return { position: point.position, isSnapped: true, snappedPoint: point }; return { position: point.position, isSnapped: true, snappedPoint: point };
}
} }
}
return { position: position, isSnapped: false, snappedPoint: null }; return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherAislePoints]); },
[currentPoint, getAllOtherAislePoints]
);
const snapAisleAngle = useCallback((newPosition: [number, number, number]): { const snapAisleAngle = useCallback(
position: [number, number, number], (newPosition: [number, number, number]): { position: [number, number, number]; isSnapped: boolean; snapSources: THREE.Vector3[] } => {
isSnapped: boolean, if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
snapSources: THREE.Vector3[]
} => { const connectedPoints = getConnectedAislePoints(currentPoint.uuid);
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] }; 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 { return {
position: newPosition, position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped: false, isSnapped,
snapSources: [] snapSources,
}; };
} },
[currentPoint, getConnectedAislePoints]
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]);
// Floor Snapping // Floor Snapping
const getAllOtherFloorPoints = useCallback(() => { const getAllOtherFloorPoints = useCallback(() => {
if (!currentPoint) return []; if (!currentPoint) return [];
return floors.flatMap(floor => return floors.flatMap((floor) => floor.points.filter((point) => point.pointUuid !== currentPoint.uuid));
floor.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [floors, currentPoint]); }, [floors, currentPoint]);
const snapFloorPoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => { const snapFloorPoint = useCallback(
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; (position: [number, number, number], tempPoints?: Point[] | []) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
let otherPoints = getAllOtherFloorPoints(); let otherPoints = getAllOtherFloorPoints();
if (tempPoints) { if (tempPoints) {
otherPoints = [...otherPoints, ...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 };
} }
} const currentVec = new THREE.Vector3(...position);
return { position: position, isSnapped: false, snappedPoint: null }; for (const point of otherPoints) {
}, [currentPoint, getAllOtherFloorPoints]); const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
const snapFloorAngle = useCallback((newPosition: [number, number, number]): { if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Floor") {
position: [number, number, number], return { position: point.position, isSnapped: true, snappedPoint: point };
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) { return { position: position, isSnapped: false, snappedPoint: null };
if (!closestZ || zDist < closestZ.dist) { },
closestZ = { pos: cPos, dist: zDist }; [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 snappedPos = newPos.clone();
const snapSources: THREE.Vector3[] = []; const snapSources: THREE.Vector3[] = [];
if (closestX) { if (closestX) {
snappedPos.x = closestX.pos.x; snappedPos.x = closestX.pos.x;
snapSources.push(closestX.pos.clone()); snapSources.push(closestX.pos.clone());
} }
if (closestZ) { if (closestZ) {
snappedPos.z = closestZ.pos.z; snappedPos.z = closestZ.pos.z;
snapSources.push(closestZ.pos.clone()); snapSources.push(closestZ.pos.clone());
} }
const isSnapped = snapSources.length > 0; const isSnapped = snapSources.length > 0;
return { return {
position: [snappedPos.x, snappedPos.y, snappedPos.z], position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped, isSnapped,
snapSources snapSources,
}; };
}, [currentPoint, getConnectedFloorPoints]); },
[currentPoint, getConnectedFloorPoints]
);
// Zone Snapping // Zone Snapping
const getAllOtherZonePoints = useCallback(() => { const getAllOtherZonePoints = useCallback(() => {
if (!currentPoint) return []; if (!currentPoint) return [];
return zones.flatMap(zone => return zones.flatMap((zone) => zone.points.filter((point) => point.pointUuid !== currentPoint.uuid));
zone.points.filter(point => point.pointUuid !== currentPoint.uuid)
);
}, [zones, currentPoint]); }, [zones, currentPoint]);
const snapZonePoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => { const snapZonePoint = useCallback(
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; (position: [number, number, number], tempPoints?: Point[] | []) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
let otherPoints = getAllOtherZonePoints(); let otherPoints = getAllOtherZonePoints();
if (tempPoints) { if (tempPoints) {
otherPoints = [...otherPoints, ...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 };
} }
} const currentVec = new THREE.Vector3(...position);
return { position: position, isSnapped: false, snappedPoint: null }; for (const point of otherPoints) {
}, [currentPoint, getAllOtherZonePoints]); const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
const snapZoneAngle = useCallback((newPosition: [number, number, number]): { if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Zone") {
position: [number, number, number], return { position: point.position, isSnapped: true, snappedPoint: point };
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) { return { position: position, isSnapped: false, snappedPoint: null };
if (!closestZ || zDist < closestZ.dist) { },
closestZ = { pos: cPos, dist: zDist }; [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 snappedPos = newPos.clone();
const snapSources: THREE.Vector3[] = []; const snapSources: THREE.Vector3[] = [];
if (closestX) { if (closestX) {
snappedPos.x = closestX.pos.x; snappedPos.x = closestX.pos.x;
snapSources.push(closestX.pos.clone()); snapSources.push(closestX.pos.clone());
} }
if (closestZ) { if (closestZ) {
snappedPos.z = closestZ.pos.z; snappedPos.z = closestZ.pos.z;
snapSources.push(closestZ.pos.clone()); snapSources.push(closestZ.pos.clone());
} }
const isSnapped = snapSources.length > 0; const isSnapped = snapSources.length > 0;
return { return {
position: [snappedPos.x, snappedPos.y, snappedPos.z], position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped, isSnapped,
snapSources snapSources,
}; };
}, [currentPoint, getConnectedZonePoints]); },
[currentPoint, getConnectedZonePoints]
);
return { return {
snapAislePoint, snapAislePoint,
@@ -376,4 +385,4 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
snapZonePoint, snapZonePoint,
snapZoneAngle, snapZoneAngle,
}; };
}; };

View File

@@ -1,151 +1,55 @@
import { useMemo } from 'react'; import { useMemo } from "react";
import * as turf from '@turf/turf'; import * as turf from "@turf/turf";
export function useWallClassification(walls: Walls) { export function useWallClassification(walls: Walls) {
const findRooms = () => { const findRooms = () => {
if (walls.length < 3) return []; if (walls.length < 3) return [];
// Map pointUuid to list of connected line segments const wallSet = new Map<string, Wall>();
const pointMap = new Map<string, Wall[]>();
const makeKey = (p1: Point, p2: Point) => {
return [p1.pointUuid, p2.pointUuid].sort().join("-");
};
for (const wall of walls) { for (const wall of walls) {
for (const point of wall.points) { const [p1, p2] = wall.points;
const list = pointMap.get(point.pointUuid) || [];
list.push(wall); if (p1.pointUuid === p2.pointUuid || (p1.position[0] === p2.position[0] && p1.position[1] === p2.position[1] && p1.position[2] === p2.position[2])) {
pointMap.set(point.pointUuid, list); continue;
}
const key = makeKey(p1, p2);
if (!wallSet.has(key)) {
wallSet.set(key, wall);
} }
} }
// Create graph of connected walls using pointUuid const uniqueWalls = Array.from(wallSet.values());
const visited = new Set<string>();
const mergedLineStrings = [];
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 collection = turf.featureCollection(lineStrings);
const key = wallKey(wall.points[0], wall.points[1]);
if (visited.has(key) || visited.has(wallKey(wall.points[1], wall.points[0]))) continue;
let line: Point[] = [...wall.points]; const polygons = turf.polygonize(collection);
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 rooms: Point[][] = []; const rooms: Point[][] = [];
polygons.features.forEach(feature => { polygons.features.forEach((feature) => {
if (feature.geometry.type === 'Polygon') { if (feature.geometry.type === "Polygon") {
const coordinates = feature.geometry.coordinates[0]; const coords = feature.geometry.coordinates[0];
const roomPoints: Point[] = []; const roomPoints: Point[] = [];
for (const [x, z] of coordinates) { for (const [x, z] of coords) {
const matchingPoint = walls.flatMap(wall => wall.points) 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));
.find(p =>
p.position[0].toFixed(10) === x.toFixed(10) &&
p.position[2].toFixed(10) === z.toFixed(10)
);
if (matchingPoint) { if (matchingPoint) {
roomPoints.push(matchingPoint); roomPoints.push(matchingPoint);
} }
} }
if (roomPoints.length > 0 && if (roomPoints.length > 0 && roomPoints[0].pointUuid !== roomPoints[roomPoints.length - 1].pointUuid) {
roomPoints[0].pointUuid !== roomPoints[roomPoints.length - 1].pointUuid) {
roomPoints.push(roomPoints[0]); roomPoints.push(roomPoints[0]);
} }
@@ -161,12 +65,14 @@ export function useWallClassification(walls: Walls) {
const rooms = useMemo(() => findRooms(), [walls]); const rooms = useMemo(() => findRooms(), [walls]);
const findWallType = (wall: Wall) => { const findWallType = (wall: Wall) => {
const containingRooms = rooms.filter(room => { const containingRooms = rooms.filter((room) => {
for (let i = 0; i < room.length - 1; i++) { for (let i = 0; i < room.length - 1; i++) {
const p1 = room[i]; const p1 = room[i];
const p2 = room[i + 1]; const p2 = room[i + 1];
if ((wall.points[0].pointUuid === p1.pointUuid && wall.points[1].pointUuid === p2.pointUuid) || if (
(wall.points[0].pointUuid === p2.pointUuid && wall.points[1].pointUuid === p1.pointUuid)) { (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; return true;
} }
} }
@@ -175,18 +81,18 @@ export function useWallClassification(walls: Walls) {
if (containingRooms.length === 0) { if (containingRooms.length === 0) {
return { return {
type: 'segment', type: "segment",
rooms: [] rooms: [],
}; };
} else if (containingRooms.length === 1) { } else if (containingRooms.length === 1) {
return { return {
type: 'room', type: "room",
rooms: containingRooms rooms: containingRooms,
}; };
} else { } else {
return { return {
type: 'rooms', type: "rooms",
rooms: containingRooms rooms: containingRooms,
}; };
} }
}; };
@@ -197,16 +103,16 @@ export function useWallClassification(walls: Walls) {
const isRoomWall = (wall: Wall): boolean => { const isRoomWall = (wall: Wall): boolean => {
const type = findWallType(wall).type; const type = findWallType(wall).type;
return type === 'room' || type === 'rooms'; return type === "room" || type === "rooms";
}; };
const isSegmentWall = (wall: Wall): boolean => { const isSegmentWall = (wall: Wall): boolean => {
return findWallType(wall).type === 'segment'; return findWallType(wall).type === "segment";
}; };
const isWallFlipped = (wall: Wall): boolean => { const isWallFlipped = (wall: Wall): boolean => {
const wallType = findWallType(wall); const wallType = findWallType(wall);
if (wallType.type === 'segment') return false; if (wallType.type === "segment") return false;
for (const room of wallType.rooms) { for (const room of wallType.rooms) {
for (let i = 0; i < room.length - 1; i++) { for (let i = 0; i < room.length - 1; i++) {
@@ -223,13 +129,12 @@ export function useWallClassification(walls: Walls) {
return false; return false;
}; };
return { return {
rooms, rooms,
getWallType, getWallType,
isRoomWall, isRoomWall,
isSegmentWall, isSegmentWall,
findRooms, findRooms,
isWallFlipped isWallFlipped,
}; };
} }

View File

@@ -27,7 +27,7 @@ function WallInstances() {
const { rooms } = useWallClassification(walls); const { rooms } = useWallClassification(walls);
useEffect(() => { useEffect(() => {
// console.log('walls: ', walls); // console.log("walls: ", walls);
}, [walls]); }, [walls]);
useEffect(() => { useEffect(() => {

View File

@@ -1,12 +1,12 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from "react";
import * as THREE from 'three'; import * as THREE from "three";
import { useFrame, useThree } from '@react-three/fiber'; import { useFrame, useThree } from "@react-three/fiber";
import { Html } from '@react-three/drei'; import { Html } from "@react-three/drei";
import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store'; import { useActiveLayer, useToolMode, useToggleView } from "../../../../store/builder/store";
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping'; import { useDirectionalSnapping } from "../../point/helpers/useDirectionalSnapping";
import { usePointSnapping } from '../../point/helpers/usePointSnapping'; import { usePointSnapping } from "../../point/helpers/usePointSnapping";
import ReferenceLine from '../../line/reference/referenceLine'; import ReferenceLine from "../../line/reference/referenceLine";
interface ReferenceWallProps { interface ReferenceWallProps {
tempPoints: Point[]; tempPoints: Point[];
@@ -26,10 +26,10 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position); const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[0]?.position || null); 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(() => { useFrame(() => {
if (toolMode === 'Wall' && toggleView && tempPoints.length === 1) { if (toolMode === "Wall" && toggleView && tempPoints.length === 1) {
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3(); const intersectionPoint = new THREE.Vector3();
raycaster.ray.intersectPlane(plane, intersectionPoint); raycaster.ray.intersectPlane(plane, intersectionPoint);
@@ -37,7 +37,7 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
if (!intersectionPoint) return; 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) { if (snapped.isSnapped && snapped.snappedPoint) {
finalPosition.current = snapped.position; finalPosition.current = snapped.position;
@@ -58,23 +58,22 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
const wallPoints: [Point, Point] = [ const wallPoints: [Point, Point] = [
tempPoints[0], tempPoints[0],
{ {
pointUuid: 'temp-point', pointUuid: "temp-point",
pointType: 'Wall', pointType: "Wall",
position: finalPosition.current, position: finalPosition.current,
layer: activeLayer, layer: activeLayer,
} },
]; ];
setTempWall({ setTempWall({
wallUuid: 'temp-wall', wallUuid: "temp-wall",
points: wallPoints, points: wallPoints,
outsideMaterial: 'default', outsideMaterial: "default",
insideMaterial: 'default', insideMaterial: "default",
wallThickness: wallThickness, wallThickness: wallThickness,
wallHeight: wallHeight, wallHeight: wallHeight,
decals: [] decals: [],
}) });
} else if (tempWall !== null) { } else if (tempWall !== null) {
setTempWall(null); setTempWall(null);
} }
@@ -87,9 +86,7 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
if (!tempWall) return null; if (!tempWall) return null;
const renderWall = () => { const renderWall = () => {
return ( return <ReferenceLine points={tempWall.points} />;
<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); 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> </Html>
)} )}
</> </>
) );
} };
return ( return (
<group name="Wall-Reference-Group"> <group name="Wall-Reference-Group">
@@ -124,4 +121,4 @@ function ReferenceWall({ tempPoints }: Readonly<ReferenceWallProps>) {
); );
} }
export default ReferenceWall; export default ReferenceWall;

View File

@@ -22,7 +22,7 @@ function WallCreator() {
const { activeLayer } = useActiveLayer(); const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { wallStore, undoRedo2DStore, versionStore } = useSceneContext(); const { wallStore, undoRedo2DStore, versionStore } = useSceneContext();
const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore(); const { addWall, getWallPointById, removeWall, getWallByPoints, getConnectedPoints } = wallStore();
const { push2D } = undoRedo2DStore(); const { push2D } = undoRedo2DStore();
const drag = useRef(false); const drag = useRef(false);
const isLeftMouseDown = useRef(false); const isLeftMouseDown = useRef(false);
@@ -379,7 +379,9 @@ function WallCreator() {
newPoint.layer = snappedPoint.layer; 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; return;
} }
@@ -485,7 +487,29 @@ function WallCreator() {
canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext); 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 ( return (
<> <>

View File

@@ -86,6 +86,10 @@ function ZoneCreator() {
return; return;
} }
if (tempPoints.length <= 2 && pointIntersects && pointIntersects.object.uuid === tempPoints[0]?.pointUuid) {
return;
}
if (snappedPosition && !snappedPoint) { if (snappedPosition && !snappedPoint) {
newPoint.position = snappedPosition; newPoint.position = snappedPosition;
} }
@@ -282,7 +286,7 @@ function ZoneCreator() {
canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext); 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 ( return (
<> <>

View File

@@ -1,9 +1,9 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import * as THREE from 'three'; import * as THREE from "three";
import { PerspectiveCamera, OrthographicCamera, CameraControls } from '@react-three/drei'; import { PerspectiveCamera, OrthographicCamera, CameraControls } from "@react-three/drei";
import { useParams } from "react-router-dom"; 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 { getCameraApi } from "../../../services/factoryBuilder/camera/getCameraApi";
import { useToggleView } from "../../../store/builder/store"; import { useToggleView } from "../../../store/builder/store";
@@ -18,19 +18,21 @@ export default function SwitchView() {
(controls as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse; (controls as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
} else { } else {
if (!projectId) return; if (!projectId) return;
getCameraApi(projectId).then((data) => { getCameraApi(projectId)
if (data?.position && data?.target) { .then((data) => {
(controls as CameraControls)?.setPosition(data.position.x, data.position.y, data.position.z); if (data?.position && data?.target) {
(controls as CameraControls)?.setTarget(data.target.x, data.target.y, data.target.z); (controls as CameraControls)?.setPosition(data.position.x, data.position.y, data.position.z);
} else { (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)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget); (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) { if (controls) {
(controls as any).mouseButtons.left = CONSTANTS.threeDimension.leftMouse; (controls as any).mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
@@ -62,4 +64,4 @@ export default function SwitchView() {
)} )}
</> </>
); );
} }

View File

@@ -59,7 +59,12 @@ export default function Controls() {
controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth); controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth);
if (!socket?.connected) { 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 { } else {
const camData = { const camData = {
organization, organization,
@@ -143,7 +148,6 @@ export default function Controls() {
boundaryEnclosesCamera={true} boundaryEnclosesCamera={true}
dollyDragInverted dollyDragInverted
> >
<SwitchView /> <SwitchView />
<CamMode /> <CamMode />

View File

@@ -77,7 +77,7 @@ export const createVersionStore = () => {
setVersionName: (versionId: string, versionName: string) => { setVersionName: (versionId: string, versionName: string) => {
set((state) => { set((state) => {
const version = state.versionHistory.find((v) => v.versionId === versionId); const version = get().getVersionById(versionId);
if (version) { if (version) {
version.versionName = versionName; version.versionName = versionName;
} }
@@ -86,7 +86,7 @@ export const createVersionStore = () => {
updateVersion: (versionId: string, versionName: string, versionDescription: string) => { updateVersion: (versionId: string, versionName: string, versionDescription: string) => {
set((state) => { set((state) => {
const version = state.versionHistory.find((v) => v.versionId === versionId); const version = get().getVersionById(versionId);
if (version) { if (version) {
version.versionName = versionName; version.versionName = versionName;
version.versionDescription = versionDescription; version.versionDescription = versionDescription;

View File

@@ -230,10 +230,6 @@ const KeyPressListener: React.FC = () => {
setShowShortcuts(!showShortcuts); setShowShortcuts(!showShortcuts);
} }
// if (keyCombination === "Ctrl+Shift+P") {
// pref
// }
if (keyCombination === "U") { if (keyCombination === "U") {
setViewSceneLabels((prev) => !prev); setViewSceneLabels((prev) => !prev);
} }