forked from 1b-agent/opencadstudio-upstream
Complete Viewport: layout-tab canvas oracle, unique ids, derived scale
The shared driver now activates the layout that owns a viewport and requires a drawn frame. The host assigns a unique id of at least 2 and derives custom_scale as height / view_height on every scripted create or edit, as MVIEW does, so custom_scale is read-only. A control showed the default id 0 is not invisible, so the id matters for uniqueness. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5e6a830782
commit
6e2e073e29
6 files changed
+106
-12
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# OCS canvas kind coverage ledger
|
||||
|
||||
Use with [the completion plan](plugin-host-model-completion-plan.md). This ledger is current as of 20 September 2026: **43 of 43 canvas kinds have explicit geometry writes** (41 creatable, plus update-only legacy `Polyline` and `Body`), **30 kinds are complete**, and none remains layer-only. `Mapped` means at least one non-layer field is exposed for Python writes; it does **not** mean the kind passes the completion gate. `Update-only` means Python creation is unavailable. `Layer-only` means raw inspection and canvas layer edits exist, but geometry is not mapped. Record actual evidence or a blocking issue before changing any status to `Complete`.
|
||||
Use with [the completion plan](plugin-host-model-completion-plan.md). This ledger is current as of 20 September 2026: **43 of 43 canvas kinds have explicit geometry writes** (41 creatable, plus update-only legacy `Polyline` and `Body`), **31 kinds are complete**, and none remains layer-only. `Mapped` means at least one non-layer field is exposed for Python writes; it does **not** mean the kind passes the completion gate. `Update-only` means Python creation is unavailable. `Layer-only` means raw inspection and canvas layer edits exist, but geometry is not mapped. Record actual evidence or a blocking issue before changing any status to `Complete`.
|
||||
|
||||
Completion gate codes: **C** = create through Python document model; **R** = read; **E** = edit defining geometry and another property; **D** = delete; **U** = undo/redo and atomic failure; **I** = real IPC plus GUI/canvas pick or observable behavior; **W** = DWG and DXF save/reopen; **V** = invalid-input checks and untouched-field/reference preservation; **P** = build and load the in-tree bundled plugin with a manifest matching the host toolchain and acadrust source. A kind is `Complete` only when **C/R/E/D/U/I/W/V/P** all pass and its property catalog matches implementation. `Blocked` requires a specific dependency or failing fixture.
|
||||
|
||||
@@ -40,7 +40,7 @@ Completion gate codes: **C** = create through Python document model; **R** = rea
|
||||
| RasterImage | **Integration-tested** | Writable: `file_path` (creation only; a later change is rejected because the image definition owns it), `insertion_point`, `u_vector`, `v_vector` (world units per pixel), `flags`, `clipping_enabled`, `brightness`, `contrast`, `fade`, `clip_boundary`. Read-only: `size` (pixel size, taken from the file), `class_version`, `definition_handle`, `definition_reactor_handle`. Creating from a script requires a readable image file: the host reads its pixel size, creates the `ImageDefinition` object, links it, and sets `size` and a whole-image clip boundary from the file. Host validation requires finite, non-parallel `u`/`v` vectors, brightness/contrast/fade within 0..=100, known display flags, finite clip vertices, at least 2 clip vertices when clipping is enabled, and an existing definition. `staged_python_raster_image_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real PNG fixture) passes C/R/E/D/U/I/W/V/P inside OCS: creation with a host-made definition, placement/scale/brightness/contrast/fade and clip edit in one transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG and DXF reopen with the definition link and file path intact, re-edit, and three-step undo/redo. **Why not `Complete`:** the host does not create an `ACAD_IMAGE_DICT` entry or `IMAGEDEF_REACTOR` for a scripted image, so other CAD applications may need to repair the linkage; that cannot be verified here. Editing an existing definition (file path, resolution) is out of scope. |
|
||||
| Wipeout | **Complete** | Writable: `insertion_point`, `u_vector`, `v_vector` (the region's world extent), `size`, `flags`, `clipping_enabled`, `brightness`, `contrast`, `fade`, `clip_mode`, `clip_type` and `clip_boundary_vertices` (normalized image coordinates, -0.5..0.5). Read-only: `class_version`, `definition_handle`, `definition_reactor_handle`. Scripted creation matches OCS's native WIPEOUT command, which also leaves the definition link unset. Host validation requires finite non-parallel `u`/`v` vectors, a positive `size`, brightness/contrast/fade within 0..=100, known flags, finite clip vertices, exactly 2 vertices for a rectangular clip and at least 3 for a polygonal one. `staged_python_wipeout_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/W/V/P: rectangular creation, a switch to a three-vertex polygonal inside-clip with a move in one transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. |
|
||||
| Underlay | **Integration-tested / blocked at W by acadrust (DXF rotation units)** | Writable: `underlay_type` and `definition_handle` (both fixed by the definition: creation names an existing `UnderlayDefinition`, and a mismatched type or missing definition is rejected), `insertion_point`, `x_scale`, `y_scale`, `z_scale`, `rotation`, `normal`, `flags`, `contrast`, `fade`, `clip_boundary_vertices`, `clip_inverted`. The file path and page live on the definition object, which scripts cannot create or list yet; they reference one the drawing already owns. Host validation requires finite nonzero scales, a finite rotation, a nonzero normal, contrast/fade within 0..=100, known flags, finite clip vertices (none, or at least 2) and an existing definition of the same type. `staged_python_underlay_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle` with a fixture PDF definition) passes C/R/E/D/U/I/V/P and DWG W: creation, a placement/scale/rotation/contrast/fade/clip transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W blocker (engine, pinned by canary assertions):** DXF stores the rotation in degrees but the reader returns it unconverted, so 0.5 rad reopens as 28.6 and each further DXF save-reopen compounds it (1641.4 after a second), silently corrupting the rotation. Not `Complete` until the reader converts degrees back to radians. Not covered: creating definitions from a file path, DWF/DGN fixtures. |
|
||||
| Viewport | **Integration-tested** | Writable: `center`, `width`, `height`, `status`, `view_center`, `view_direction`, `view_target`, `lens_length`, `front_clip_z`, `back_clip_z`, `view_height`, `twist_angle`, `custom_scale`, `frozen_layers` (layer handles), `render_mode`, `circle_sides`, `ucs_icon_visible`. Read-only: `id`, snap/grid settings, UCS fields, `clip_boundary_handle`, `style_sheet`, shade-plot, background, visual-style and sun handles, lighting, `ambient_color`, `elevation`. Reference rules: the owner must be a paper-space layout block (a model-space viewport is rejected) and every frozen-layer handle must be an existing layer. Host validation requires finite centers, positive width, height, view height, lens length and scale, a nonzero view direction, finite clip planes and twist, and at least 3 circle sides. `staged_python_viewport_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, paper-space owner supplied by the driver) passes C/R/E/D/U/V/W/P: creation in the paper-space block, a move/resize/view-height/twist/frozen-layer transaction, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. **Why not `Complete`:** the I gate lacks a canvas oracle because a paper-space viewport only draws on its layout tab, which the driver does not activate; viewport `id` uniqueness within a layout, clip boundary entities and view-to-scale synchronization are not handled by the host. |
|
||||
| Viewport | **Complete (OCS round trip)** | Writable: `center`, `width`, `height`, `status`, `view_center`, `view_direction`, `view_target`, `lens_length`, `front_clip_z`, `back_clip_z`, `view_height`, `twist_angle`, `frozen_layers` (layer handles), `render_mode`, `circle_sides`, `ucs_icon_visible`. Read-only: `id`, `custom_scale`, snap and grid settings, UCS fields, `clip_boundary_handle`, `style_sheet`, shade-plot, background, visual-style and sun handles, lighting, `ambient_color`, `elevation`. Reference rules: the owner must be a paper-space layout block and every frozen-layer handle an existing layer. **Derived by the host (new 2026-09-20), as the MVIEW command does:** a scripted viewport gets an `id` unique in its layout and at least 2 (max existing plus one), and `custom_scale` is `height / view_height`, recomputed whenever either changes, so `custom_scale` is now read-only (setting the scale directly would have let it drift from the geometry). Host validation requires finite centers, positive width, height, view height, lens length and scale, a nonzero view direction, finite clip planes and twist, and at least 3 circle sides. **Canvas oracle (new):** the shared driver now activates the layout that owns the viewport (`Scene::set_current_layout`) and requires the viewport to draw a frame both after creation and after the edit. `staged_python_viewport_lifecycle_over_real_ipc` passes C/R/E/D/U/I/V/W/P: creation in the paper-space block, a move/resize/view-height/twist/frozen-layer transaction, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. `audit_python_viewport_ids_and_scale_over_real_ipc` checks unique ids (2, 3, 4), the derived scale on create and after a zoom and a resize, an id kept across edits, a rejected direct `custom_scale` write, and that every viewport draws a frame. A control showed that the engine default id 0 is **not** invisible in a layout with no sheet viewport (it draws a frame), which corrects an earlier suspicion; the derived id matters for uniqueness. Not covered: clip boundary entities (`clip_boundary_handle` is read-only and preserved), non-rectangular viewports, and AutoCAD acceptance of the written viewports, which cannot be tested here. |
|
||||
| ViewBorder | **Integration-tested / blocked at W by acadrust (DXF kind)** | Writable: `min`, `max`, `center` (2-D pairs), `scale`, `rotation_angle`, `active_viewport` and `scale_handle` (both required at creation). `version` is read-only. Reference rules: `active_viewport` must be an existing Viewport entity and `scale_handle` an existing `Scale` object. Host validation requires finite bounds with `min` below `max` on both axes, a positive scale and a finite rotation. `staged_python_view_border_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, driver-made viewport and scale fixtures) passes C/R/E/D/U/I/V/P and DWG W: creation, a resize/recenter/scale/rotation transaction, canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W blocker (engine, pinned by a canary):** a DXF `DRAWINGVIEW` reopens as a different, opaque entity kind rather than as a ViewBorder, so only DWG restores the typed record. Not `Complete` until DXF loading restores the typed entity. Not covered: the linked view representation and drawing-view definition records. |
|
||||
| SectionSymbol | **Integration-tested / blocked at W by cadcodec (DXF SECTIONLINE)** | The earlier blocker was overstated, and reading cadcodec and OCS showed why. cadcodec documents `points` as the canonical geometry, says the point-count fields equal the point count in every verified ODA export, and ships `sync_display_fields()` to refresh the `end_a`/`end_b`/`tick_a`/`tick_b`/`label` projections; OCS's own grip and transform code already calls it. OCS also renders a symbol with no style and no view link. So the kind is mapped: writable `points` (each `{point, bulge, label, label_offset, raw_flag_280}`), `symbol_scale`, `style_handle` and `view_rep_handle`; read-only `view_symbol_version`, `raw_view_symbol_70`, `version`, `raw_flags_90`, the two derived counts and the five projections. The host sets `raw_point_count_90` and `raw_point_record_count` to the point count and calls `sync_display_fields()` whenever `points` is created or changed, and leaves a symbol whose points did not change exactly as read. Host validation requires at least two finite points with finite bulges and label offsets, a positive scale, and that each non-null reference is an existing `SectionViewStyle` (style) or `ViewRep` (view representation) class object; the wrong object kind is refused. Creating one without references gives a standalone symbol. `audit_python_section_symbol_lifecycle_over_real_ipc` (shared driver) passes C/R/E/D/U/I/V/P and DWG W: a two-point symbol, a three-point edit with a new label, bulge and scale in one transaction (counts and projections follow), canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, three-step undo/redo. `audit_python_section_symbol_references_over_real_ipc` covers valid references, a standalone symbol and four wrong-object refusals with host-made class objects. **W blocker (cadcodec, pinned by a canary; issue 5 in [cadcodec-reader-gaps.md](cadcodec-reader-gaps.md)):** the DXF reader dispatches `SECTIONLINE` only inside blocks, so a symbol in the drawing's entity list reopens as an `Unknown` entity. Open items that cannot be checked here: `raw_flags_90` has unknown semantics (0 for a new symbol, preserved on read), and whether AutoCAD accepts a symbol with no parent view. Scripts cannot create the style or view-representation objects. |
|
||||
| Light | **Integration-tested / blocked at W by acadrust (DXF `cast_shadows`)** | Writable: `name`, `light_type` (1 distant, 2 point, 3 spot), `position`, `target`, `status`, `light_color`, `plot_glyph`, `intensity`, the attenuation type and limits, `hotspot_angle`, `falloff_angle`, `cast_shadows`, `shadow_type`, `shadow_map_size`, `shadow_map_softness`, `photometric_mode`, `photometric_data`; `class_version` is read-only. Host validation requires a non-empty name, a known light type, finite placement, a spot or distant target that differs from the position, non-negative intensity, angles and limits, an attenuation start not above its end, a spot hotspot not above its falloff, and a non-negative shadow map size. `staged_python_light_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/V/P and DWG W: a spot light with an RGB colour, an intensity/target/position/shadow transaction that preserves the colour and untouched fields, the canvas glyph, eight rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W blocker (engine, pinned by a canary):** the DXF reader does not restore `cast_shadows`, which reopens false; colour, placement and intensity persist. Not covered: photometric web data edits and sun/skylight objects. |
|
||||
|
||||
Reference in new issue
Block a user