Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-05-21 21:25:04 +00:00
parent 22e3ede63c
commit 16c1e7b755
+64 -55
View File
@@ -104,72 +104,81 @@ export function XRControllerMeasure() {
raycaster.current.set(tmpOrigin.current, tmpDir.current);
raycaster.current.far = MAX_RAY;
const hits = raycaster.current.intersectObjects(scene.children, true);
const hit = hits.find((h) => {
const o = h.object;
if (!(o instanceof THREE.Mesh)) return false;
if (o.userData.__edgeLine) return false;
if (o.geometry instanceof THREE.SphereGeometry) return false;
if (o.geometry instanceof THREE.RingGeometry) return false;
if (o.geometry instanceof THREE.PlaneGeometry) return false;
return true;
});
let snappedPoint: THREE.Vector3 | null = null;
let snapKind: 'vertex' | 'edge' | 'surface' | 'hole' = 'surface';
let hoverDetected: { kind: 'hole' | 'edge'; value: number; position: THREE.Vector3; endpoints?: { a: THREE.Vector3; b: THREE.Vector3 } } | null = null;
if (hit && hit.object instanceof THREE.Mesh) {
const size = gl.getSize(new THREE.Vector2());
const canvasSize = { width: size.x || 1024, height: size.y || 1024 };
const fakeCam = new THREE.PerspectiveCamera(60, 1, 0.01, 100);
fakeCam.position.copy(tmpOrigin.current);
fakeCam.quaternion.copy(tmpQuat.current);
fakeCam.updateMatrixWorld(true);
if (doHeavy) {
const hits = raycaster.current.intersectObjects(scene.children, true);
const hit = hits.find((h) => {
const o = h.object;
if (!(o instanceof THREE.Mesh)) return false;
if (o.userData.__edgeLine) return false;
if (o.geometry instanceof THREE.SphereGeometry) return false;
if (o.geometry instanceof THREE.RingGeometry) return false;
if (o.geometry instanceof THREE.PlaneGeometry) return false;
return true;
});
// Snap to existing registered hole centers first (within ~30px screen)
const existing = useModelStore.getState().measurements;
let bestHoleCenter: THREE.Vector3 | null = null;
let bestHolePx = Infinity;
const hitProj = hit.point.clone().project(fakeCam);
const hx = (hitProj.x * 0.5 + 0.5) * canvasSize.width;
const hy = (-hitProj.y * 0.5 + 0.5) * canvasSize.height;
for (const m of existing) {
if (m.kind !== 'hole') continue;
const c = new THREE.Vector3(m.pointA.x, m.pointA.y, m.pointA.z);
const p = c.clone().project(fakeCam);
const sx = (p.x * 0.5 + 0.5) * canvasSize.width;
const sy = (-p.y * 0.5 + 0.5) * canvasSize.height;
const d = Math.hypot(sx - hx, sy - hy);
if (d < 30 && d < bestHolePx) { bestHolePx = d; bestHoleCenter = c; }
}
if (hit && hit.object instanceof THREE.Mesh) {
const size = gl.getSize(new THREE.Vector2());
const canvasSize = { width: size.x || 1024, height: size.y || 1024 };
const fakeCam = new THREE.PerspectiveCamera(60, 1, 0.01, 100);
fakeCam.position.copy(tmpOrigin.current);
fakeCam.quaternion.copy(tmpQuat.current);
fakeCam.updateMatrixWorld(true);
if (snapEnabled && bestHoleCenter) {
snappedPoint = bestHoleCenter;
snapKind = 'hole';
} else if (snapEnabled) {
// Try detecting a circular edge (hole) at hit
const circle = detectCircularEdgeAtPoint(hit.object, hit.point, fakeCam, canvasSize, 60);
if (circle) {
snappedPoint = circle.center;
// Snap to existing registered hole centers first (within ~30px screen)
const existing = useModelStore.getState().measurements;
let bestHoleCenter: THREE.Vector3 | null = null;
let bestHolePx = Infinity;
const hitProj = hit.point.clone().project(fakeCam);
const hx = (hitProj.x * 0.5 + 0.5) * canvasSize.width;
const hy = (-hitProj.y * 0.5 + 0.5) * canvasSize.height;
for (const m of existing) {
if (m.kind !== 'hole') continue;
const c = new THREE.Vector3(m.pointA.x, m.pointA.y, m.pointA.z);
const p = c.clone().project(fakeCam);
const sx = (p.x * 0.5 + 0.5) * canvasSize.width;
const sy = (-p.y * 0.5 + 0.5) * canvasSize.height;
const d = Math.hypot(sx - hx, sy - hy);
if (d < 30 && d < bestHolePx) { bestHolePx = d; bestHoleCenter = c; }
}
if (snapEnabled && bestHoleCenter) {
snappedPoint = bestHoleCenter;
snapKind = 'hole';
hoverDetected = { kind: 'hole', value: circle.diameterMM, position: circle.center };
} else {
const snap = resolveSnap(hit.object, hit.point, fakeCam, canvasSize, 14, 18);
snappedPoint = snap.point;
snapKind = snap.type;
if (snap.type === 'edge') {
const seg = findNearestEdgeSegment(hit.object, hit.point, fakeCam, canvasSize, 18);
if (seg) {
const lenMM = seg.a.distanceTo(seg.b) * 1000;
hoverDetected = { kind: 'edge', value: lenMM, position: seg.midpoint, endpoints: { a: seg.a, b: seg.b } };
} else if (snapEnabled) {
// Try detecting a circular edge (hole) at hit
const circle = detectCircularEdgeAtPoint(hit.object, hit.point, fakeCam, canvasSize, 60);
if (circle) {
snappedPoint = circle.center;
snapKind = 'hole';
hoverDetected = { kind: 'hole', value: circle.diameterMM, position: circle.center };
} else {
const snap = resolveSnap(hit.object, hit.point, fakeCam, canvasSize, 14, 18);
snappedPoint = snap.point;
snapKind = snap.type;
if (snap.type === 'edge') {
const seg = findNearestEdgeSegment(hit.object, hit.point, fakeCam, canvasSize, 18);
if (seg) {
const lenMM = seg.a.distanceTo(seg.b) * 1000;
hoverDetected = { kind: 'edge', value: lenMM, position: seg.midpoint, endpoints: { a: seg.a, b: seg.b } };
}
}
}
} else {
snappedPoint = hit.point.clone();
snapKind = 'surface';
}
} else {
snappedPoint = hit.point.clone();
snapKind = 'surface';
}
// Cache for non-heavy frames so the laser + trigger still have a target
cachedSnappedPoint.current = snappedPoint ? snappedPoint.clone() : null;
cachedSnapKind.current = snapKind;
} else {
// Light frame: reuse the last computed snap so visuals stay smooth
snappedPoint = cachedSnappedPoint.current ? cachedSnappedPoint.current.clone() : null;
snapKind = cachedSnapKind.current;
}
// ── Dwell detection (1 s) to auto-register hovered hole/edge ─────