forked from 1b-agent/opencadstudio-upstream
Add DWG-native constraint compatibility design doc
Hakan (upstream maintainer) asked for the sketch-constraint system to
persist using the constraint object model already exposed in acadrust/
cadcodec (Assoc2dConstraintGroup, AssocConstraintNode), so a file saved
by OpenCADStudio carries constraints AutoCAD/BricsCAD recognize as
their own native objects, not just an opaque custom XRecord blob.
Covers:
- The full ConstraintKind -> AssocConstraintNodeData mapping table,
grounded in build_constraint's actual refs shapes (the real contract,
not the doc comments).
- The geometry-node mapping for SketchRef (line/circle/arc/point
references) onto Line/BoundedLine/Circle/Arc/ImplicitPoint nodes.
- The AssocNetwork -> AssocAction -> Assoc2dConstraintGroup graph shape
actually needed, not just a flat data swap.
- A proposed additive persistence strategy: write both our own
OCS_SKETCH_CONSTRAINTS XRecord and the native graph, since our model
carries things (DrivingValue::Named) the native graph can't yet
losslessly express.
- Known gaps: Equal has no length/radius sub-kind split natively,
several AutoCAD constraint types we don't model at all, Tangent
involving an Arc (a pre-existing gap in the constraint system,
unrelated to DWG compatibility).
Three of five open questions are resolved against Autodesk's own
primary sources rather than left as inference: the ObjectARX 2012 SDK's
own C++ headers (mirrored on GitHub, Autodesk's own copyright notice
grants free use/distribution -- it's the SDK Autodesk publishes for
third-party interop) and Autodesk's live public ObjectARX reference
site. Confirmed: the associative network's dictionary key
("ACAD_ASSOCNETWORK"), the AcDb::ImplicitPointType enum settling how
SketchRef's point markers map to constraint geometry nodes, and that
AssocVariable's evaluator field can be left empty (Autodesk's own doc:
"the default evaluator for current acad version will be used"). Two
questions remain and need a real write-then-open test rather than more
reading: whether AutoCAD tolerates a partially-populated graph, and
DWG version gating on fields not yet checked.
Suggested staged approach: prove out Horizontal/Vertical on a single
Line first (sidesteps the value-dependency wiring), verify it opens
cleanly in real AutoCAD/BricsCAD or a DWG inspector, before writing
more of the graph on an unverified foundation. Design only -- no
implementation yet.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
7cddf31bcb
commit
5d93476c01
1 file changed
+352
@@ -0,0 +1,352 @@
|
||||
# DWG-Native Constraint Compatibility — Design
|
||||
|
||||
## 1. What this is, and why
|
||||
|
||||
Hakan (upstream maintainer) reviewed the sketch-constraint system
|
||||
(`docs/parametric_system_design.md`) and asked for it to persist using the
|
||||
constraint object model already exposed in `acadrust`/`cadcodec` (the pinned
|
||||
CAD kernel), so a file saved by OpenCADStudio carries constraints AutoCAD or
|
||||
BricsCAD would recognize as their own native objects — not just an opaque
|
||||
custom blob only this app understands.
|
||||
|
||||
This is purely a **persistence-format** question, not a solver question.
|
||||
`ocs_gcs` (the FreeCAD `planegcs` port: `crates/ocs_gcs/src/solvers/{bfgs,dogleg,lm}.rs`)
|
||||
is the numerical engine that computes geometry from a constraint system —
|
||||
that stays exactly as it is regardless of what this document decides. What
|
||||
changes is only how `SketchConstraintSet` gets written to and read from a
|
||||
`CadDocument`, in `src/scene/sketch_persist.rs`, currently a custom
|
||||
`bincode`-in-XRecord blob keyed `OCS_SKETCH_CONSTRAINTS`.
|
||||
|
||||
## 2. What `acadrust` already provides (confirmed from source)
|
||||
|
||||
All paths below are relative to the pinned checkout
|
||||
(`~/.cargo/git/checkouts/cadcodec-*/*/src/`, rev pinned in `Cargo.toml`).
|
||||
|
||||
- `objects/associative.rs` defines AutoCAD's real 2D geometric-constraint
|
||||
object graph as plain Rust types, reachable generically through
|
||||
`ObjectType::Associative(AssociativeObject { data: AssociativeData::ConstraintGroup(Assoc2dConstraintGroup), .. })`
|
||||
— the same `document.objects: HashMap<Handle, ObjectType>` map
|
||||
`sketch_persist.rs` already inserts `XRecord`/`Dictionary` entries into.
|
||||
- Both DWG (`io/dwg/dwg_stream_{readers,writers}/object_{reader,writer}/associative.rs`,
|
||||
~1240/~870 lines) and DXF (`io/dxf/{reader,writer}/section_{reader,writer}/associative.rs`)
|
||||
have **complete read and write** implementations for every type below —
|
||||
confirmed by reading the actual field-by-field (de)serialization code, not
|
||||
inferred from the type definitions alone.
|
||||
- `classes/mod.rs` registers the DXF class names every one of these needs
|
||||
when written (`(DXF_NAME, "AcDbClassName", ...)` tuples):
|
||||
`AcDbAssocDependency`, `AcDbAssocGeomDependency`, `AcDbAssocAction`,
|
||||
`AcDbAssocNetwork`, `AcDbAssoc2dConstraintGroup`, `AcDbAssocVariable`,
|
||||
`AcDbAssocPersSubentManager`.
|
||||
|
||||
### The graph shape
|
||||
|
||||
Not a flat list — a small object graph, matching AutoCAD's real associative
|
||||
framework:
|
||||
|
||||
```
|
||||
AssocNetwork (per block/space that owns constrained geometry)
|
||||
└─ owned_actions: Vec<Handle> ─→ AssocAction
|
||||
├─ action_body: Handle ─→ Assoc2dConstraintGroup
|
||||
│ ├─ work_plane: [Vector3; 3]
|
||||
│ └─ nodes: Vec<AssocConstraintNode>
|
||||
│ (both the *constraint* nodes
|
||||
│ and the *geometry* nodes live
|
||||
│ in this one Vec — see §3/§4)
|
||||
└─ dependencies: Vec<AssocActionDependency>
|
||||
─→ AssocGeomDependency
|
||||
├─ dependency: AssocDependency
|
||||
│ └─ dependent_on: Handle
|
||||
│ (the real Line/Circle/... entity)
|
||||
└─ persistent_subent: AssocPersistentSubentId
|
||||
```
|
||||
|
||||
`AssocConstraintNode { node_id: i32, status: u8, connections: Vec<i32>,
|
||||
class_name: String, registry_flag: bool, data: AssocConstraintNodeData }` —
|
||||
`node_id`/`connections` link nodes to each other *within* the group (a
|
||||
constraint node's `owner_id` fields point at a geometry node's `node_id`);
|
||||
`class_name` picks which `AssocConstraintNodeData` variant applies on
|
||||
read (`read_constraint_node_data(reader, class_name)`,
|
||||
`dwg_stream_readers/object_reader/associative.rs:663`).
|
||||
|
||||
### 2b. Primary sources beyond `cadcodec`'s own code
|
||||
|
||||
`cadcodec` ships no constraint-bearing test fixture and its own source only
|
||||
shows *what bytes get read*, not *what they mean* semantically — §7 (now
|
||||
mostly resolved) needed Autodesk's own interface documentation to answer.
|
||||
Two real, first-party sources turned out to be publicly available with no
|
||||
sample DWG file needed at all:
|
||||
|
||||
- **The ObjectARX 2012 SDK's own C++ headers**, mirrored on GitHub at
|
||||
[Stalso/CadPlugin](https://github.com/Stalso/CadPlugin/tree/master/ObjectARX%202012/inc)
|
||||
— Autodesk's own copyright header on each file explicitly grants "permission
|
||||
to use, copy, modify, and distribute this software... for any purpose and
|
||||
without fee", and this is the SDK Autodesk itself publishes specifically so
|
||||
third parties can build interoperable tools — not leaked or decompiled
|
||||
source. `AcDbAssoc2dConstraintGroup.h`, `AcDbAssocNetwork.h`,
|
||||
`AcDbAssocManager.h`, `AcDbAssocVariable.h`, `AcConstrainedGeometry.h` were
|
||||
all read directly; their class-level doc comments describe the exact
|
||||
same object graph `cadcodec` implements, in Autodesk's own words.
|
||||
- **Autodesk's live public ObjectARX reference**
|
||||
(`help.autodesk.com/view/OARXMAC/2025/...`) — confirmed the
|
||||
`AcDb::ImplicitPointType` enum's exact values and per-member descriptions
|
||||
(used for §4's `point_type` mapping) and `AcDbAssocNetwork`'s
|
||||
`getInstanceFromObject`/`getInstanceFromDatabase` dictionary-key default
|
||||
(used for §7 question 1). No login or SDK download needed — the reference
|
||||
guide is indexed and publicly browsable.
|
||||
|
||||
## 3. Constraint-kind mapping
|
||||
|
||||
Our `ConstraintKind` (`src/scene/sketch_constraints.rs`) against
|
||||
`AssocConstraintNodeData` (`objects/associative.rs:885`). The "refs shape"
|
||||
column is copied from `sketch_solve::build_constraint`'s actual `match`
|
||||
(`src/scene/sketch_solve.rs:151-208`) — the ground truth for what each kind
|
||||
expects, not the doc comment.
|
||||
|
||||
| `ConstraintKind` | refs shape (from `build_constraint`) | `AssocConstraintNodeData` | Notes |
|
||||
|---|---|---|---|
|
||||
| `Coincident` | `[point, point]` | `Geometrical` + `class_name = "ACPOINTCOINCIDENCECONSTRAINT"` | |
|
||||
| `Horizontal` | `[whole_line]` | `Geometrical` + `"ACHORIZONTALCONSTRAINT"` | |
|
||||
| `Vertical` | `[whole_line]` | `Geometrical` + `"ACVERTICALCONSTRAINT"` | |
|
||||
| `Parallel` | `[whole_line, whole_line]` | `Parallel { datum_line_index: None, .. }` | dedicated variant, not `Geometrical` |
|
||||
| `Perpendicular` | `[whole_line, whole_line]` | `Geometrical` + `"ACPERPENDICULARCONSTRAINT"` | |
|
||||
| `Equal` (line case) | `[whole_line, whole_line]` | `Geometrical` + `"ACEQUALLENGTHCONSTRAINT"` | |
|
||||
| `Equal` (circle/arc case) | `[whole_circle, whole_circle]` | `Geometrical` + `"ACEQUALRADIUSCONSTRAINT"` | **our `Equal` doesn't distinguish these — see §5** |
|
||||
| `Distance` | `[point, point]` | `Distance { direction_type, distance: Option<Vector3>, value_dependency, dimension_dependency, .. }` | needs an `AssocVariable`/value dependency for the driving value — see §5 |
|
||||
| `Angle` | `[whole_line, whole_line]` | `Angle { sector_type, value_dependency, dimension_dependency, .. }` | same value-dependency need |
|
||||
| `Radius` | `[whole_circle]` | `RadiusDiameter { mode, value_dependency, dimension_dependency, .. }` | `mode` presumably distinguishes radius vs. diameter — we're always radius |
|
||||
| `Tangent` | `[circle, circle]` or `[circle, line]`/`[line, circle]` — Line-Line and anything involving an `Arc` are explicitly unsupported today (`sketch_solve.rs:239-241`, `Arc` isn't a supported `EntityGeom` variant yet) | `Geometrical` + `"ACTANGENTCONSTRAINT"` | our own `Arc`-tangent gap predates this document, unrelated to DWG compatibility |
|
||||
|
||||
AutoCAD's `is_plain_geometrical_constraint` list also includes several kinds
|
||||
we have no `ConstraintKind` for at all: `ACCENTERPOINTCONSTRAINT`,
|
||||
`ACCOLINEARCONSTRAINT`, `ACCONCENTRICCONSTRAINT`, `ACEQUALCURVATURECONSTRAINT`,
|
||||
`ACEQUALDISTANCECONSTRAINT`, `ACEQUALHELPPARAMETERCONSTRAINT`,
|
||||
`ACFIXEDCONSTRAINT`, `ACMIDPOINTCONSTRAINT`, `ACNORMALCONSTRAINT`,
|
||||
`ACPOINTCURVECONSTRAINT`, `ACSYMMETRICCONSTRAINT` — genuinely nothing to map
|
||||
to yet, not a mapping gap (§5).
|
||||
|
||||
## 4. Geometry-node mapping
|
||||
|
||||
Every entity a constraint references also needs its own node in the same
|
||||
`nodes: Vec<AssocConstraintNode>`, so constraint nodes have something to
|
||||
point `owner_id` at, *and* a paired `AssocGeomDependency` (in the owning
|
||||
`AssocAction`'s `dependencies`) so the graph — not just the constraint data
|
||||
— actually references the real `Line`/`Circle`/... entity by `Handle`.
|
||||
|
||||
| Our reference | `AssocConstraintNodeData` geometry variant |
|
||||
|---|---|
|
||||
| `SketchRef::whole(line)` | `BoundedLine { point, direction, is_ray: false, start_point, end_point, .. }` — confirmed a *bounded* line is the right choice: `Line`/`BoundedLine`'s split exists because AutoCAD's own constrained-geometry set includes true infinite construction lines (`ACCONSTRAINEDCONSTRUCTIONLINE`/`ACCONSTRAINEDDATUMLINE`, `read_constraint_node_data`, `associative.rs:711-714`) as a *separate* case from an ordinary bounded segment; nothing we draw is an infinite line |
|
||||
| `SketchRef::point(line, 0\|1)` | `ImplicitPoint { point_type: kStartImplicit(0)\|kEndImplicit(1), point_index: -1, curve_id: <the Line/BoundedLine node's node_id>, .. }` — **confirmed**, `AcDb::ImplicitPointType` enum, `kStartImplicit`/`kEndImplicit`/`kMidImplicit`/`kCenterImplicit`/`kDefineImplicit` (0-4), Autodesk's own live ObjectARX reference (§2b). `point_index` is `-1` for every case except `kDefineImplicit` (a spline control point, which we don't reference); `curve_id` is presumably the owning geometry node's own `node_id` (not yet cross-checked against a real file — the *values* are confirmed, this one wiring detail isn't) |
|
||||
| `SketchRef::whole(circle)` | `Circle { center, normal, direction, radius, start_parameter: 0.0, end_parameter: TAU, .. }` |
|
||||
| `SketchRef::whole(arc)` | `Arc { .. same fields .. , start_point, end_point }` |
|
||||
| `SketchRef::center(circle\|arc)` | `ImplicitPoint { point_type: kCenterImplicit(3), point_index: -1, curve_id: <the Circle/Arc node's node_id>, .. }` — confirmed by the same enum (`kCenterImplicit`: "Center point of a circle, arc, ellipse or bounded ellipse") — **not** the separate `Point`/`ACCONSTRAINEDPOINT` variant, which the ObjectARX docs' own class hierarchy (§2b) show is for a *free-standing* constrained point, not one implicitly defined by another curve |
|
||||
| `SketchRef::point(_, -2)` (reserved, unused today) | would be `kMidImplicit` if ever used — "Mid point of a bounded line ... or arc" is exactly what marker `-2`'s doc comment already reserved this for |
|
||||
|
||||
`SketchRef` and this graph already agree on one thing directly: our own doc
|
||||
comment (`src/scene/sketch_constraints.rs:20-22`) says the `marker`
|
||||
convention was deliberately borrowed from
|
||||
`AssocDimensionReference::main_gs_marker` (`src/scene/dimension_assoc.rs`,
|
||||
already shipping, already reading/writing real `Assoc*` objects for
|
||||
associative dimensions). That's a different, lighter mechanism than the
|
||||
constraint graph (an `AcDbOsnapPointRef` chain attached directly to a
|
||||
`Dimension` entity, not the `AssocNetwork`/`AssocAction` framework — see
|
||||
`AssocDimensionAssociation`, `objects/associative.rs:1200`), so it's a
|
||||
precedent for *how this codebase already talks to `Assoc*` types*, not a
|
||||
template for the constraint graph's own wiring.
|
||||
|
||||
## 5. Known gaps (not addressed by this document)
|
||||
|
||||
- **`Equal` is one `ConstraintKind`, AutoCAD has (at least) two**:
|
||||
`ACEQUALLENGTHCONSTRAINT` (lines) vs. `ACEQUALRADIUSCONSTRAINT`
|
||||
(circles/arcs), plus `ACEQUALCURVATURECONSTRAINT`/
|
||||
`ACEQUALDISTANCECONSTRAINT` we don't model at all. `build_constraint`
|
||||
already branches on `refs`' resolved shape at solve time
|
||||
(`sketch_solve.rs:181-188`), so the same branch can pick the write-side
|
||||
`class_name` — no data-model change needed for the length/radius split;
|
||||
the other two AutoCAD sub-kinds are a real, separate gap.
|
||||
- **`DrivingValue::Named` — confirmed, has a real native equivalent, and the
|
||||
full wiring is now traced end to end.**
|
||||
`AcDbAssocVariable` (`objects/associative.rs:1024`: `name`, `expression`,
|
||||
`evaluator`, `description`, cached `value`) is fully read *and* written on
|
||||
both DWG and DXF (confirmed in
|
||||
`dwg_stream_readers/object_reader/associative.rs:1031-1063` and the DXF
|
||||
reader/writer equivalents) — it's the same associative-action graph a
|
||||
constraint group lives in (`AssocVariable` wraps an `AssocAction`, exactly
|
||||
like `Assoc2dConstraintGroup` does), not a separate table.
|
||||
|
||||
The two handles on `Distance`/`Angle`/`RadiusDiameter`
|
||||
(`read_explicit_constraint`, `associative.rs:652-661` — reads them in this
|
||||
order: `value_dependency` first, `dimension_dependency` second) resolve
|
||||
to two different, unrelated things:
|
||||
|
||||
- **`value_dependency`** → an `AssocValueDependency` object
|
||||
(`{ dependency: AssocDependency, name, value: AssocEvalVariant }`,
|
||||
`objects/associative.rs:554`). Its own `value: AssocEvalVariant`
|
||||
(`{ code: i16 /* DXF resbuf type */, value: AssocEvalValue::Real(f64) }`,
|
||||
confirmed `associative.rs:579-596`) is the **cached, already-resolved
|
||||
number** — always present, read fast without evaluating anything. The
|
||||
embedded `AssocDependency.dependent_on: Handle`
|
||||
(`read_dependency`, `associative.rs:83-109`) is what actually links to a
|
||||
named parameter: `Handle::NULL` when the driving value is a bare
|
||||
literal, or an `AssocVariable`'s handle when it's driven by one. This
|
||||
gives a direct, confirmed mapping:
|
||||
- `DrivingValue::Literal(v)` → `AssocValueDependency` with
|
||||
`value = Real(v)`, `dependency.dependent_on = Handle::NULL`.
|
||||
- `DrivingValue::Named(name)` → the same, but `dependency.dependent_on`
|
||||
points at an `AssocVariable{ name, expression: <our formula>, .. }`
|
||||
object (create once per distinct name, reused by every constraint
|
||||
referencing it — `value` still carries the current resolved number,
|
||||
kept in sync with `ParameterTable`'s last evaluation, same duty our
|
||||
own `dof`/`conflicts` caches already have).
|
||||
- **`dimension_dependency`** → a *separate* dependency chain
|
||||
(`AssocAnnotationDependency`/`AssocDimDependencyBody`,
|
||||
`associative.rs:744-751` and `:1077-1082`) linking to the actual
|
||||
`Dimension` entity AutoCAD draws next to a constraint to show its
|
||||
current value on screen — nothing to do with named parameters at all.
|
||||
Not required for our purposes unless we also want the constraint to
|
||||
carry a visible on-canvas dimension the way AutoCAD's does (open
|
||||
question, not yet decided — §7).
|
||||
|
||||
Remaining gap before mapping directly: AutoCAD's `expression` is
|
||||
evaluated by a named, extensible `evaluator` (not fixed) — real-file
|
||||
survey needed to know what evaluator name(s) actually appear in practice
|
||||
(§7 open question 4), ours has one hardcoded evaluator.
|
||||
- **Missing `ConstraintKind`s**: Concentric, Colinear, Symmetric, Fixed,
|
||||
Midpoint, Normal, CenterPoint, PointOnCurve — real AutoCAD constraint
|
||||
types with no `ConstraintKind` variant to map from at all. Out of scope
|
||||
until/unless the sketch-constraint system itself grows them.
|
||||
- **`Tangent` involving an `Arc`** has no native mapping to design, since
|
||||
`build_constraint` doesn't support it at all yet (`sketch_solve.rs:239-241`
|
||||
— `Arc` isn't a supported `EntityGeom` variant in that function). A
|
||||
pre-existing gap in the constraint system itself, unrelated to and not
|
||||
blocking this document.
|
||||
|
||||
## 6. Persistence architecture: additive, not a replacement
|
||||
|
||||
**Proposal: write both.** Keep `OCS_SKETCH_CONSTRAINTS` as the authoritative
|
||||
round-trip format OpenCADStudio itself reads back
|
||||
(`sketch_persist::load_sketch_constraints_from_document`), and *additionally*
|
||||
materialize the `AssocNetwork`/.../`Assoc2dConstraintGroup` graph alongside
|
||||
it on save, for other applications to read.
|
||||
|
||||
Reasoning:
|
||||
- Our own model carries information the native graph can't represent
|
||||
losslessly today — `DrivingValue::Named` chief among them (§5), also
|
||||
`enabled` (a suppressed-but-kept constraint) which may or may not have a
|
||||
clean native equivalent (`AssocConstraintNodeData`'s `is_active` field on
|
||||
several variants looks like the right fit, but unconfirmed, §7).
|
||||
Round-tripping through the native format only and reconstructing our
|
||||
model from it risks silent data loss on every save/reopen cycle.
|
||||
- The native graph's own geometry-node fields (`center`, `radius`, `point`,
|
||||
...) are *snapshots* of the current solved geometry, not derived at load
|
||||
time — so they need to be kept in sync with whatever `ocs_gcs` last
|
||||
solved, same as we already do for our own tessellated wire geometry. Two
|
||||
representations of "the same" constraint, kept consistent by one save
|
||||
path, is a smaller invariant to maintain than trying to make the native
|
||||
graph the *only* source of truth for a system it can't fully express.
|
||||
- This is reversible: if the native graph turns out to work well enough
|
||||
(once tested against real AutoCAD/BricsCAD opens, §7) to *become* the
|
||||
primary format later, dropping the redundant `OCS_SKETCH_CONSTRAINTS`
|
||||
write is a small follow-up, not a rewrite.
|
||||
|
||||
## 7. Open questions
|
||||
|
||||
Three of the original five are now resolved from Autodesk's own primary
|
||||
sources (§2b), not just inferred from `cadcodec`'s struct shapes — kept
|
||||
here with their answers so the reasoning stays visible, not deleted.
|
||||
`cadcodec` itself still ships no constraint-bearing DWG/DXF fixture and no
|
||||
constraint-specific test (checked: no hits for `Assoc2dConstraintGroup`/
|
||||
`AssocConstraintNode` under any `tests/` directory or fixture path in the
|
||||
dependency), so even the resolved ones haven't been checked against a real
|
||||
written-and-reopened file yet — that step still matters (§8 stage 1) for
|
||||
confirming implementation correctness, just not for the *design* questions
|
||||
below anymore.
|
||||
|
||||
1. **Resolved.** Where the top-level `AssocNetwork` attaches:
|
||||
`AcDbAssocNetwork::getInstanceFromDatabase`/`getInstanceFromObject`'s own
|
||||
doc comments (§2b) state the default sub-dictionary key directly —
|
||||
`"ACAD_ASSOCNETWORK"` — used both for the one top-level network (owned by
|
||||
the database's Named Object Dictionary) and for each per-block-record
|
||||
network (owned by that `AcDbBlockTableRecord`'s own extension
|
||||
dictionary — confirming the parallel to `OCS_SKETCH_CONSTRAINTS`'s own
|
||||
attachment was right). The two are linked: creating a per-block network
|
||||
with `addToTopLevelNetwork = true` (the default) also registers it as an
|
||||
action inside the top-level network, so evaluating the top-level network
|
||||
cascades through every block's sub-network.
|
||||
2. **Resolved.** Sub-entity addressing for `ImplicitPoint`: confirmed the
|
||||
real addressing lives entirely in the constraint-node's own
|
||||
`point_type`/`point_index`/`curve_id` fields, not in
|
||||
`AssocPersistentSubentId` (that type turned out to be an abstract C++
|
||||
base class for a *different*, more general subentity-tracking need
|
||||
elsewhere in the associative framework — irrelevant to 2D constraint
|
||||
geometry). `AcDb::ImplicitPointType`'s exact enum (§4) settles the
|
||||
mapping.
|
||||
3. **Still open.** Whether a partially-populated graph (e.g. omitting
|
||||
`AssocPersSubentManager`, or leaving some `AssocAction` bookkeeping
|
||||
fields at their `Default`) still opens cleanly in real AutoCAD, or
|
||||
whether it requires every field correctly populated to avoid a repair
|
||||
prompt or silent constraint loss. Genuinely needs a real write-then-open
|
||||
test, not documentation — this is a robustness/tolerance question, not a
|
||||
structural one.
|
||||
4. **Narrowed to almost nothing.** `AssocVariable`'s `evaluator` field:
|
||||
`AcDbAssocVariable.h`'s own doc comment (§2b) says plainly — "If empty,
|
||||
the default evaluator for current acad version will be used." Leaving it
|
||||
as an empty string is officially-supported behavior per the SDK itself,
|
||||
not a guess. No further investigation needed unless a real file later
|
||||
shows this doesn't round-trip as expected.
|
||||
5. **Still open.** DWG version gating — `AssocConstraintNode`'s own reader
|
||||
already branches on `version.r2013_plus(dxf_version)` for one field
|
||||
(`dwg_stream_readers/object_reader/associative.rs:604`); unclear whether
|
||||
other fields need per-version handling this document hasn't looked for.
|
||||
Likely answerable the same way as questions 1/2/4 were (Autodesk's own
|
||||
docs usually note version-specific members) — just not done yet.
|
||||
6. **Still open, low priority.** Whether to bother wiring
|
||||
`dimension_dependency` at all (§5) — it drives the small on-canvas
|
||||
dimension label AutoCAD shows next to a constrained value, cosmetic
|
||||
rather than structural. Leaving it `Handle::NULL` is presumably safe (the
|
||||
constraint itself is still fully defined without it) but unconfirmed;
|
||||
deferring this to a later stage either way (§8).
|
||||
|
||||
## 8. Suggested staged approach
|
||||
|
||||
1. **Proof of concept, narrowest possible slice**: `Horizontal`/`Vertical`
|
||||
on a single `Line`, no dimensional constraints (sidesteps the
|
||||
`AssocVariable`/value-dependency wiring entirely, though that's now fully
|
||||
designed too — §5). Write the graph — `AssocNetwork` at
|
||||
`"ACAD_ASSOCNETWORK"` on the scope owner's extension dictionary (§7.1),
|
||||
one `Assoc2dConstraintGroup` action, one `BoundedLine` geometry node, one
|
||||
`Geometrical`/`"ACHORIZONTALCONSTRAINT"` (or vertical) constraint node —
|
||||
and open the file in whatever real-DWG-reading tool is available, to
|
||||
confirm AutoCAD/BricsCAD actually shows a constraint icon on the line and
|
||||
doesn't flag the file as corrupt. This is what answers open question 3
|
||||
(partial-graph tolerance) — the one remaining question documentation
|
||||
alone can't settle.
|
||||
2. **Geometric constraints, still no driving value**: add `Coincident`
|
||||
(needs `ImplicitPoint`/`kStartImplicit`/`kEndImplicit`, §4, now
|
||||
confirmed), `Parallel`, `Perpendicular`, `Equal` (both sub-kinds) —
|
||||
verify against the same real-file test from stage 1.
|
||||
3. **Dimensional constraints**: `Distance`, `Angle`, `Radius`, using the now
|
||||
fully-traced `value_dependency → AssocValueDependency → AssocVariable`
|
||||
chain (§5) — including deciding whether `DrivingValue::Named` gets the
|
||||
real native mapping designed there, or stays a resolved-literal degrade
|
||||
for a first pass.
|
||||
4. **Everything else**: `Tangent`'s non-circle-circle cases, the missing
|
||||
`ConstraintKind`s (§5) if/when the sketch-constraint system grows them,
|
||||
`dimension_dependency` (§7.6) if a visible on-canvas dimension label
|
||||
turns out to matter.
|
||||
|
||||
Each stage's file should be hand-inspected (or opened in real software)
|
||||
before moving to the next — the graph's correctness genuinely can't be
|
||||
verified by this codebase's own round-trip tests alone, since a bug that's
|
||||
symmetric (write it wrong, read the same wrongness back) would pass here and
|
||||
still fail to open correctly elsewhere.
|
||||
|
||||
## 9. Progress
|
||||
|
||||
Design only — no implementation yet. Of the original five open questions,
|
||||
three are now resolved from Autodesk's own primary documentation (§7.1, 7.2,
|
||||
7.4) rather than inference — the network's dictionary key, `ImplicitPoint`'s
|
||||
addressing enum, and the `AssocVariable` evaluator default are no longer
|
||||
guesses. Two remain (§7.3 partial-graph tolerance, §7.5 version gating) and
|
||||
both genuinely need a real write-then-open test rather than more reading.
|
||||
Next action per §8 is stage 1's narrowest slice — the design for it no
|
||||
longer rests on an unconfirmed foundation, only on an unverified one.
|
||||
Reference in new issue
Block a user