diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index af720ea76..bdfe81e3f 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -102,6 +102,10 @@ impl OpenCADStudio { self.tabs[i].scene.clear_preview_wire(); self.tabs[i].snap_result = None; self.last_point = None; + // Points collected in the space being left are meaningless in the new + // one (PR1). + self.clear_accepted_snaps(); + self.pending_click_snap = None; self.snapper.from_point = None; self.snapper.clear_tracking(); self.otrack_active = None; @@ -368,6 +372,11 @@ impl OpenCADStudio { if !self.command_point_allowed(i, *point) { return Task::none(); } + // Typed / dynamic-input / headless points carry no snap, but the + // accepted-snap list must stay index-parallel with the points the + // command collects (PR1). Interactive picks record themselves in + // the click handler and never reach here. + self.record_accepted_snap(None, None, *point); } if default_start { let StepInput::Point(point) = &input else { @@ -1590,9 +1599,7 @@ impl OpenCADStudio { }; if is_associative_dimension && association_enabled { if let Some(handle) = committed { - let sources = self.tabs[i] - .scene - .infer_dimension_sources(handle); + let sources = self.infer_dimension_sources_guarded(i, handle); self.tabs[i] .scene .attach_dimension_association(handle, sources); @@ -2063,9 +2070,7 @@ impl OpenCADStudio { let committed = self.commit_entity_handle(entity); if is_associative_dimension && association_enabled { if let Some(handle) = committed { - let sources = self.tabs[i] - .scene - .infer_dimension_sources(handle); + let sources = self.infer_dimension_sources_guarded(i, handle); self.tabs[i] .scene .attach_dimension_association(handle, sources); @@ -2175,9 +2180,7 @@ impl OpenCADStudio { crate::command::DimensionAssociationInput::Infer(source) => { let sources: Vec<_> = source.map_or_else( || { - self.tabs[i] - .scene - .infer_dimension_sources(handle) + self.infer_dimension_sources_guarded(i, handle) }, |source| { if single_source_dimension { @@ -2302,9 +2305,7 @@ impl OpenCADStudio { crate::command::DimensionAssociationInput::Infer(source) => { source.map_or_else( || { - self.tabs[i] - .scene - .infer_dimension_sources(handle) + self.infer_dimension_sources_guarded(i, handle) .into_iter() .map(|source| { source.map( diff --git a/src/app/commands/mod.rs b/src/app/commands/mod.rs index 3e749827f..a8cf35bc5 100644 --- a/src/app/commands/mod.rs +++ b/src/app/commands/mod.rs @@ -120,6 +120,9 @@ impl OpenCADStudio { // Reset the last committed point so the first click of the new command // is not constrained by ortho/polar relative to a previous command's endpoint. self.last_point = None; + // A new command collects its own points, so the previous command's + // accepted snaps must not leak into it (PR1). + self.clear_accepted_snaps(); // Starting a command restarts the right-click cycle, so its first // right-click acts as Enter rather than opening the context menu. self.tabs[i] diff --git a/src/app/mod.rs b/src/app/mod.rs index 0ad215b09..a4a00956b 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -707,6 +707,25 @@ pub(super) struct OpenCADStudio { show_layout_tabs: bool, /// Last point committed by a drawing command — used as ortho/polar base. last_point: Option, + /// Viewport frame the live snap marker is currently looking through (PR1). + /// Set by the cursor-move handler when the displayed snap came from a + /// layout viewport, cleared otherwise. Display/bookkeeping only — the point + /// itself is already projected onto the sheet. + pub(crate) vp_snap_frame: Option, + /// Snaps accepted by the active command's point steps, parallel to the + /// points it collected (PR1). A command that does not care keeps using the + /// plain paper point; dimension commands (PR2) read the model point, + /// viewport and frame from here, and PR3 reads `source`. Cleared whenever a + /// command starts or ends. + accepted_snaps: Vec, + /// Snap resolved by the click currently being handled, with the viewport + /// frame it came through (PR1). Set inside the click handler's point + /// resolution so the point-accept path further down can record the + /// [`crate::scene::viewport_ref::AcceptedSnap`] without recomputing. + pending_click_snap: Option<( + crate::snap::SnapResult, + Option, + )>, /// Endpoint + unit exit-tangent of the most recently drawn line/arc, so /// `ARC_CONT` (Arc → Continue) can start tangentially from where drawing /// ended. `None` once a non-line/arc entity is committed. @@ -3689,6 +3708,9 @@ impl OpenCADStudio { show_file_tabs: true, show_layout_tabs: true, last_point: None, + vp_snap_frame: None, + accepted_snaps: Vec::new(), + pending_click_snap: None, main_window: None, thumbnail_capture_clean: false, #[cfg(not(target_arch = "wasm32"))]