Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -459,61 +459,93 @@ function HoverDetector() {
|
|||||||
return null;
|
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() {
|
function MeasureClickHandler() {
|
||||||
const { camera, scene, gl } = useThree();
|
const { camera, scene, gl } = useThree();
|
||||||
const measureMode = useModelStore((s) => s.measureMode);
|
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 raycaster = useMemo(() => new THREE.Raycaster(), []);
|
||||||
const mouse = useMemo(() => new THREE.Vector2(), []);
|
const mouse = useMemo(() => new THREE.Vector2(), []);
|
||||||
|
|
||||||
const handleClick = useCallback((event: MouseEvent) => {
|
useEffect(() => {
|
||||||
if (!measureMode) return;
|
if (!measureMode) {
|
||||||
|
gl.domElement.style.cursor = 'grab';
|
||||||
// Use snap point if available
|
|
||||||
if (snapPoint) {
|
|
||||||
addMeasurePoint({ x: snapPoint.x, y: snapPoint.y, z: snapPoint.z });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
gl.domElement.style.cursor = 'crosshair';
|
||||||
|
|
||||||
const rect = gl.domElement.getBoundingClientRect();
|
const canvas = gl.domElement;
|
||||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
let downX = 0, downY = 0, downBtn = 0;
|
||||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
let armed = false;
|
||||||
|
|
||||||
raycaster.setFromCamera(mouse, camera);
|
const onDown = (e: PointerEvent) => {
|
||||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
if (e.button !== 0) { armed = false; return; }
|
||||||
|
downX = e.clientX;
|
||||||
|
downY = e.clientY;
|
||||||
|
downBtn = e.button;
|
||||||
|
armed = true;
|
||||||
|
};
|
||||||
|
|
||||||
const hit = intersects.find(i => {
|
const onUp = (e: PointerEvent) => {
|
||||||
const obj = i.object;
|
if (!armed || e.button !== downBtn) return;
|
||||||
if (obj instanceof THREE.GridHelper) return false;
|
armed = false;
|
||||||
if (obj instanceof THREE.Mesh) {
|
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 = 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.Mesh)) return false;
|
||||||
|
if (obj.userData.__edgeLine) return false;
|
||||||
if (obj.geometry instanceof THREE.SphereGeometry) return false;
|
if (obj.geometry instanceof THREE.SphereGeometry) return false;
|
||||||
if (obj.geometry instanceof THREE.RingGeometry) return false;
|
if (obj.geometry instanceof THREE.RingGeometry) return false;
|
||||||
|
if (obj.geometry instanceof THREE.PlaneGeometry) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (hit) {
|
||||||
|
st.addMeasurePoint({ x: hit.point.x, y: hit.point.y, z: hit.point.z });
|
||||||
}
|
}
|
||||||
return true;
|
};
|
||||||
});
|
|
||||||
|
|
||||||
if (hit) {
|
const onKey = (e: KeyboardEvent) => {
|
||||||
addMeasurePoint({ x: hit.point.x, y: hit.point.y, z: hit.point.z });
|
const st = useModelStore.getState();
|
||||||
}
|
if (!st.measureMode) return;
|
||||||
}, [measureMode, addMeasurePoint, snapPoint, camera, scene, gl, raycaster, mouse]);
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
canvas.addEventListener('pointerdown', onDown);
|
||||||
const canvas = gl.domElement;
|
canvas.addEventListener('pointerup', onUp);
|
||||||
canvas.addEventListener('click', handleClick);
|
window.addEventListener('keydown', onKey);
|
||||||
return () => canvas.removeEventListener('click', handleClick);
|
return () => {
|
||||||
}, [gl, handleClick]);
|
canvas.removeEventListener('pointerdown', onDown);
|
||||||
|
canvas.removeEventListener('pointerup', onUp);
|
||||||
// Change cursor when in measure mode
|
window.removeEventListener('keydown', onKey);
|
||||||
useEffect(() => {
|
gl.domElement.style.cursor = 'grab';
|
||||||
gl.domElement.style.cursor = measureMode ? 'crosshair' : 'grab';
|
};
|
||||||
return () => { gl.domElement.style.cursor = 'grab'; };
|
}, [measureMode, camera, scene, gl, raycaster, mouse]);
|
||||||
}, [measureMode, gl]);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GridLayer() {
|
function GridLayer() {
|
||||||
const showGrid = useModelStore((s) => s.showGrid);
|
const showGrid = useModelStore((s) => s.showGrid);
|
||||||
const gridY = useModelStore((s) => s.gridY);
|
const gridY = useModelStore((s) => s.gridY);
|
||||||
|
|||||||
Reference in New Issue
Block a user