Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -167,26 +167,82 @@ function SceneModels() {
|
||||
);
|
||||
}
|
||||
|
||||
/** Sphere marker at a 3D point */
|
||||
/** Sphere marker at a 3D point — scaled per-frame to keep constant screen size */
|
||||
function PointMarker({ position, color = '#e8a838' }: { position: [number, number, number]; color?: string }) {
|
||||
const ref = useRef<THREE.Mesh>(null);
|
||||
const { camera, size } = useThree();
|
||||
useFrame(() => {
|
||||
if (!ref.current) return;
|
||||
const dist = camera.position.distanceTo(ref.current.position);
|
||||
const perspCam = camera as THREE.PerspectiveCamera;
|
||||
const fov = (perspCam.fov ?? 50) * (Math.PI / 180);
|
||||
const worldPerPixel = (2 * dist * Math.tan(fov / 2)) / size.height;
|
||||
// Target: ~6 px radius (radius = 1 in base geometry → scale = 6*wpp)
|
||||
const s = Math.max(0.0005, 6 * worldPerPixel);
|
||||
ref.current.scale.setScalar(s);
|
||||
});
|
||||
return (
|
||||
<mesh position={position}>
|
||||
<sphereGeometry args={[0.003, 16, 16]} />
|
||||
<meshBasicMaterial color={color} />
|
||||
<mesh ref={ref} position={position}>
|
||||
<sphereGeometry args={[1, 16, 16]} />
|
||||
<meshBasicMaterial color={color} depthTest={false} transparent opacity={0.95} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
/** Snap ring indicator */
|
||||
function SnapRing({ position }: { position: [number, number, number] }) {
|
||||
/** Snap ring indicator — scaled per-frame */
|
||||
function SnapRing({ position, color = '#eab308' }: { position: [number, number, number]; color?: string }) {
|
||||
const ref = useRef<THREE.Mesh>(null);
|
||||
const { camera, size } = useThree();
|
||||
useFrame(() => {
|
||||
if (!ref.current) return;
|
||||
ref.current.lookAt(camera.position);
|
||||
const dist = camera.position.distanceTo(ref.current.position);
|
||||
const perspCam = camera as THREE.PerspectiveCamera;
|
||||
const fov = (perspCam.fov ?? 50) * (Math.PI / 180);
|
||||
const worldPerPixel = (2 * dist * Math.tan(fov / 2)) / size.height;
|
||||
const s = Math.max(0.0008, 10 * worldPerPixel);
|
||||
ref.current.scale.setScalar(s);
|
||||
});
|
||||
return (
|
||||
<mesh position={position} rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<ringGeometry args={[0.003, 0.005, 24]} />
|
||||
<meshBasicMaterial color="#eab308" side={THREE.DoubleSide} />
|
||||
<mesh ref={ref} position={position}>
|
||||
<ringGeometry args={[0.7, 1, 28]} />
|
||||
<meshBasicMaterial color={color} side={THREE.DoubleSide} depthTest={false} transparent opacity={0.9} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
/** 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 (
|
||||
<Html position={position} center distanceFactor={df} zIndexRange={[100, 0]} style={{ pointerEvents: 'none' }}>
|
||||
<div className={`rounded bg-card/95 border ${borderClass} px-2 py-0.5 shadow-lg backdrop-blur-sm`}>
|
||||
<span className={`font-mono text-[11px] font-bold ${textClass} whitespace-nowrap`}>
|
||||
{text}
|
||||
</span>
|
||||
</div>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
|
||||
/** 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 && (
|
||||
<Html position={hoverInfo.position} center distanceFactor={1} style={{ pointerEvents: 'none' }}>
|
||||
<div className="rounded bg-card/95 border border-primary/50 px-2 py-0.5 shadow-lg backdrop-blur-sm">
|
||||
<span className="font-mono text-[11px] font-bold text-primary whitespace-nowrap">
|
||||
{hoverInfo.type === 'hole' ? '⌀ ' : ''}
|
||||
{hoverInfo.value.toFixed(1)} mm
|
||||
</span>
|
||||
</div>
|
||||
</Html>
|
||||
<MeasurementLabel
|
||||
position={hoverInfo.position}
|
||||
text={`${hoverInfo.type === 'hole' ? '⌀ ' : ''}${hoverInfo.value.toFixed(1)} mm`}
|
||||
variant="primary"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Pending first point */}
|
||||
@@ -237,14 +292,9 @@ function MeasurementOverlay() {
|
||||
points={[a, b]}
|
||||
color="#22c55e"
|
||||
lineWidth={2}
|
||||
depthTest={false}
|
||||
/>
|
||||
<Html position={mid} center distanceFactor={1} style={{ pointerEvents: 'none' }}>
|
||||
<div className="rounded bg-card/95 border border-success/50 px-2 py-0.5 shadow-lg backdrop-blur-sm">
|
||||
<span className="font-mono text-[11px] font-bold text-success whitespace-nowrap">
|
||||
{m.distanceMM.toFixed(1)} mm
|
||||
</span>
|
||||
</div>
|
||||
</Html>
|
||||
<MeasurementLabel position={mid} text={`${m.distanceMM.toFixed(1)} mm`} variant="success" />
|
||||
</group>
|
||||
);
|
||||
})}
|
||||
@@ -252,6 +302,7 @@ function MeasurementOverlay() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/** Smart vertex snap on pointer move */
|
||||
function SmartSnapHandler() {
|
||||
const { camera, scene, gl } = useThree();
|
||||
|
||||
Reference in New Issue
Block a user