fix(snap): priority-based osnap selection + true perpendicular

Object snaps were chosen purely by nearest screen distance, so the
"continuous" snaps that sit on the geometry (Nearest, Perpendicular)
were almost always closer than a discrete Endpoint/Midpoint/Center and
silently suppressed every other enabled snap. Selection is now
priority-then-distance: a higher-priority snap inside the aperture wins,
distance only breaks ties within a priority. (#118)

The aperture gate is `!(d2 < radius2)` rather than `d2 >= radius2` so a
NaN distance from degenerate geometry is rejected instead of passing the
gate and being chosen on rank alone (which fed a NaN point to the fill
tessellator and panicked).

Perpendicular now drops its foot from the command's drawing origin
(`Snapper::from_point`, set before each snap) instead of the cursor, so
the new segment is genuinely perpendicular to the target line rather than
just the nearest point on it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-18 16:30:37 +03:00
commit e1d128c4dc
2 changed files with 71 additions and 7 deletions

View file

@ -2507,6 +2507,8 @@ impl OpenCADStudio {
// `hit_test_wires` — snap against the set directly, no clone
// and no self-snap.
let all_wires = self.tabs[i].scene.hit_test_wires();
// Grip drag has no single rubber-band origin for a perp foot.
self.snapper.from_point = None;
let snap_hit = self.snapper.snap(raw, p, &all_wires[..], vp_mat, bounds);
let mut snapped = snap_hit.map(|s| s.world).unwrap_or(raw);
self.tabs[i].snap_result = snap_hit;
@ -2679,6 +2681,7 @@ impl OpenCADStudio {
bounds,
)
} else {
self.snapper.from_point = self.last_point;
self.snapper
.snap(cursor_world, p, &all_wires[..], view_proj, bounds)
};
@ -3292,6 +3295,7 @@ impl OpenCADStudio {
self.snapper
.snap_tangent_only(raw, p, &all_wires[..], vp_mat, bounds)
} else {
self.snapper.from_point = self.last_point;
self.snapper.snap(raw, p, &all_wires[..], vp_mat, bounds)
};
// snap.world is in paper-space (projected wire coords in MSPACE);