From 4c9e838f934dffec7fdcab86360cc59b80e6cf87 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Thu, 21 May 2026 20:51:01 +0000
Subject: [PATCH] Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
---
src/components/three/ModelViewer.tsx | 111 +++++++++++++++------------
1 file changed, 63 insertions(+), 48 deletions(-)
diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx
index a46f032..47f0dbe 100644
--- a/src/components/three/ModelViewer.tsx
+++ b/src/components/three/ModelViewer.tsx
@@ -352,15 +352,70 @@ function MeasurementLabel({ position, text, variant, fixed = false }: { position
);
}
-/** Renders all measurements, snap point, and hover info */
+/** Renders a single measurement's geometry + label.
+ * Coordinates are interpreted in whichever frame the component is mounted in. */
+function MeasurementGraphic({ m }: { m: import('@/stores/useModelStore').Measurement }) {
+ const a: [number, number, number] = [m.pointA.x, m.pointA.y, m.pointA.z];
+ const b: [number, number, number] = [m.pointB.x, m.pointB.y, m.pointB.z];
+ const mid: [number, number, number] = [
+ (a[0] + b[0]) / 2,
+ (a[1] + b[1]) / 2,
+ (a[2] + b[2]) / 2,
+ ];
+
+ if (m.kind === 'hole') {
+ return (
+
+
+
+
+ );
+ }
+
+ if (m.kind === 'edge') {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+/** Renders measurements attached to a given model, in that model's local frame.
+ * Mounted inside GLBModel so they follow the model when it is repositioned. */
+function ModelMeasurements({ modelId }: { modelId: string }) {
+ const measurements = useModelStore((s) => s.measurements);
+ const attached = measurements.filter((m) => m.modelId === modelId);
+ if (attached.length === 0) return null;
+ return (
+ <>
+ {attached.map((m) => (
+
+ ))}
+ >
+ );
+}
+
+/** Renders snap indicator, hover tooltip, pending point and any *legacy*
+ * world-frame measurements that aren't attached to a specific model. */
function MeasurementOverlay() {
const measurements = useModelStore((s) => s.measurements);
const measurePoints = useModelStore((s) => s.measurePoints);
const snapPoint = useModelStore((s) => s.snapPoint);
const hoverInfo = useModelStore((s) => s.hoverInfo);
const measureMode = useModelStore((s) => s.measureMode);
- const scaleRatio = useModelStore((s) => s.scaleRatio);
- const factor = scaleRatio?.factor ?? 1;
return (
<>
@@ -381,55 +436,15 @@ function MeasurementOverlay() {
/>
)}
- {/* Pending first point */}
+ {/* Pending first point (always in world coords) */}
{measurePoints.length === 1 && (
)}
- {/* Completed measurements */}
- {measurements.map((m) => {
- const a: [number, number, number] = [m.pointA.x, m.pointA.y, m.pointA.z];
- const b: [number, number, number] = [m.pointB.x, m.pointB.y, m.pointB.z];
- const mid: [number, number, number] = [
- (a[0] + b[0]) / 2,
- (a[1] + b[1]) / 2,
- (a[2] + b[2]) / 2,
- ];
-
- if (m.kind === 'hole') {
- return (
-
-
-
-
- );
- }
-
- if (m.kind === 'edge') {
- return (
-
-
-
-
-
-
- );
- }
-
- return (
-
-
-
-
-
-
- );
- })}
+ {/* Legacy world-frame measurements (no modelId) */}
+ {measurements.filter((m) => !m.modelId).map((m) => (
+
+ ))}
>
);
}