refactor(scene): reference-only designation becomes a scene-scoped set (API v9)

Per the upstream review of the XDATA-based storage: the host no longer writes
its own state into the saved file. The marker record under the host-owned OCS
APPID, the on-first-use APPID registration and the verbatim-EED rewrite
handling are removed; the single source of truth is a scene-scoped in-memory
set of entity handles (Scene::reference_only) that dies with the drawing tab,
so a reopened file starts undesignated and the owning plugin re-marks on the
document-opened notification.

All behaviour sites are unchanged, driven by the set: the wire stamp moves
from an XDATA read to a set lookup threaded through the tessellation
pipelines (tessellate_entity and the dimension text path take the handle set
as a plain parameter), and the pick filters, selection retains and
update/erase refusals read Scene::is_reference_only.

Undo keeps the entity-write contract: set mutations are recorded as explicit
first-touch before/after values on each history delta (the parametric-
constraints channel pattern). A designation inside a plugin push_undo scope
joins it, a scope-less change is one step of its own, an idempotent re-mark
records nothing, erasing a designated entity drops the set entry with the
entity, and undo/redo restores entity and designation together.

The round-trip test is inverted: reference_only_designation_is_not_written_
to_dwg_or_dxf now asserts the marker appears in neither format's bytes, that
no OCS APPID or marker record survives a reload, and that the designation
stays live in memory.
This commit is contained in:
Sebastian committed 2026-10-14 21:12:17 +02:00
1 parent 06ee0798a8
commit 50db12b169
17 files changed
+623 -232

No files matched your search

+20 -7
View File
@@ -123,13 +123,23 @@ re-designation that would not change the state succeeds without recording an
undo step. Undo composition is the entity-write contract: a change inside a
plugin-opened `push_undo` scope joins it, so an operation composed of several
writes is a single undo step under the plugin's label; with no scope open a
change is one undo step of its own. The designation survives DWG and DXF
save/reload, stored as a marker value in an XDATA record under the host-owned
`OCS` application namespace — plugins register their own APPIDs through their
manifest, so the two cannot collide. Out of scope today: entities nested
inside block instances (the block cache rebuilds instance wires without the
flag) and a Python mapping. The MCP `entities` read exposes the flag as a
`reference_only` boolean on every entity record.
change is one undo step of its own.
**Storage is a scene-scoped in-memory set, not a file format.** The host
writes nothing of its own into the saved DWG/DXF — no APPID registration, no
XDATA marker on the entity — so a saved drawing never depends on host state
to load correctly. The set lives and dies with the drawing tab: closing a
tab forgets its designations, and a freshly opened drawing starts
undesignated. Because the designation is per handle, an in-place refresh
(clone → edit → replace) keeps it by construction. Undo keeps the contract
through an explicit designation channel on each history step (the same
pattern the parametric-constraint directory uses): undo/redo restores the
designation together with the entity it belongs to — erasing a designated
entity inside a scope, then undoing, brings back the entity *and* its
designation. Out of scope today: entities nested inside block instances
(the block cache rebuilds instance wires without the flag) and a Python
mapping. The MCP `entities` read exposes the flag as a `reference_only`
boolean on every entity record.
### Usage from a plugin
@@ -169,6 +179,9 @@ Notes for plugin authors:
no undo step.
- The designation travels with the entity: a clone → edit → replace refresh
keeps it, so a plugin marks its display data once and refreshes freely.
- The designation is scene state, not file state: it does not survive
DWG/DXF save and reload — a plugin re-marks its display data when the
document opens, and idempotent re-marks keep that loop finite.
- Rendering and object snap are unaffected; grips, picking and the built-in
write paths are the only observers.
- Plugins that read the document through the IPC client (the