diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index d16dff8a9..fa833b75f 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -889,19 +889,8 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task { } else { let (go, gr) = self.tabs[i].ucs_grid_basis(); self.snapper.from_point = self.last_point; - let force_corners = self.tabs[i] - .active_cmd - .as_ref() - .map(|c| c.forces_endpoint_snap()) - .unwrap_or(false); - if force_corners { - self.snapper.snap_forced_corners( - snap_cursor, p, &all_wires[..], view_rot, eye, bounds, go, gr, - ) - } else { - self.snapper - .snap(snap_cursor, p, &all_wires[..], view_rot, eye, bounds, go, gr) - } + self.snapper + .snap(snap_cursor, p, &all_wires[..], view_rot, eye, bounds, go, gr) }; // Object Snap Tracking: update dwell, then align the cursor @@ -1828,23 +1817,7 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task { } else { let (go, gr) = self.tabs[i].ucs_grid_basis(); self.snapper.from_point = self.last_point; - // Mirror the preview path (see on_tick): a command that - // forces corner snap (PLOTWINDOW) must commit the forced - // Endpoint hit, not a plain snap — otherwise the marker - // promises an endpoint the click places at the raw cursor - // whenever the global OSNAP master (or Endpoint mode) is off. - let force_corners = self.tabs[i] - .active_cmd - .as_ref() - .map(|c| c.forces_endpoint_snap()) - .unwrap_or(false); - if force_corners { - self.snapper.snap_forced_corners( - snap_cursor, p, &all_wires[..], view_rot, eye, bounds, go, gr, - ) - } else { - self.snapper.snap(snap_cursor, p, &all_wires[..], view_rot, eye, bounds, go, gr) - } + self.snapper.snap(snap_cursor, p, &all_wires[..], view_rot, eye, bounds, go, gr) }; // Snap runs in model space; the result is already model. let mut pt = snap_hit.map(|s| s.world).unwrap_or(raw); diff --git a/src/command.rs b/src/command.rs index 862f80d24..3587d7e41 100644 --- a/src/command.rs +++ b/src/command.rs @@ -530,13 +530,6 @@ pub trait CadCommand: Send { false } - /// True if this command should have Endpoint/Intersection object-snap - /// forced on while it is active, regardless of the global OSNAP toggle. - /// Used by PLOTWINDOW so the plot-area corners always snap to geometry. - fn forces_endpoint_snap(&self) -> bool { - false - } - /// Called when the text editor closes, either because the user committed or cancelled the edit. fn on_editor_closed(&mut self, _committed: bool) -> CmdResult { CmdResult::Cancel diff --git a/src/modules/view/plot_window.rs b/src/modules/view/plot_window.rs index 5456e657e..5582329cf 100644 --- a/src/modules/view/plot_window.rs +++ b/src/modules/view/plot_window.rs @@ -22,10 +22,6 @@ impl CadCommand for PlotWindowCommand { "PLOTWINDOW" } - fn forces_endpoint_snap(&self) -> bool { - true - } - fn prompt(&self) -> String { if self.p1.is_none() { "PLOTWINDOW Specify first corner of plot window:".into() @@ -85,14 +81,3 @@ impl CadCommand for PlotWindowCommand { // ── Autocomplete registry ───────────────────────────────── inventory::submit!(crate::command::CommandRegistration { names: &["PLOTWINDOW", "PW"] }); // PlotWindowCommand - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn plot_window_forces_endpoint_snap() { - let cmd = PlotWindowCommand::new(); - assert!(cmd.forces_endpoint_snap()); - } -} diff --git a/src/snap.rs b/src/snap.rs index 3daf758f7..cac31adde 100644 --- a/src/snap.rs +++ b/src/snap.rs @@ -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 { - 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 { - 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); - } -}