-
-
+
+
+
);
}
+/** Computes a distanceFactor that keeps an Html label roughly constant on screen */
+function useLabelDistanceFactor(position: [number, number, number]): number {
+ const { camera, size } = useThree();
+ const v = useMemo(() => new THREE.Vector3(), []);
+ const ref = useRef(1);
+ useFrame(() => {
+ v.set(position[0], position[1], position[2]);
+ const dist = camera.position.distanceTo(v);
+ const perspCam = camera as THREE.PerspectiveCamera;
+ const fov = (perspCam.fov ?? 50) * (Math.PI / 180);
+ const worldPerPixel = (2 * dist * Math.tan(fov / 2)) / size.height;
+ // distanceFactor scales the html so it stays ~140 px tall at this distance
+ ref.current = Math.max(0.05, worldPerPixel * 140);
+ });
+ return ref.current;
+}
+
+function MeasurementLabel({ position, text, variant }: { position: [number, number, number]; text: string; variant: 'success' | 'primary' }) {
+ const df = useLabelDistanceFactor(position);
+ const borderClass = variant === 'success' ? 'border-success/60' : 'border-primary/60';
+ const textClass = variant === 'success' ? 'text-success' : 'text-primary';
+ return (
+
+
+
+ {text}
+
+
+
+ );
+}
+
/** Renders all measurements, snap point, and hover info */
function MeasurementOverlay() {
const measurements = useModelStore((s) => s.measurements);
@@ -194,6 +250,8 @@ function MeasurementOverlay() {
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 (
<>
@@ -204,14 +262,11 @@ function MeasurementOverlay() {
{/* Hover auto-detect tooltip */}
{hoverInfo && (
-
-
-
- {hoverInfo.type === 'hole' ? '⌀ ' : ''}
- {hoverInfo.value.toFixed(1)} mm
-
-
-
+
)}
{/* Pending first point */}
@@ -237,14 +292,9 @@ function MeasurementOverlay() {
points={[a, b]}
color="#22c55e"
lineWidth={2}
+ depthTest={false}
/>
-
-
-
- {m.distanceMM.toFixed(1)} mm
-
-
-
+