The PR pinned codec and kernel to its fork through [patch]; codec #77 and
kernel #73 are merged upstream, so the patch sections are replaced by
the upstream pins (codec 8d23408, kernel d22a270, graph 9b5ae39) in this
same commit: the kernel now applies the body transform during the lift,
and an OCS without this PR's change would apply it twice. The new codec
also brings AssocCurveValue, registered with the plugin API's type
registry, and EmbeddedEntity::Body, named as a polyline profile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codec PRs #75 (field authoring API) and #76 (solid history evaluation
graphs, ACSH layouts); kernel PR #71 (sweep history with a placed profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codec: PRs #71-#74 merged (MPOLYGON layout, multiline ATTDEF/ATTRIB DXF,
DXF MATERIAL/ACSH matrix, restorable SAT/SAB + history roots) and issues
#55 #64-#69 fixed. EntityType now boxes Surface, Extended, Table,
MultiLeader and Helix (2344 B -> 848 B per entity): construction sites
wrap in Box, the entity traits forward through Box, and the python
plugin generator converts with into(). Kernel: PR #70 (cuboid ring
winding).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Point clouds: drawn from their scans, attached, cropped and stylized.
The fork's codec patch is resolved to the upstream pins: the point cloud
records landed in opencadcodec 5ef9376, carried through opencadkernel
68eeda1 and opencadgraph e204966.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bug:
The typed ensure_plugin_state helper and the object-safe trait method
it wraps turned two recoverable conditions into process deaths. On the
in-process host, the helper downcast the stored state with
.expect("plugin state type mismatch for plugin_id"), so registering
state under one plugin_id as one type and later asking for another
(e.g. a reloaded plugin version changes the type while the per-tab
state map survives) panicked the host. On out-of-process hosts
(ipc/client.rs, ipc/v4/client.rs) and on NullHost, the trait method
itself was implemented as a bare panic!("ensure_plugin_state is not
supported for out-of-process plugins..."), so any out-of-process
plugin calling the documented helper crashed its own runner process -
even though the sibling plugin_state_any and plugin_state_any_mut
methods on those same implementations already degrade to None
gracefully. The helper also used .expect("init called once") on the
initialiser closure.
Reproduce:
1. Out-of-process: a plugin whose runner calls ensure_plugin_state
(&mut host, "my.id", ...).
2. Type mismatch: in an in-process plugin, ensure state under one id
as u32, then ensure a different type under the same id.
3. Before: step 1 kills the plugin runner at the trait panic; step 2
kills the host at the downcast expect. After: both get None, keep
running, and existing state is untouched.
Fix:
Give the trait method a failure channel: ensure_plugin_state_any now
returns Option<&mut (dyn Any + Send + Sync)>, mirroring the sibling
plugin_state_any_mut on the same implementations, and the typed helper
returns Option<&mut T> (any?.downcast_mut). The panicking
implementations (ipc v3, ipc v4, NullHost, and the three never-called
test dummies) return None; the in-process app host wraps its real
storage in Some with unchanged insert-or-return semantics. The init
closure is now guarded by take() with a unit-Any fallback, so even an
implementation invoking it twice fails the typed downcast instead of
panicking. Object safety, argument types and vtable slot are
unchanged; external plugin sources get a compile error at the helper
and adopt Option handling on rebuild. Regression tests use catch_unwind
so they compile against both signatures: one per panic site (out-of-
process, NullHost, type mismatch), each additionally asserting the
None result and that pre-existing state survived.
The #1538 drawing's solids now draw every face: extrusion, sum and
offset surfaces and summary-only intcurves are read, vertex blends are
filled over their boundary, and the mesher closes boundaries fitted to a
few thousandths, notched cylinders and torus bands.
Fixes#1538
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Commit 98b56ef6 ('Complete Hatch host model coverage') added the hatch_geometry_and_boundaries_are_validated test without the #[cfg(feature = host)] guard, and commit f537aff7 imported codec directly instead of through crate::host::codec. This broke cargo test --workspace with 20 compilation errors because codec and the canvas validation helpers are only compiled when the host feature is active.
Fixes regression from commit 98b56ef673.
A V2 plugin passed the version gate and then failed silently while
registering its ribbon, since the ribbon wire types have moved on. The
host now loads V3 and newer, so V2 plugins are rejected up front with a
version message. The V2 compatibility fixture crate goes too.
Fixes#1085Fixes#987
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the DWFFRAME/DGNFRAME header variables (codec #63) and robust 2D tangency plus ellipse-circle intersection (kernel #68). Pins opencadcodec ad16215, opencadkernel 1e02630, opencadgraph 7ef5ae4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in bounded object storage on DWG import (#62) and R13/R14 LAYER
records reading as plottable. Pins opencadcodec a35f43e, opencadkernel
ee41029, opencadgraph 8b8c7f3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in: unchanged ACSH extrusion history and DIMASSOC records kept
byte for byte; native dynamic-action field layouts corrected; evaluation
graph expressions written as owned; evaluated block metadata copy;
source class numbers kept on same-version legacy saves; explicit block
attribute flags kept.
The dynamic-action model now names its fields after their native
meaning, so the object summaries read parameter ids, the move offsets'
distance multiplier and the lookup table's columns. The empty
PropertiesTable variant is gone; such tables stay opaque objects.
Pins opencadcodec 4f7bd51, opencadkernel 8186605, opencadgraph 048c9c5.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every glyph advanced by the face's letter spacing, including CJK glyphs a
style's big font or the TrueType fallback supplies. Their advances already
carry the gap (big font) or side bearings (TrueType), so with an LFF primary
such as iso each ideograph took 1.33 heights instead of one.
Face::spacing_after(ch) gives the tracking only to glyphs the stroke font
draws itself; every layout path uses it.
The 1.8-height widths in the report were the TEXT bounds estimate counting
UTF-8 bytes; opencadcodec 1109d52 counts characters.
Pins opencadcodec 1109d52, opencadkernel 8a246b0, opencadgraph 3c4af1c.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inventor DWG exports place every sheet block with AcIdBlockReference,
which the codec skipped, so these drawings opened blank. opencadcodec
7c20aa7 decodes it as an INSERT and keeps the viewport each model-space
view reference is bound to.
Those view blocks overlap in model space, so a content viewport now also
hides model-space references bound to a different viewport. The handles
join the viewport's frozen-layer set (entity and layer handles never
collide), which the wire and render-graph visibility checks and every
per-viewport cache key already use.
Pins opencadcodec 7c20aa7, opencadkernel d71a828, opencadgraph 1543a5a.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The graph panel gets New, Open and Save. A .ocg file is JSON holding the
graph (nodes, parameters, links), the canvas (positions, open sections,
pan, zoom) and each object node's entity handles, plus the fingerprint
GUID of the drawing those entities live in. On open, handles are reused
only in that same drawing and only while the entity is still the node's
kind; otherwise evaluation creates new entities, so a graph opened in
another drawing never takes over unrelated objects. A drawing without a
fingerprint gets one when its graph is first saved.
Pins opencadcodec d6d534f (DXF now writes $FINGERPRINTGUID and
$VERSIONGUID), opencadkernel 5d9e921 and opencadgraph 5c9b004 (graph
JSON round trip).
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>
- An inverted clip's roundtrip record goes in the filter's extension
dictionary through the codec's own API (ensure_extension_dictionary /
xrecord / extension_dictionary_handle) instead of a scan of every
object, which the renderer ran for each clipped reference.
- Pin cadcodec 1c3be0c (the DXF writer now links a spatial filter's
extension dictionary, so that record is not left unreferenced) and
cadkernel deb529b; plugin crates and the Python plugin lock follow.
- The XCLIP boundary preview stays f64.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pin cadcodec upstream at d290dbe (the merged underlay change) and
cadkernel at 4134ae3, which follows it; drop the fork patch, move the
plugin-API and web-worker crates and the Python plugin lock to the same
codec so one copy is built.
- "Import as block" with raster images left IMAGE entities without an
image definition: block definition now creates it like add_entity does,
through one shared helper.
- The three preview outlines (attach frame, clip boundary, import area)
stay f64 instead of casting world points to f32.
- Page size / total / PDF scale entries use the catalog's positional
placeholder; the bare {name} form failed to format in 20 locales.
- The Underlay Layers bulb is the SVG layer icon, not a text glyph.
- PDF dialogs use the shared dialog buttons and radios.
- Options opened from a PDF dialog clears its saved state on return.
- Paper-space underlay background reuses the model block handle already
resolved; dead `let _ = i` lines and a misplaced doc comment removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1469 inserted `CommitMany` and `CommitManyAndEnd` between `Commit`,
`CommitAndEnd` and `Done`. `CommandStep` crosses the plugin IPC, which
encodes a variant by its position, so a plugin built against the previous
API that answered `Done` would have been read as `CommitAndEnd` — or
rejected as undecodable — by the new host. The two variants now sit after
`Cancel`, as the other additions to the v7 wire types do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the DXF round-trip fixes merged as cadcodec #48 and #51 and the
block-definition fixes for cadcodec issues #49 and #52. cadkernel moved to
the same codec revision first, so the graph keeps a single acadrust. The
Python plugin, which is outside the workspace, has its own lock moved too:
its staging script requires the plugin and the host to share one acadrust
source.
The canaries that asserted the old wrong results were flipped against the
real-IPC audits, all 60 of which pass with the plugin built: Helix, Light,
Underlay, ViewBorder and SectionSymbol now round-trip through DXF, as do the
text style flags, the dimension style's text style name, the block
description, the table's merged ranges and the leader's annotation link and
vectors. Those five kinds move to Complete in the coverage ledger.
Two narrower gaps stay pinned. The ATTDEF writer emits no group 280, so
`lock_position` is still lost on a DXF save; every other field now
survives. A true-colour leader `override_color` cannot be held by DXF group
77, which takes an ACI index, and the DWG writer does not store it.
Behaviour change for users: a text style's oblique angle is now written to
DXF in degrees, as the format defines it. DXF files written by earlier
versions carry radians there and reopen with a much smaller slant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bundled `opencad.python` plugin drives OCS through plugin API v7: all
43 canvas entity kinds, the drawing tables, and OCS's own commands run
step by step. RustPython stays in the plugin process — the plugin crate is
outside the workspace with its own lock file, so the application's own
dependencies are unchanged.
Reconciled with the plugin command work that landed as #1425, which the
branch was written before:
- Both added `impl Drop for PluginProcessInteractiveAdapter`, which git
merges silently into two impls that do not compile. One body now
cancels the command and then drops it from the runner, in that order: a
plugin waiting on a step has to be released before the runner forgets
the command.
- `execute_command` (#1425) and `run_command` (#1423) are both routes from
a script to the command line, and only the second was guarded. The
SCRIPTCOMMANDS switch and the denylist are shared now, so neither route
is the unguarded one.
- `run_command` dispatched with plugin dispatch still enabled, so a script
naming a plugin command re-entered the plugin that was blocked waiting
for it. It sets the suppression flag for the length of the dispatch, as
`execute_command` does.
The denylist read the command as typed, while the dispatcher resolves
aliases: a user-defined `Q` for `QUIT` (ALIASEDIT writes those) walked
past it. It resolves the alias first.
Also: the active-command comparison ran on every message, a mouse move
included, allocating two strings for names that are already `&'static
str`; SCRIPTCOMMANDS' own messages go through the translation macros; and
`docs/.DS_Store` (now ignored) and an unrelated RTF note about constraint
commands are out of the tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Remove personal names, local paths and fork-only process notes from code comments,
docs and the plugin manifest (repository now points at the upstream project), and
drop the handoff section from the plan.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
HostApi::run_command drives the real OCS command through the same primitives
the automation channel uses, synchronously on the host thread: run a line,
start a command and answer point/text/token/entity/selection/enter prompts, or
cancel (closing anything the command opened). Refused while another command is
active, for a denylist (QUIT, NEW, OPEN, SAVE*, PY_* ...) and off the active tab.
Python: doc.command, doc.start_command and doc.modify (offset, trim, extend,
fillet, move, copy, rotate, scale, mirror, erase). Real-IPC audit checks exact
geometry for every wrapper plus guards and undo; IPC and Python unit tests; docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TableOperation gains simple-linetype create/modify/rename/delete (standard names
taken, rename follows layers and entities, guarded delete) and paper-space
layout create/rename/delete/set-current/set-page (default page setup and sheet
viewport, the application's own layout switch, Model protected). CTAB now reports
the current layout. Real-IPC audit with DWG/DXF persistence and undo, IPC and
Python unit tests, docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BlockEntityAdd adds a validated entity to a block definition (owner set by the
host, same validation as a model-space entity, self-nesting inserts refused),
reusing the scene's block-editor routing. Deleting a block member now also
drops its handle from the block record. Real-IPC audit with an attribute
definition inside a block, DWG/DXF persistence and undo; IPC and Python unit
tests; docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TableOperation gains BlockCreate (copy entities into a definition shifted by the
base point, optionally erasing originals), BlockModify, BlockRename (inserts
follow) and BlockDelete (refused while referenced; removes members, markers and
orphaned draw-order tables). Real-IPC audit with DWG/DXF persistence and undo,
IPC and Python unit tests, docs; the DXF loss of the block description is pinned
by a canary.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extends TableOperation with text style create/modify, dimension style
create/modify (DimStyle fields as a validated JSON object), rename, delete and
set-current. Reuses OCS's style in-use scan and reference-following rename
(four style_ops methods widened to pub(super)); a renamed text style also
updates dimension styles that name it. Real-IPC audit with DWG/DXF persistence
and undo, IPC and Python unit tests, docs. Four cadcodec findings are pinned by
canaries.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the additive v7 TableOperation request and HostApi::table_operation for
layer create/modify/rename/delete/set-current. Refusals are decided before the
undo step is recorded. Python wrapper, real-IPC audit with DWG/DXF persistence,
IPC round-trip test, Python unit test and docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
EmbedPicture builds the frame with OCS's own EmbeddedImage and
build_embedded_ole, so the opaque storage is never scripted. PNG, JPEG and
BMP are stored as read and other formats are re-encoded as PNG. The lifecycle
test now creates through Python; a new test covers formats, aspect, origin,
layer and refusals.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
points is canonical; the host derives the point counts and refreshes the
end/tick/label projections with sync_display_fields when it changes, and
validates that style and view-representation references are existing class
objects. The lifecycle test passes every gate in DWG; DXF reopens the symbol
as an unknown entity because cadcodec dispatches SECTIONLINE only inside
blocks (pinned by a canary and already in the cadcodec write-up).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SolidOperation::Boolean runs synchronously, surfaces the kernel's refusal
with a plain reason and leaves the operands untouched, consumes them on
success unless keep_operands, and records one undo step. The lifecycle test
uses planar boxes with exact volumes and covers chaining, refusals, both
formats and undo; an ignored test shows a curved refusal reaching the script.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds SurfaceFromProfile and Extrude kernel operations (doc.solids.surface and
doc.solids.extrude), accepts surfaces in Transform, and maps Surface with
editable isolines. Fixes a real bug found by the test: the surface kind field
overwrote the entity-type kind key in Python dicts, so it is exposed as
surface_kind, with a catalog test guarding against name collisions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RegionFromProfile reuses the REGION command's kernel calls; doc.solids.region
exposes it with layer and delete_source options. The lifecycle test builds
regions from real circle and polyline entities and covers refusals, a move,
both formats and undo. create_entity now accepts coordinate tuples for point
properties and every generated float setter accepts Python ints.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
An additive HostApi::solid_operation builds cadkernel primitives and applies
rigid transforms, verifying each result lifts back losslessly and refusing a
payload it cannot lift. Exposed as doc.solids in Python; Solid3D, Body and
Region are mapped read-only with the opaque payload preserved. The lifecycle
test passes through the real runner with analytic volumes and exact extents as
oracles, DWG and DXF reopen with re-edit, and undo restores exactly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>