forked from 1b-agent/opencadstudio-upstream
Record viewport associations for new dimensions
Removes PR3's `viewport_frame` seam in `viewport_ref.rs`; the frame is PR1's camera-derived one everywhere. The five dimension commands (DIMLINEAR / DIMALIGNED / DIMRADIUS / DIMDIAMETER / DIMANGULAR) now record a real association when they measure through a viewport. The commit path's one association gate (`dimension_association_allowed`, added when PR2 was stacked) gains an else-branch: where PR2 committed the dimension unassociated and PR1's inference guard returned an empty source list, `Scene::attach_viewport_ dimension_association` now stores the whole chain — dimension -> viewport -> block path -> entity -> feature — from the AcceptedSnaps PR1 captured, in definition-point slot order. * `Scene::dimension_association_slot_points` names that order, including the radial dimensions `dimension_reference_points` does not cover. * `OpenCADStudio::viewport_dimension_snaps` builds the slot array. A snap whose paper point is no longer the definition point it fills has its model point recomputed from that definition point through the frame: the object pick records one click for both extension origins, and a rotated DIMLINEAR projects its origins onto the measuring axis. * Radial dimensions take their own path inside `attach_viewport_dimension_association`. Their single reference is an osnap type 10 carrying the radial marker (which circle / which polyline arc segment) and the angle around it; `source_reference` would hand back the generic curve-parameter marker -2, which `radial_source_for_marker` cannot find. One measurement rule, documented in `viewport_ref::measurement_rule`: `actual_measurement` is always the RAW measurement in the dimension's own space (paper, for a viewport dimension), and the viewport compensation lives only in the magnitude of the negative DIMLFAC override, applied once by the formatter. PR2's persistence already followed it; PR3's refresh did not — it substituted the model distance times `model_factor()`, which made a refreshed dimension read `user_lfac` times too much versus an identical freshly-created one. Refresh now writes the paper measurement (a debug assert pins resolved paper points to the model points through the frame), and `axis_distance` goes with it. Block sub-entity snaps: verified that `block_cache` stamps the top-level INSERT's handle onto every expanded and clipped sub-wire, so `wire_source` can only ever report the INSERT — nested inserts included. `OpenCADStudio::resolve_source_block_path` now descends from that INSERT at accept time (using the object-pick path's existing `resolve_measurable_entity`, so arbitrary depth, not just one level) and fills `SnapSourceRef.block_path` with the real inner entity. Where the descent cannot identify one — a non-planar-pickable type such as text, hatch, spline or solid, or nesting past eight levels — the source stays the INSERT with an empty path, which is coarser but never wrong. Both the limitation and where it bites are documented on `Scene::resolve_block_snap_source` and `SnapResult::source`. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
f2c9e90c34
commit
be7ba3d68e
8 files changed
+336
-95
No files matched your search
@@ -4173,6 +4173,7 @@ impl OpenCADStudio {
|
||||
// `world_pt` exactly as before.
|
||||
let pending = self.pending_click_snap.take();
|
||||
self.record_accepted_snap(
|
||||
i,
|
||||
pending.map(|(hit, _)| hit),
|
||||
pending.and_then(|(_, frame)| frame),
|
||||
world_pt,
|
||||
|
||||
@@ -102,11 +102,12 @@ impl OpenCADStudio {
|
||||
/// after ortho / polar / axis-lock / dynamic-input resolution.
|
||||
pub(crate) fn record_accepted_snap(
|
||||
&mut self,
|
||||
tab: usize,
|
||||
snap: Option<SnapResult>,
|
||||
frame: Option<ViewportFrame>,
|
||||
committed: glam::DVec3,
|
||||
) {
|
||||
let accepted = match snap {
|
||||
let mut accepted = match snap {
|
||||
// A viewport snap keeps its frame only when the hit really came
|
||||
// through it, so a paper-sheet snap never claims a model point.
|
||||
Some(hit) => {
|
||||
@@ -115,9 +116,42 @@ impl OpenCADStudio {
|
||||
}
|
||||
None => AcceptedSnap::free(committed),
|
||||
};
|
||||
self.resolve_source_block_path(tab, &mut accepted);
|
||||
self.push_accepted_snap(accepted);
|
||||
}
|
||||
|
||||
/// Replace a snap source that is a block INSTANCE with the entity inside
|
||||
/// it that actually owns the snapped feature, plus the INSERT path.
|
||||
///
|
||||
/// The snap engine can only report what the wire carries, and the
|
||||
/// renderer stamps every wire of an expanded block with the *top-level
|
||||
/// INSERT's* handle — nested inserts included. `SnapSourceRef` promises
|
||||
/// the innermost entity in `source.handle` and the instance chain in
|
||||
/// `block_path`, so the descent happens here, once, at accept time (not
|
||||
/// per pointer move).
|
||||
///
|
||||
/// When the descent cannot identify the inner entity the source stays the
|
||||
/// INSERT with an empty path. See
|
||||
/// [`crate::scene::Scene::resolve_block_snap_source`] for exactly when
|
||||
/// that happens; the resulting association is coarser (it tracks the whole
|
||||
/// block instance) but never wrong.
|
||||
fn resolve_source_block_path(&self, tab: usize, accepted: &mut AcceptedSnap) {
|
||||
let Some(source) = accepted.source.as_mut() else {
|
||||
return;
|
||||
};
|
||||
if !source.block_path.is_empty() {
|
||||
return;
|
||||
}
|
||||
if let Some((entity, path)) = self.tabs[tab]
|
||||
.scene
|
||||
.resolve_block_snap_source(source.source.handle, accepted.model_point)
|
||||
{
|
||||
source.source =
|
||||
crate::command::DimensionAssociationSource::inferred(entity);
|
||||
source.block_path = path;
|
||||
}
|
||||
}
|
||||
|
||||
/// Append an already-built accepted snap, keeping the retention cap.
|
||||
///
|
||||
/// Used by paths that are not point steps (the dimension object pick
|
||||
|
||||
Reference in new issue
Block a user