revert: drop PLOTWINDOW forced endpoint snap (#293)

Remove the forces_endpoint_snap mechanism merged in #293:
CadCommand::forces_endpoint_snap, Snapper::snap_forced_corners /
forced_corner_modes, the PlotWindowCommand override, the on_tick +
click-commit wiring, and the associated tests. PLOTWINDOW now uses the
normal snapper like every other command.

Rationale: forcing Endpoint snap on regardless of the global OSNAP master
overrides an explicit user setting and is surprising — a plot window is a
coarse print region that usually wants a margin, not a corner lock, and
the forced set only caught Endpoint (not center/mid/intersection) so it
was a partial, opinionated helper. Parking it pending a design discussion
with the PR author; can be reinstated behind a clearer UX (e.g. respecting
OSNAP, or an opt-in modifier) if justified.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-06 20:35:50 +03:00
1 parent cb41910f12
commit 9490c85b32
4 files changed
+3 -138

No files matched your search

-86
View File
@@ -1355,48 +1355,6 @@ impl Snapper {
best
}
/// The exact object-snap mode set used while corner snap is forced.
///
/// Endpoint only, deliberately: frame corners are line endpoints, so
/// Endpoint (a single O(n) pass over vertices) catches them. Intersection
/// snap is O(n²) over every pair of segments in the hit-test wire set — on a
/// large drawing viewed zoomed-out (exactly when a plot window is picked)
/// that froze the UI, so it is excluded from the forced set.
fn forced_corner_modes() -> HashSet<SnapType> {
let mut modes = HashSet::default();
modes.insert(SnapType::Endpoint);
modes
}
/// Run object snap with the active modes replaced by exactly Endpoint,
/// regardless of the global OSNAP master toggle or the user's configured
/// modes, then restore both afterward. Commands that always want corner
/// snapping (PLOTWINDOW) call this so the plot-area corners snap without
/// changing the user's global OSNAP settings.
#[allow(clippy::too_many_arguments)]
pub fn snap_forced_corners(
&mut self,
cursor_world: glam::DVec3,
cursor_screen: Point,
wires: &[WireModel],
view_rot: Mat4,
eye: glam::DVec3,
bounds: Rectangle,
grid_origin: Vec3,
grid_rot: Mat4,
) -> Option<SnapResult> {
let saved_master = self.snap_enabled;
let saved_modes = self.enabled.clone();
self.snap_enabled = true;
self.enabled = Self::forced_corner_modes();
let result = self.snap(
cursor_world, cursor_screen, wires, view_rot, eye, bounds, grid_origin, grid_rot,
);
self.snap_enabled = saved_master;
self.enabled = saved_modes;
result
}
}
// ── Object-snap priority ───────────────────────────────────────────────────
@@ -1915,47 +1873,3 @@ mod ext_tests {
assert!(circle_tangent_points(Vec3::new(1.0, 0.0, 0.0), c, 5.0).is_none());
}
}
#[cfg(test)]
mod forced_corner_tests {
use super::*;
#[test]
fn snap_forced_corners_restores_state() {
let mut s = Snapper::default();
// Master off, and a non-default configured mode set.
s.snap_enabled = false;
s.enabled.clear();
s.enabled.insert(SnapType::Midpoint);
let before = s.enabled.clone();
// Empty wire set → no candidate → None; we only assert the state
// is untouched afterward (the risky save/restore path).
let r = s.snap_forced_corners(
glam::DVec3::ZERO,
Point::new(0.0, 0.0),
&[],
Mat4::IDENTITY,
glam::DVec3::ZERO,
Rectangle { x: 0.0, y: 0.0, width: 800.0, height: 600.0 },
Vec3::ZERO,
Mat4::IDENTITY,
);
assert!(r.is_none());
assert!(!s.snap_enabled, "master toggle must be restored to off");
assert_eq!(s.enabled, before, "configured modes must be restored");
}
#[test]
fn forced_corner_modes_are_endpoint_only() {
let modes = Snapper::forced_corner_modes();
assert!(modes.contains(&SnapType::Endpoint));
// Intersection is O(n²) over the wire set and is deliberately excluded —
// forcing it froze the UI on large drawings.
assert!(!modes.contains(&SnapType::Intersection));
assert!(!modes.contains(&SnapType::Midpoint));
assert!(!modes.contains(&SnapType::Center));
assert_eq!(modes.len(), 1);
}
}