Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -393,22 +393,24 @@ function SmartSnapHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Hover detector for auto-detect hole diameter and edge length */
|
||||
/** Hover detector for auto-detect hole diameter and edge length.
|
||||
* Runs in BOTH view mode and measure mode. Outside measure mode the label
|
||||
* auto-hides after 4s; in measure mode it persists until the user clicks
|
||||
* (registering it as a saved measurement) or moves the mouse significantly. */
|
||||
function HoverDetector() {
|
||||
const { camera, scene, gl } = useThree();
|
||||
const measureMode = useModelStore((s) => s.measureMode);
|
||||
const setHoverInfo = useModelStore((s) => s.setHoverInfo);
|
||||
const raycaster = useMemo(() => new THREE.Raycaster(), []);
|
||||
const mouse = useMemo(() => new THREE.Vector2(), []);
|
||||
const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const lastHitKey = useRef('');
|
||||
|
||||
useEffect(() => {
|
||||
if (measureMode) {
|
||||
setHoverInfo(null);
|
||||
if (hoverTimer.current) clearTimeout(hoverTimer.current);
|
||||
return;
|
||||
}
|
||||
const clearTimers = () => {
|
||||
if (hoverTimer.current) { clearTimeout(hoverTimer.current); hoverTimer.current = null; }
|
||||
if (hideTimer.current) { clearTimeout(hideTimer.current); hideTimer.current = null; }
|
||||
};
|
||||
|
||||
const onMove = (e: MouseEvent) => {
|
||||
const rect = gl.domElement.getBoundingClientRect();
|
||||
@@ -429,24 +431,31 @@ function HoverDetector() {
|
||||
if (!hit || !(hit.object instanceof THREE.Mesh)) {
|
||||
lastHitKey.current = '';
|
||||
setHoverInfo(null);
|
||||
if (hoverTimer.current) clearTimeout(hoverTimer.current);
|
||||
clearTimers();
|
||||
return;
|
||||
}
|
||||
|
||||
// Stability check – same approximate position for debounce
|
||||
// Stability check – same approximate position for debounce (~3 mm)
|
||||
const key = `${hit.point.x.toFixed(3)},${hit.point.y.toFixed(3)},${hit.point.z.toFixed(3)}`;
|
||||
if (key === lastHitKey.current) return; // timer already running
|
||||
lastHitKey.current = key;
|
||||
setHoverInfo(null);
|
||||
clearTimers();
|
||||
|
||||
if (hoverTimer.current) clearTimeout(hoverTimer.current);
|
||||
hoverTimer.current = setTimeout(() => {
|
||||
if (!(hit.object instanceof THREE.Mesh)) return;
|
||||
const canvas = gl.domElement;
|
||||
const canvasSize = { width: canvas.clientWidth, height: canvas.clientHeight };
|
||||
|
||||
// Try hole detection first
|
||||
if (hit.faceIndex !== undefined) {
|
||||
// 1) Try circle fit from edge vertices around the hit (catches holes on flat faces)
|
||||
const circ = detectCircularEdgeAtPoint(hit.object, hit.point, camera, canvasSize, 50);
|
||||
if (circ) {
|
||||
setHoverInfo({
|
||||
type: 'hole',
|
||||
position: [circ.center.x, circ.center.y, circ.center.z],
|
||||
value: circ.diameterMM,
|
||||
});
|
||||
} else if (hit.faceIndex !== undefined && (() => {
|
||||
const hole = detectHoleAtFace(hit.object, hit.faceIndex);
|
||||
if (hole) {
|
||||
setHoverInfo({
|
||||
@@ -454,29 +463,42 @@ function HoverDetector() {
|
||||
position: [hole.center.x, hole.center.y, hole.center.z],
|
||||
value: hole.diameterMM,
|
||||
});
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try edge detection
|
||||
return false;
|
||||
})()) {
|
||||
// handled inside IIFE
|
||||
} else {
|
||||
// 3) Edge length
|
||||
const edge = findNearestEdgeSegment(hit.object, hit.point, camera, canvasSize, 15);
|
||||
if (edge) {
|
||||
setHoverInfo({
|
||||
type: 'edge',
|
||||
position: [edge.midpoint.x, edge.midpoint.y, edge.midpoint.z],
|
||||
value: edge.lengthMM,
|
||||
endpoints: {
|
||||
a: { x: edge.a.x, y: edge.a.y, z: edge.a.z },
|
||||
b: { x: edge.b.x, y: edge.b.y, z: edge.b.z },
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setHoverInfo(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setHoverInfo(null);
|
||||
}, 1000);
|
||||
// Outside measure mode, auto-hide after 4 s
|
||||
const inMeasure = useModelStore.getState().measureMode;
|
||||
if (!inMeasure) {
|
||||
hideTimer.current = setTimeout(() => setHoverInfo(null), 4000);
|
||||
}
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const onLeave = () => {
|
||||
lastHitKey.current = '';
|
||||
setHoverInfo(null);
|
||||
if (hoverTimer.current) clearTimeout(hoverTimer.current);
|
||||
clearTimers();
|
||||
};
|
||||
|
||||
gl.domElement.addEventListener('pointermove', onMove);
|
||||
@@ -484,9 +506,9 @@ function HoverDetector() {
|
||||
return () => {
|
||||
gl.domElement.removeEventListener('pointermove', onMove);
|
||||
gl.domElement.removeEventListener('pointerleave', onLeave);
|
||||
if (hoverTimer.current) clearTimeout(hoverTimer.current);
|
||||
clearTimers();
|
||||
};
|
||||
}, [measureMode, camera, scene, gl, raycaster, mouse, setHoverInfo]);
|
||||
}, [camera, scene, gl, raycaster, mouse, setHoverInfo]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user