Use viewport snap frame and accepted snaps for dimension measurement

Removes PR2's two seams now that PR1 (viewport snapping) is underneath.

* `Scene::viewport_frame` is PR1's camera-derived implementation. The
  viewport-record version is gone; PR1's returns `None` for oblique /
  perspective viewports, which is what we want — every entry point in
  `viewport_dim_seam` then declines rather than guessing a planar
  mapping. `viewport_dim_seam` is now just the dimension object-pick
  path, and its module doc says so.

* There is one accepted-snap store: PR1's `OpenCADStudio::accepted_snaps`.
  PR2's parallel `dim_accepted_snaps` (and its command-name ownership
  tracking, which PR1's clear-on-command-start/end already does) is
  deleted, along with the `record_dimension_accepted_snap` seam.
  `accepted_snap_for_point` reads PR1's list.

  Two things layer on top:
  - the explicit object pick is not a point step, so it still records its
    own snaps, now through `push_accepted_snap` into PR1's list, and now
    carrying the picked entity handle + block path as a `SnapSourceRef`
    so PR3 can associate it;
  - a typed coordinate carries no snap, so `measure_snap` re-attaches the
    content viewport under a snap that is free *and* source-less. That
    keeps `DIMLINEAR` with typed coordinates inside a 1:10 viewport
    measuring the model, without ever reinterpreting a real paper-sheet
    snap that happens to sit over a viewport.

* One association gate, not two. `dimension_association_allowed` is the
  single predicate, derived from the same `dimension_measure_space`
  classification that decides whether to compensate the measurement, so
  the two can never disagree. `infer_dimension_sources_guarded` delegates
  to it, and the commit path's "commit unassociated when measured through
  a viewport" condition is now that predicate rather than a second,
  independently-computed bool. It also covers the explicit-pick and
  explicit-source-list branches, which the old inference-only guard did
  not. Because the classification ignores the trailing dimension-line
  placement click, an ordinary paper dimension whose placement click lands
  inside a viewport rectangle keeps its association.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Christopher OverbeckandClaude Fable 5.1 committed 2026-09-15 12:45:59 -05:00
1 parent 848f789018
commit 43dea41498
5 files changed
+170 -177

No files matched your search

+15 -41
View File
@@ -1,19 +1,20 @@
//! PR2 seams onto the paper-space viewport world.
//! Object-pick resolution for paper-space dimensions taken through a layout
//! viewport.
//!
//! Everything in this file exists so that viewport-aware dimension
//! measurement works *without* PR1 (viewport snapping) in the tree. Each seam
//! is small and self-contained; the merge notes say which ones PR1 replaces.
//! Paper-space hit testing only sees the sheet's own entities, so a dimension
//! command's explicit "select object" pick that lands inside a content
//! viewport finds nothing. [`Scene::dimension_pick_through_viewport`] maps the
//! click into model space through the viewport's
//! [`crate::scene::viewport_ref::ViewportFrame`], hit-tests the viewport's
//! resident model wires (descending into block instances and baking the
//! instance transform), and hands back a copy of the picked entity already
//! projected onto the sheet.
//!
//! ## Seams
//! * [`Scene::viewport_frame`] — **replaced by PR1.** PR1 owns construction of
//! [`ViewportFrame`] from the live viewport camera. This implementation
//! derives the frame straight from the DXF viewport record, which is correct
//! for plan (non-oblique) content viewports.
//! * [`Scene::content_viewport_at_paper_point`] /
//! [`Scene::viewport_frame_at_paper_point`] — PR1 supersedes these with its
//! snap-time viewport resolution; harmless to keep.
//! * [`Scene::dimension_pick_through_viewport`] — PR2 owns this permanently.
//! It is the explicit-object-pick path and does not depend on PR1.
//! The frame itself is built by [`Scene::viewport_frame`] (see
//! `crate::scene::mspace`), which derives it from the *live viewport camera*
//! so it always agrees with the pixels the renderer drew. It returns `None`
//! for oblique / perspective viewports, and every entry point here then
//! declines the pick rather than guessing a planar mapping.
use super::*;
@@ -43,33 +44,6 @@ pub struct ViewportDimPick {
}
impl Scene {
// ───────────────────────── PR1 SEAM: BEGIN ──────────────────────────
// Replace the body of `viewport_frame` with PR1's implementation on
// merge; the signature is the shared contract and must not change.
/// Planar paper↔model mapping for one layout viewport.
///
/// Derived from the viewport record rather than the render camera:
/// `paper_center = vp.center`, `model_target = vp.view_target`,
/// `scale = vp_effective_scale(...)`, `twist = vp.twist_angle`.
pub fn viewport_frame(&self, viewport: Handle) -> Option<ViewportFrame> {
let Some(EntityType::Viewport(vp)) = self.document.get_entity(viewport) else {
return None;
};
let scale = vp_effective_scale(vp.custom_scale, vp.view_height, vp.height);
if !scale.is_finite() || scale.abs() < 1e-12 {
return None;
}
Some(ViewportFrame {
viewport,
paper_center: DVec2::new(vp.center.x, vp.center.y),
model_target: DVec2::new(vp.view_target.x, vp.view_target.y),
scale,
twist: vp.twist_angle,
locked: vp.status.locked,
})
}
// ────────────────────────── PR1 SEAM: END ───────────────────────────
/// The smallest *on* content viewport of the current layout whose paper
/// rectangle contains `paper`. `None` on the Model tab, while editing