Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user