fix(snap): correct snap symbols + drop bogus midpoints on circles (#34)

Two bugs from #34:

1. Hovering a circle showed the Center snap as a diamond instead of
   the conventional circle outline.
2. Hovering a circle also showed Midpoint hits (triangle marker)
   even though a closed curve has no midpoint.

(2) came from a "tessellated curve" fallback in the Midpoint snap
path that produced a midpoint for every chord of the polyline
approximation. Drop that fallback — only entities with explicit
`key_vertices` (Line, LwPolyline) contribute per-segment midpoints
now. Curves that DO have a single well-defined midpoint (arcs, and
later splines) declare it explicitly via a new
`SnapHint::Midpoint`; `arc.rs` emits one at the arc-length centre.

(1) — and the broader "everything past Endpoint / Midpoint / Grid
was a diamond" inconsistency — fixed by giving every `SnapType` a
distinct marker in `SelectionCanvas::draw`:

- Center: hollow circle
- Node: circle with inscribed X
- Quadrant: diamond (unchanged)
- Intersection: X
- Apparent Intersection: X inside a square
- Insertion: two overlapping rectangles (tag glyph)
- Perpendicular: right-angle hook
- Tangent: circle with a bar across the top
- Nearest: hourglass / bowtie
- Extension: three dots
- Parallel: two parallel diagonal bars
- Endpoint / Midpoint / Grid: unchanged

Closes #34
This commit is contained in:
Hakan Seven committed 2026-05-29 01:39:02 +03:00
1 parent 89fcb21b46
commit 0ec6d77cb1
4 files changed
+162 -28

No files matched your search

+6 -11
View File
@@ -334,6 +334,7 @@ impl Snapper {
SnapHint::Node => SnapType::Node,
SnapHint::Quadrant => SnapType::Quadrant,
SnapHint::Insertion => SnapType::Insertion,
SnapHint::Midpoint => SnapType::Midpoint,
};
if self.is_on(snap_type) {
try_pt(world, snap_type);
@@ -367,13 +368,17 @@ impl Snapper {
}
// ── Midpoint ───────────────────────────────────────────────────────
// Only explicit vertex sets (Line, LwPolyline) contribute per-segment
// midpoints. Tessellated curves (Circle, Arc, Ellipse, Spline) emit a
// single `SnapHint::Midpoint` snap_pt where one exists — iterating
// every chord here would otherwise turn a circle's tessellation into
// a haze of false midpoint hits. See #34.
if self.is_on(SnapType::Midpoint) {
for wire in wires {
if !wire_in_range(wire) {
continue;
}
if !wire.key_vertices.is_empty() {
// Use explicit vertices for accurate per-segment midpoints.
for seg in wire.key_vertices.windows(2) {
let a = Vec3::from(seg[0]);
let b = Vec3::from(seg[1]);
@@ -381,16 +386,6 @@ impl Snapper {
try_pt((a + b) * 0.5, SnapType::Midpoint);
}
}
} else {
// Tessellated curves: midpoint of whole arc (first↔last midpoint skipped
// for closed curves; arcs use their own quadrant snaps instead).
for seg in wire.points.windows(2) {
let a = Vec3::from(seg[0]);
let b = Vec3::from(seg[1]);
if a.distance_squared(b) > 1e-12 {
try_pt((a + b) * 0.5, SnapType::Midpoint);
}
}
}
}
}