completed floor

This commit is contained in:
2025-06-27 15:44:31 +05:30
parent 64f0cdb148
commit c73bdf4556
22 changed files with 1422 additions and 1159 deletions

View File

@@ -1,339 +1,208 @@
import React, { useRef, useState } from "react";
import {
Vector3,
Raycaster,
BufferGeometry,
LineBasicMaterial,
Line,
Mesh,
Group,
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<Mesh>;
object: number;
boundingBoxRef: React.RefObject<Mesh>;
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,
boundingBoxRef,
object,
}: DistanceFindingControlsProps) => {
const { camera, scene } = useThree();
const [labelValues, setLabelValues] = useState<{
textPosX: any;
textNegX: any;
textPosZ: any;
textNegZ: any;
}>({
textPosX: "",
textNegX: "",
textPosZ: "",
textNegZ: "",
});
const { camera, scene } = useThree();
const [labelValues, setLabelValues] = useState<Record<string, string>>({
textPosX: "",
textNegX: "",
textPosZ: "",
textNegZ: "",
});
// Refs for measurement lines
const line1 = useRef<Line>(null);
const line2 = useRef<Line>(null);
const line3 = useRef<Line>(null);
const line4 = useRef<Line>(null);
const line5 = useRef<Line>(null);
// Refs for measurement text labels
const textPosX = useRef<Group>(null);
const textNegX = useRef<Group>(null);
const textPosZ = useRef<Group>(null);
const textNegZ = useRef<Group>(null);
const textPosY = useRef<Group>(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 lineRefs = {
posX: useRef<Line>(null),
negX: useRef<Line>(null),
posZ: useRef<Line>(null),
negZ: useRef<Line>(null),
posY: useRef<Line>(null),
};
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone();
const textRefs = {
textPosX: useRef<Group>(null),
textNegX: useRef<Group>(null),
textPosZ: useRef<Group>(null),
textNegZ: useRef<Group>(null),
textPosY: useRef<Group>(null),
};
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,
const lineGeometries = useRef({
posX: new BufferGeometry(),
negX: new BufferGeometry(),
posZ: new BufferGeometry(),
negZ: new BufferGeometry(),
posY: new BufferGeometry(),
});
updateLine({
line: line2.current,
geometry: lineGeometries.current.negX,
direction: new Vector3(-1, 0, 0), // Negative X
angle: "neg",
mesh: textNegX,
vec,
size,
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);
});
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<Group>;
vec: Vector3;
size: { x: number; y: number; z: number };
}) => {
if (!line) return;
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];
const points = [];
if (!line) return;
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 points: Vector3[] = [];
const halfSize = new Vector3(size.x / 2, size.y / 2, size.z / 2);
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;
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));
}
// Find intersection points
const wallsGroup = scene.children.find((val) =>
val?.name.includes("Walls")
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) => (
<group name={id} ref={textRefs[id]}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues[id] === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id={id}>{labelValues[id]}</div>
</Html>
</group>
);
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;
}
}
}
if (points[1]) {
geometry.dispose();
geometry.setFromPoints([points[0], points[1]]);
line.geometry = geometry;
// Calculate the distance only once
const distance = points[0].distanceTo(points[1]).toFixed(2);
// 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 = `${distance}m`;
// Update specific label state based on the label ID
switch (label.id) {
case "textPosX":
setLabelValues((prevState) => ({ ...prevState, textPosX: distance }));
break;
case "textNegX":
setLabelValues((prevState) => ({ ...prevState, textNegX: distance }));
break;
case "textPosZ":
setLabelValues((prevState) => ({ ...prevState, textPosZ: distance }));
break;
case "textNegZ":
setLabelValues((prevState) => ({ ...prevState, textNegZ: distance }));
break;
default:
break;
}
}
}
} 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 = "";
// Clear the corresponding label value in the state
switch (label.id) {
case "textPosX":
setLabelValues((prevState) => ({ ...prevState, textPosX: "" }));
break;
case "textNegX":
setLabelValues((prevState) => ({ ...prevState, textNegX: "" }));
break;
case "textPosZ":
setLabelValues((prevState) => ({ ...prevState, textPosZ: "" }));
break;
case "textNegZ":
setLabelValues((prevState) => ({ ...prevState, textNegZ: "" }));
break;
default:
break;
}
}
}
};
const Material = new LineBasicMaterial({ color: "#d2baff" });
return (
<>
{/* Measurement text labels */}
{boundingBoxRef.current && object > 0 && (
return (
<>
<group name="textPosX" ref={textPosX}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textPosX === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id="textPosX">{labelValues.textPosX}</div>
</Html>
</group>
{boundingBoxRef.current && object > 0 && (
<group
name="DistanceFindingControls"
>
{renderLabel("textPosX")}
{renderLabel("textNegX")}
{renderLabel("textPosZ")}
{renderLabel("textNegZ")}
<group name="textNegX" ref={textNegX}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textNegX === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id="textNegX">{labelValues.textNegX}</div>
</Html>
</group>
<group name="textPosZ" ref={textPosZ}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[2, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textPosZ === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id="textPosZ">{labelValues.textPosZ}</div>
</Html>
</group>
<group name="textNegZ" ref={textNegZ}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{
pointerEvents: "none",
visibility: labelValues.textNegZ === "" ? "hidden" : "visible",
}}
>
<div className="distance-label" id="textNegZ">{labelValues.textNegZ}</div>
</Html>
</group>
{/* Measurement lines */}
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line1}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line2}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line3}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line4}
/>
<primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.posX} />
<primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.negX} />
<primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.posZ} />
<primitive object={new Line(new BufferGeometry(), material)} ref={lineRefs.negZ} />
</group>
)}
</>
)
}
</>
);
);
};
export default DistanceFindingControls;