Drop two unneeded mut bindings in tests and mark the generated dict/enum
converters allow(dead_code), since a kind can leave one unused.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
polyline_close/open/width/reverse/join (a line or arc is turned into a polyline
first), array_path and array_3d, written against observed prompts and checked
for exact geometry in the real-IPC audit. Polylines default their pick point to
the first vertex. The audit runs on a large-stack thread because PEDIT's
line-to-polyline step needs more than a test thread's 2 MiB in a debug build.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The generated dict-to-struct conversions for nested records defaulted every
absent field and ignored unknown keys, so an LwPolyline built from
{'x': .., 'y': ..} vertices became zero-coordinate vertices. They now refuse an
unknown key (naming the keys the record takes) and a manifest list,
required_struct_fields, enforces the fields that define a nested record's
geometry (vertex locations, hatch edge ends and centres, section symbol points).
Regression test over real IPC; every existing lifecycle audit still passes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Each is written against the prompt sequence observed in the exploration harness
and checked for exact geometry in the real-IPC audit. HATCH is left to
create_entity because its command needs an interactive boundary pick.
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>
Only the first line's ends are pinned while the second line is made
parallel, the way the Parallel constraint itself moves it; the distance
is then measured on the solved geometry.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Point & line and 2Lines take a text baseline or an ellipse axis as the
line; a zero value folds the points onto the first one; 2Lines turns the
second line about its start. The Properties Constraint section edits
Constraint Form (Dynamic on the *ADSK_CONSTRAINTS layer, Annotational on
the current layer), Reference (driven constraint in parentheses whose
parameter follows the measurement) and Description (stored with the
parameter and the native variable). Dynamic dimensions keep a 12 px text
and 8 px arrows at any zoom, drop trailing zeros and show formulas as
`fx: len=d1*2`; the hidden constraints layer stays out of the layer lists.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DCFORM sets the annotational or dynamic form and continues into the
DIMCONSTRAINT option prompt, whose wording and last-used default match the
reference. -PARAMETERS manages parameters from the command line; a
double-clicked constraint dimension opens its value prompt. Dynamic
dimensions rescale on every camera change and their display overrides stay
out of the saved file. A driving dimension between an entity's own ends no
longer has its length retained, so a new value moves the second point along
the dimension axis; parameter edits hold the first constraint point.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DCALIGNED opens with `[Object/Point & line/2Lines] <Object>`. Point & line
asks `Specify constraint point or [Line] <Line>:` then `Select line:` (or the
line first and `Select constraint point:`), measures perpendicular to the
line from the constraint point to the line end nearest to it, and stores a
directed distance with the line as direction reference. 2Lines asks
`Select first line:` and `Select second line to make parallel:`, adds a
Parallel constraint solved with the first line pinned, then measures on the
solved geometry between the nearest ends. Unsupported line picks report the
reference's message; the same measurement again reports `The constraint
already exists on the selected objects.` and ends the command; an invalid
expression reports both reference lines and asks again.
The offered default and seeded expression carry twelve significant digits;
a typed value moves the second point (or the other object) while the first
point and its measured line stay, negative values keep their sign in the
parameter and use their magnitude, zero folds the points together, and the
dynamic dimension's origins follow every solve. The lock mark sits after the
text, a selected dynamic dimension shows triangle grips at its points and the
text square, and its Properties show the Constraint section (Name and
Expression editable) plus Text rotation.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A plain number shows as typed (d1=100), an expression under the fx: prefix
(fx: d2=d1*2); the text stays horizontal through DIMTIH/DIMTOH overrides
and the lock mark follows the text.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every dynamic dimension gets a DIMSCALE override for 12 px text, refreshed
per zoom band, at creation and after load. A SCALE/ROTATE of an entity whose
own end points a driving dimension measures now pins its second constraint
point so the dimension moves the first end, as the reference does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The dynamic dimension of a dimensional constraint is now linked in the
drawing file (AcDbAssocDependency + AcDbAssocDimDependencyBody on the
constraint node), reread on load, hidden by DCHIDE/DYNCONSTRAINTDISPLAY
and never plotted; it carries a lock mark before its text. New
CONSTRAINTNAMEFORMAT and DYNCONSTRAINTDISPLAY variables and the
DIMCONSTRAINT option front end. A transformed entity whose own end points
a driving dimension measures keeps only the first constraint point, so a
scaled line returns to its dimension value.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DCLINEAR, DCHORIZONTAL, DCVERTICAL and DCALIGNED follow the reference flow:
`Specify first constraint point or [Object] <Object>:`, `Specify second
constraint point:`, `Specify dimension line location:`, then `Dimension
text = …` and `Enter value or name and value <d1=100>:`. Linear reads
horizontal or vertical from the dimension line location the way a linear
dimension does; Object takes a line's, an arc's or a polyline segment's
ends. A miss reports `No valid constraint point found.` / `No object
found.` and asks again; another object type reports `Invalid selection for
Linear. Select a line, polyline segment or arc.`.
Each constraint creates a drawing parameter (`d1`, `d2`, …, or the typed
`name=expression`), drives the constraint through it, and draws a dynamic
dimension in constraint gray whose text follows CONSTRAINTNAMEFORMAT
(`d1=100.0000`); the dimension is associated with the constrained objects
and its text is refreshed after every solve. A typed value other than the
measured one moves the second point while the first stays, and so does a
later parameter edit. Deleting the constraint removes its dimension and,
when nothing else reads it, its parameter.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A fresh reference session shows the "=" bar of every object made equal
through Multiple right away; the earlier hidden state was not the rule.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A second object of the wrong kind (a circle after a line, a line after a
circle) or a pair that already carries Equal now reports and asks for the
second object again instead of ending the command. The mismatch message
names what the first object asks for — `Select a line segment, polyline
segment, text, MText, major or minor axis of ellipse or elliptical arc.`
after a length object, `Select a circle or arc.` after a radius object —
and the same-pick message carries the reference's double space.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- An object that carries several Equal relations shows a single "="
badge, as the reference does, instead of one stacked badge per relation.
- A pick made in the Multiple flow leaves its new constraint bar unshown
until the object is selected or bars are shown, matching the reference;
the single flow still shows the bar at once.
- A typed coordinate at the Fix `Select object:` prompt and at the
Horizontal/Vertical `Select an object or [2Points]` prompt picks the
object under that point, like a click, instead of being read as a point.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An OTRACK crossing is the meeting of two tracking vectors, but
`OtrackHit` carried only the one a typed distance is measured along, so
the overlay had a single guide to draw. Snapping to the crossing of two
vectors worked and looked like an unexplained point on one line.
Carry the crossed ray out of `otrack_snap` as `OtrackHit::cross`, keep it
on the app beside `otrack_active`, and let the overlay take a list of
guides rather than one. `otrack_active` still drives typed-distance
entry on its own, so distance entry is unchanged.
FixesHakanSeven12/OpenCADStudio#1313
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQzYZsXBwPZ5HwBaGitEUV
(cherry picked from commit 2f6caacece)
- The acadrust DXF reader has converted ATTRIB/ATTDEF rotation to radians
since f226110 (in every codec rev OCS pinned from 2026-08-08 on), while
fix_dxf_dimension_rotations still applied to_radians a second time on
load. Only Shape needs the conversion here. Surfaced by #1391's hunk.
- Shape-file text styles are found by their is_shape_file flag instead of
an empty name (also from #1391).
- #1393's add_layer/modify_layer wrote an unresolved linetype name into
the LAYER record; a name the drawing (plus the standard set) cannot
supply is now refused, through one resolve_linetype helper.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A scripted RasterImage now reuses one ImageDefinition per file and registers
it in ACAD_IMAGE_DICT, which owns it, as for an AutoCAD-attached image. The
reactor is deliberately not written because its owner convention cannot be
verified. An ignored spike records that OCS's own IMAGE command creates bare
images that lose their file path on save.
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>
OCS already creates, transforms and combines bodies through cadkernel, and
solid_to_sat / kernel_acis_body verify each result lifts back losslessly.
Two ignored spike tests measure volumes and DWG/DXF round trips. The
blocker is an operation channel, not geometry; the note recommends an
additive solid_operation host method and phases the work.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Frame corners and the aspect lock are scriptable; the opaque storage and
envelope are excluded and refused on edit. The audit embeds a real PNG
through OCS's own builder and proves it survives DWG and DXF byte-for-byte.
Python creation stays a follow-up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Documents six cadcodec reader/writer gaps with source references, canary
tests and suggested fixes (nothing filed). Verifying them showed the Spline
DXF weight loss was an OCS bug: the writer emits weights only for a rational
spline, so the host now sets flags.rational from the weights. Spline passes
every gate.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Insert can be created by naming an existing ordinary block; the host refuses
unknown blocks, model/paper space and containment cycles, and keeps legacy
inserts of missing blocks editable. The legacy Polyline is update-only with a
hint toward Polyline2D and Polyline3D because both writers convert it on
save. The generator gains update_only_message.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Compares every writable ATTDEF field through DWG and DXF. DWG round-trips the
complete state; acadrust's DXF reader handles only eight group codes, so
alignment, width factor, oblique angle, attribute flags, field length,
generation flags, lock and text style reopen as defaults. Pinned by canary
expectations and recorded in the ledger.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Runs every first-generation kind through the real-runner lifecycle driver.
The audit found Ellipse unvalidated, zero-height Text/MText and non-finite
Arc angles and Point/Line thickness accepted, and no creation-time checks
for most kinds. validate_basic_entity now covers them and refuses only
problems an edit introduces. Twelve kinds pass every gate; Polyline,
Polyline2D, Polyline3D and Spline carry pinned persistence findings, and
Insert is update-only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Light placement, intensity, colour, attenuation and shadow fields are
writable with host validation. staged_python_light_lifecycle_over_real_ipc
passes every gate except DXF cast_shadows, pinned by a canary.
SectionSymbol is recorded as blocked: it needs host-created style and view
representation objects and its raw point counts have no documented
derivation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bounds, scale and rotation are writable with viewport and scale reference
rules. staged_python_view_border_lifecycle_over_real_ipc passes every gate
in DWG; a DXF DRAWINGVIEW reopens as an opaque entity, pinned by a canary.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Curated view fields are writable; the owner must be a paper-space block and
frozen layers must exist. staged_python_viewport_lifecycle_over_real_ipc
passes every gate except a paper-space canvas oracle, so the kind is
recorded as integration-tested.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Scripts place an underlay against an existing UnderlayDefinition; the host
checks that the definition exists and matches the underlay type.
staged_python_underlay_lifecycle_over_real_ipc passes every gate except DXF
persistence: acadrust's reader returns the degree-valued rotation
unconverted and repeated saves compound it. Pinned by canary expectations.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Maps the placement, display and clip-boundary fields with rectangular and
polygonal clip validation. staged_python_wipeout_lifecycle_over_real_ipc
passes the full gate including DWG and DXF reopen with re-edit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>