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 {
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={() => {

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
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
@@ -10,7 +10,7 @@ const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping i
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,34 +21,29 @@ 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) => {
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') {
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]);
},
[currentPoint, getAllOtherWallPoints]
);
const snapWallAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
} => {
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);
@@ -56,13 +51,13 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
return {
position: newPosition,
isSnapped: false,
snapSources: []
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;
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);
@@ -101,21 +96,22 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
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]) => {
const snapAislePoint = useCallback(
(position: [number, number, number]) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
const otherPoints = getAllOtherAislePoints();
@@ -123,19 +119,18 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
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') {
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]);
},
[currentPoint, getAllOtherAislePoints]
);
const snapAisleAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
} => {
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);
@@ -143,13 +138,13 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
return {
position: newPosition,
isSnapped: false,
snapSources: []
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;
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);
@@ -188,21 +183,22 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
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[] | []) => {
const snapFloorPoint = useCallback(
(position: [number, number, number], tempPoints?: Point[] | []) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
let otherPoints = getAllOtherFloorPoints();
@@ -215,18 +211,23 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Floor') {
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Floor") {
return { position: point.position, isSnapped: true, snappedPoint: point };
}
}
return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherFloorPoints]);
},
[currentPoint, getAllOtherFloorPoints]
);
const snapFloorAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
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: [] };
@@ -236,8 +237,8 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
}
const newPos = new THREE.Vector3(...newPosition);
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
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);
@@ -275,21 +276,22 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
snapSources,
};
}, [currentPoint, getConnectedFloorPoints]);
},
[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[] | []) => {
const snapZonePoint = useCallback(
(position: [number, number, number], tempPoints?: Point[] | []) => {
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
let otherPoints = getAllOtherZonePoints();
@@ -302,18 +304,23 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
const pointVec = new THREE.Vector3(...point.position);
const distance = currentVec.distanceTo(pointVec);
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Zone') {
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === "Zone") {
return { position: point.position, isSnapped: true, snappedPoint: point };
}
}
return { position: position, isSnapped: false, snappedPoint: null };
}, [currentPoint, getAllOtherZonePoints]);
},
[currentPoint, getAllOtherZonePoints]
);
const snapZoneAngle = useCallback((newPosition: [number, number, number]): {
position: [number, number, number],
isSnapped: boolean,
snapSources: THREE.Vector3[]
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: [] };
@@ -323,8 +330,8 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
}
const newPos = new THREE.Vector3(...newPosition);
let closestX: { pos: THREE.Vector3, dist: number } | null = null;
let closestZ: { pos: THREE.Vector3, dist: number } | null = null;
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);
@@ -362,9 +369,11 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string
return {
position: [snappedPos.x, snappedPos.y, snappedPos.z],
isSnapped,
snapSources
snapSources,
};
}, [currentPoint, getConnectedZonePoints]);
},
[currentPoint, getConnectedZonePoints]
);
return {
snapAislePoint,

View File

@@ -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>();
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);
}
}
// Create graph of connected walls using pointUuid
const visited = new Set<string>();
const mergedLineStrings = [];
const wallKey = (p1: Point, p2: Point) => `${p1.pointUuid}-${p2.pointUuid}`;
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;
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,
},
const makeKey = (p1: Point, p2: Point) => {
return [p1.pointUuid, p2.pointUuid].sort().join("-");
};
for (const wall of walls) {
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;
}
return null;
}).filter(Boolean);
const key = makeKey(p1, p2);
if (!wallSet.has(key)) {
wallSet.set(key, wall);
}
}
if (validLineStrings.length === 0) return [];
const uniqueWalls = Array.from(wallSet.values());
const lineStrings = turf.featureCollection(validLineStrings as any);
const polygons = turf.polygonize(lineStrings as any);
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 });
});
const collection = turf.featureCollection(lineStrings);
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,
};
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,7 +18,8 @@ export default function SwitchView() {
(controls as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
} else {
if (!projectId) return;
getCameraApi(projectId).then((data) => {
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);
@@ -26,11 +27,12 @@ export default function SwitchView() {
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
}
}).catch(() => {
})
.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;

View File

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

View File

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

View File

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