import React, { useRef, useState } from "react"; import { Vector3, Raycaster, BufferGeometry, LineBasicMaterial, Line, Mesh, Group, } from "three"; import { useThree, useFrame } from "@react-three/fiber"; import { Html } from "@react-three/drei"; interface DistanceFindingControlsProps { boundingBoxRef: React.RefObject; object: number; } const material = new LineBasicMaterial({ color: "#d2baff" }); const DIRECTION_LABEL_MAP = { textPosX: "textPosX", textNegX: "textNegX", textPosZ: "textPosZ", textNegZ: "textNegZ", } as const; const DistanceFindingControls = ({ boundingBoxRef, object, }: DistanceFindingControlsProps) => { const { camera, scene } = useThree(); const [labelValues, setLabelValues] = useState>({ textPosX: "", textNegX: "", textPosZ: "", textNegZ: "", }); const lineRefs = { posX: useRef(null), negX: useRef(null), posZ: useRef(null), negZ: useRef(null), posY: useRef(null), }; const textRefs = { textPosX: useRef(null), textNegX: useRef(null), textPosZ: useRef(null), textNegZ: useRef(null), textPosY: useRef(null), }; const lineGeometries = useRef({ posX: new BufferGeometry(), negX: new BufferGeometry(), posZ: new BufferGeometry(), negZ: new BufferGeometry(), posY: new BufferGeometry(), }); useFrame(() => { const bboxMesh = boundingBoxRef?.current; if (!bboxMesh) return; bboxMesh.geometry.computeBoundingBox(); const bbox = bboxMesh.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 center = bboxMesh.getWorldPosition(new Vector3()).clone(); if (!center) return; updateLine("posX", new Vector3(1, 0, 0), "pos", size, center); updateLine("negX", new Vector3(-1, 0, 0), "neg", size, center); updateLine("posZ", new Vector3(0, 0, 1), "pos", size, center); updateLine("negZ", new Vector3(0, 0, -1), "neg", size, center); updateLine("posY", new Vector3(0, -1, 0), "posY", size, center); }); const updateLine = ( key: keyof typeof lineRefs, direction: Vector3, angle: string, size: { x: number; y: number; z: number }, origin: Vector3 ) => { const line = lineRefs[key].current; const geometry = lineGeometries.current[key]; const mesh = textRefs[`text${key[0].toUpperCase() + key.slice(1)}` as keyof typeof textRefs]; if (!line) return; const points: Vector3[] = []; const halfSize = new Vector3(size.x / 2, size.y / 2, size.z / 2); if (angle === "pos") { points[0] = origin.clone().add(direction.clone().multiply(halfSize)); } else if (angle === "neg") { points[0] = origin.clone().sub(direction.clone().multiply(halfSize)); } else if (angle === "posY") { points[0] = origin.clone().sub(new Vector3(0, size.y / 2, 0)); } const ray = new Raycaster(); ray.camera = camera; ray.set(origin, direction); ray.params.Line.threshold = 0.1; const wallsGroup = scene.children.find((val) => val?.name.includes("Walls") ); const intersects = wallsGroup ? ray.intersectObjects([wallsGroup], true) : []; const intersect = intersects.find((i) => i.object.name.includes("Wall") ); if (intersect) { points[1] = angle !== "posY" ? intersect.point : new Vector3(origin.x, 0, origin.z); } if (points[1]) { geometry.dispose(); geometry.setFromPoints(points); line.geometry = geometry; const distance = points[0].distanceTo(points[1]).toFixed(2); if (mesh?.current) { geometry.computeBoundingSphere(); mesh.current.position.copy(geometry.boundingSphere!.center); const labelEl = document.getElementById(mesh.current.name); if (labelEl) { labelEl.innerText = `${distance}m`; if (DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) { setLabelValues((prev) => ({ ...prev, [labelEl.id]: distance, })); } } } } else { geometry.dispose(); geometry.setFromPoints([new Vector3(), new Vector3()]); line.geometry = geometry; const labelEl = document.getElementById(mesh?.current?.name ?? ""); if (labelEl && DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) { labelEl.innerText = ""; setLabelValues((prev) => ({ ...prev, [labelEl.id]: "", })); } } }; const renderLabel = (id: keyof typeof textRefs) => (
{labelValues[id]}
); return ( <> {boundingBoxRef.current && object > 0 && ( {renderLabel("textPosX")} {renderLabel("textNegX")} {renderLabel("textPosZ")} {renderLabel("textNegZ")} )} ); }; export default DistanceFindingControls;