diff --git a/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx b/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx new file mode 100644 index 0000000..2c9c7b4 --- /dev/null +++ b/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx @@ -0,0 +1,253 @@ +import React, { useEffect, useRef } from "react"; +import { Vector3, Raycaster, BufferGeometry, LineBasicMaterial, Line, Object3D, Mesh } from "three"; +import { useThree, useFrame } from "@react-three/fiber"; +import { Group } from "three"; +import { Html } from "@react-three/drei"; +import * as THREE from "three"; + +interface DistanceFindingControlsProps { + boundingBoxRef: React.RefObject; +} + +const DistanceFindingControls = ({ boundingBoxRef }: DistanceFindingControlsProps) => { + const { camera, scene } = useThree(); + + + // Refs for measurement lines + const line1 = useRef(null); + const line2 = useRef(null); + const line3 = useRef(null); + const line4 = useRef(null); + const line5 = useRef(null); + + // Refs for measurement text labels + const textPosX = useRef(null); + const textNegX = useRef(null); + const textPosZ = useRef(null); + const textNegZ = useRef(null); + const textPosY = useRef(null); + + // Store line geometries to avoid recreation + const lineGeometries = useRef({ + posX: new BufferGeometry(), + negX: new BufferGeometry(), + posZ: new BufferGeometry(), + negZ: new BufferGeometry(), + posY: new BufferGeometry() + }); + + + useFrame(() => { + if (!boundingBoxRef?.current) return; + + boundingBoxRef.current.geometry.computeBoundingBox(); + const bbox = boundingBoxRef.current.geometry.boundingBox; + + if (!bbox) return; + + const size = { + x: bbox.max.x - bbox.min.x, + y: bbox.max.y - bbox.min.y, + z: bbox.max.z - bbox.min.z + }; + + const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone(); + + if (!vec) return; + updateLine({ + line: line1.current, + geometry: lineGeometries.current.posX, + direction: new Vector3(1, 0, 0), // Positive X + angle: 'pos', + mesh: textPosX, + vec, + size + }); + updateLine({ + line: line2.current, + geometry: lineGeometries.current.negX, + direction: new Vector3(-1, 0, 0), // Negative X + angle: 'neg', + mesh: textNegX, + vec, + size + }); + updateLine({ + line: line3.current, + geometry: lineGeometries.current.posZ, + direction: new Vector3(0, 0, 1), // Positive Z + angle: 'pos', + mesh: textPosZ, + vec, + size + }); + updateLine({ + line: line4.current, + geometry: lineGeometries.current.negZ, + direction: new Vector3(0, 0, -1), // Negative Z + angle: 'neg', + mesh: textNegZ, + vec, + size + }); + updateLine({ + line: line5.current, + geometry: lineGeometries.current.posY, + direction: new Vector3(0, -1, 0), // Down (Y) + angle: 'posY', + mesh: textPosY, + vec, + size + }); + }); + + const updateLine = ({ + line, + geometry, + direction, + angle, + mesh, + vec, + size + }: { + line: Line | null, + geometry: BufferGeometry, + direction: Vector3, + angle: string, + mesh: React.RefObject, + vec: Vector3, + size: { x: number, y: number, z: number } + }) => { + if (!line) return; + + const points = []; + + if (angle === "pos") { + points[0] = new Vector3(vec.x, vec.y, vec.z).add( + new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2) + ); + } else if (angle === "neg") { + points[0] = new Vector3(vec.x, vec.y, vec.z).sub( + new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2) + ); + } else if (angle === "posY") { + points[0] = new Vector3(vec.x, vec.y, vec.z).sub( + new Vector3(0, size.y / 2, 0) + ); + } + + const ray = new Raycaster(); + if (camera) ray.camera = camera; + ray.set(new Vector3(vec.x, vec.y, vec.z), direction); + ray.params.Line.threshold = 0.1; + + // Find intersection points + const wallsGroup = scene.children.find(val => val?.name.includes("Walls")); + const intersects = wallsGroup ? ray.intersectObjects([wallsGroup], true) : []; + + // Find intersection point + if (intersects[0]) { + for (const intersect of intersects) { + if (intersect.object.name.includes("Wall")) { + points[1] = angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor + break; + } + } + } + + // Update line geometry + if (points[1]) { + geometry.dispose(); + geometry.setFromPoints([points[0], points[1]]); + line.geometry = geometry; + + // Update measurement text + if (mesh?.current) { + geometry.computeBoundingSphere(); + const center = geometry.boundingSphere?.center; + if (center) { + mesh.current.position.copy(center); + } + const label = document.getElementById(mesh.current.name); + if (label) { + label.innerText = `${points[0].distanceTo(points[1]).toFixed(2)}m`; + } + } + } else { + // No intersection found - clear the line + geometry.dispose(); + geometry.setFromPoints([new Vector3(), new Vector3()]); + line.geometry = geometry; + const label = document.getElementById(mesh?.current?.name || ""); + if (label) label.innerText = ""; + } + }; + + const Material = new LineBasicMaterial({ color: "red" }); + + return ( + <> + {/* Measurement text labels */} + {boundingBoxRef && ( + <> + + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + {/* Measurement lines */} + + + + + + )} + + ); +}; + +export default DistanceFindingControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index 5b33c14..0d4d3cc 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -11,8 +11,10 @@ import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; import { snapControls } from "../../../../utils/handleSnap"; +import DistanceFindingControls from "./distanceFindingControls"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { + const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); @@ -23,7 +25,6 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const { socket } = useSocketStore(); const itemsData = useRef([]); const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("") - const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; @@ -156,17 +157,18 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje if (keyEvent === "Ctrl") { targetX = snapControls(targetX, "Ctrl"); targetZ = snapControls(targetZ, "Ctrl"); - } else if (keyEvent === "Ctrl+Shift") { - targetX = snapControls(targetX, "Ctrl+Shift"); - targetZ = snapControls(targetZ, "Ctrl+Shift"); - } else if (keyEvent === "Shift") { - targetX = snapControls(targetX, "Shift"); - targetZ = snapControls(targetZ, "Shift"); - } else { - } + // else if (keyEvent === "Ctrl+Shift") { + // targetX = snapControls(targetX, "Ctrl+Shift"); + // targetZ = snapControls(targetZ, "Ctrl+Shift"); + // } else if (keyEvent === "Shift") { + // targetX = snapControls(targetX, "Shift"); + // targetZ = snapControls(targetZ, "Shift"); + // } else { + // } const position = new THREE.Vector3(); + if (boundingBoxRef.current) { boundingBoxRef.current.getWorldPosition(position); selectionGroup.current.position.lerp( @@ -194,6 +196,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje } } } + }); @@ -215,7 +218,6 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje obj.position.copy(worldPosition); if (itemsGroupRef.current) { - const newFloorItem: Types.FloorItemType = { modelUuid: obj.uuid, modelName: obj.userData.name, @@ -311,7 +313,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje setKeyEvent("") } - return null; + return ; } export default MoveControls \ No newline at end of file