Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-05-21 12:55:53 +00:00
parent 921fb8f12e
commit 812a829c9d
+61 -29
View File
@@ -459,61 +459,93 @@ function HoverDetector() {
return null;
}
/** Raycasting click handler for measurement mode uses snap point when available */
/** Pointer handler for measurement mode uses snap point when available.
* Uses pointerdown/up with a drag gate so accidental drags don't add points. */
function MeasureClickHandler() {
const { camera, scene, gl } = useThree();
const measureMode = useModelStore((s) => s.measureMode);
const addMeasurePoint = useModelStore((s) => s.addMeasurePoint);
const snapPoint = useModelStore((s) => s.snapPoint);
const raycaster = useMemo(() => new THREE.Raycaster(), []);
const mouse = useMemo(() => new THREE.Vector2(), []);
const handleClick = useCallback((event: MouseEvent) => {
if (!measureMode) return;
useEffect(() => {
if (!measureMode) {
gl.domElement.style.cursor = 'grab';
return;
}
gl.domElement.style.cursor = 'crosshair';
// Use snap point if available
if (snapPoint) {
addMeasurePoint({ x: snapPoint.x, y: snapPoint.y, z: snapPoint.z });
const canvas = gl.domElement;
let downX = 0, downY = 0, downBtn = 0;
let armed = false;
const onDown = (e: PointerEvent) => {
if (e.button !== 0) { armed = false; return; }
downX = e.clientX;
downY = e.clientY;
downBtn = e.button;
armed = true;
};
const onUp = (e: PointerEvent) => {
if (!armed || e.button !== downBtn) return;
armed = false;
const dx = e.clientX - downX;
const dy = e.clientY - downY;
if (Math.hypot(dx, dy) > 4) return; // it was a drag, not a click
const st = useModelStore.getState();
if (!st.measureMode) return;
// Prefer snap
if (st.snapPoint) {
st.addMeasurePoint({ x: st.snapPoint.x, y: st.snapPoint.y, z: st.snapPoint.z });
return;
}
const rect = gl.domElement.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
const rect = canvas.getBoundingClientRect();
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
const hit = intersects.find(i => {
const obj = i.object;
if (obj instanceof THREE.GridHelper) return false;
if (obj instanceof THREE.Mesh) {
if (!(obj instanceof THREE.Mesh)) return false;
if (obj.userData.__edgeLine) return false;
if (obj.geometry instanceof THREE.SphereGeometry) return false;
if (obj.geometry instanceof THREE.RingGeometry) return false;
}
if (obj.geometry instanceof THREE.PlaneGeometry) return false;
return true;
});
if (hit) {
addMeasurePoint({ x: hit.point.x, y: hit.point.y, z: hit.point.z });
st.addMeasurePoint({ x: hit.point.x, y: hit.point.y, z: hit.point.z });
}
}, [measureMode, addMeasurePoint, snapPoint, camera, scene, gl, raycaster, mouse]);
};
useEffect(() => {
const canvas = gl.domElement;
canvas.addEventListener('click', handleClick);
return () => canvas.removeEventListener('click', handleClick);
}, [gl, handleClick]);
const onKey = (e: KeyboardEvent) => {
const st = useModelStore.getState();
if (!st.measureMode) return;
if (e.key === 'Escape') {
if (st.measurePoints.length > 0) st.undoLastMeasurePoint();
} else if ((e.key === 'z' || e.key === 'Z') && !e.ctrlKey && !e.metaKey) {
st.undoLastMeasurement();
}
};
// Change cursor when in measure mode
useEffect(() => {
gl.domElement.style.cursor = measureMode ? 'crosshair' : 'grab';
return () => { gl.domElement.style.cursor = 'grab'; };
}, [measureMode, gl]);
canvas.addEventListener('pointerdown', onDown);
canvas.addEventListener('pointerup', onUp);
window.addEventListener('keydown', onKey);
return () => {
canvas.removeEventListener('pointerdown', onDown);
canvas.removeEventListener('pointerup', onUp);
window.removeEventListener('keydown', onKey);
gl.domElement.style.cursor = 'grab';
};
}, [measureMode, camera, scene, gl, raycaster, mouse]);
return null;
}
function GridLayer() {
const showGrid = useModelStore((s) => s.showGrid);
const gridY = useModelStore((s) => s.gridY);