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;