COPY and PASTE kept the source's reactors on each copy, so a copied
dimension still pointed at the source's dimension association (and
boundary or group members at the source hatch or group). Another
application reports such a drawing as damaged and, after recovery,
moves the dimensions. A copy's reactors now follow only targets copied
with it; group copies give their members the new group's reactor; and a
drawing opened with such stray dimension reactors has them dropped.
Fixes#1645Fixes#1649
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The codec and kernel repositories and crates are renamed opencadcodec and
opencadkernel. Inside OCS they are depended on under short keys, so code
reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node
graph engine). File-format and protocol spellings stay as they were: the
ACIS product id `acadrust`, the ACADRUST_* switches and the plugin
version keys `acadrust_version` / `acadrust_source`.
A button left of MCP on the command line opens a translucent node graph
over the viewport, with a palette of object, input, math, logic, list,
text and geometry nodes added by click or drag. An object node owns
drawing entities and its rows are the Properties panel's own rows for
them: the single-object page is extracted from refresh_properties as
entity_property_sections, values use the control protocol's
property_json, and writes go through set_property_value with a property
target override, so the panel, MCP and the graph share one read/write
path. A list reaching an object row makes one entity per element. Each
graph action is one undo step; the canvas pans and zooms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
This wires the chain walker into the association system proper, and adds
the entry point PR2 calls.
Resolution. `resolve_reference_chain` walks the chain, evaluates the
osnap feature in the entity's own coordinates, lifts it to model space
through the block path, and — when the chain goes through a viewport —
projects it onto the sheet with that viewport's `ViewportFrame`. Both
points are kept: the dimension holds paper points, the value it displays
is a model length. `ChainMap` is the one place that conversion lives,
including a `map_radial` that carries a radial source's plane into the
dimension's space so the radius/diameter/arc paths need no viewport
special-casing of their own. Which space a stored osnap point was
written in is not recoverable from the record — our own writer used
model/WCS, AutoCAD's trans-space records use the dimension's space, and
both turn up in the same drawing — so both readings are mapped down to
the entity and the one that lands on the geometry wins. It is only ever
a hint that disambiguates candidates, never the answer.
Creation. `Scene::attach_viewport_dimension_association` takes one
`AcceptedSnap` per definition point and writes the whole chain: viewport
handle, block path, entity, osnap type, sub-entity marker and parameter,
plus `trans_space` on the association. Every link becomes a reactor
target, not just the innermost entity, so a change anywhere along a
chain reaches the refresh through the ordinary dependency index.
Refresh. `refresh_associative_dimensions` now matches a changed handle
anywhere in a chain (including the second chain of an intersection), so
a viewport change reaches the dimensions drawn through it. Perpendicular
and tangent references resolve in a second pass, measured from the other
definition point. A dimension carries its sheet annotation with it: the
dimension-line standoff and a user-dragged text position move by the
centroid shift of the points that actually moved, so a one-ended edit
keeps the dimension line centred instead of collapsing it. For a
trans-space linear or aligned dimension the displayed value is
recomputed from the model points and multiplied only by the user's
DIMLFAC (`MeasurementScale::model_factor`) — the viewport compensation
is inherent in having measured in model space, and applying it again is
the double-scaling this three-PR effort exists to stop.
Broken references. Brokenness is derived, never written: a reference
whose source was erased simply stops resolving, the dimension keeps its
last valid appearance, and the record is left byte-for-byte as it was.
An undo that restores the source restores the link with no extra
bookkeeping. `Scene::dimension_association_status` reports it per slot.
`dimension_is_associative` now walks the chain too, instead of accepting
any surviving handle in `xrefs` — which was true even for a trans-space
reference whose actual geometry had gone, because the viewport was still
there.
Undo. Creating an association journals the new object and every reactor
list it touches; refreshing a dimension journals the dimension's
before-image. Both were previously invisible to the delta recorder.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ports FreeCAD's planegcs 2D geometric constraint solver to Rust as a new
crates/ocs_gcs workspace member, and wires it into the document/scene model
as a persistent SketchConstraintSet: Coincident, Horizontal/Vertical,
Parallel/Perpendicular, Equal, Tangent, Distance, Angle and Radius
constraints that re-solve live (including mid-drag, not just on release),
render glyphs and a DOF badge, flag conflicts with a one-click resolver
pill, and round-trip through save/load via an XRecord blob.
Also includes docs/command_reference.md (a statically generated reference
of every registered command and its prompt sequence) and
docs/named_parameters_design.md + docs/backlog.md, which hand off the next
queued feature (a named-parameter/expression table) and consolidate every
deliberate scope-down and open gap found along the way, so a fresh session
has a complete starting point.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>