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) => ( + + ))} ); }