Commit Graph
19 Commits
Author SHA1 Message Date
Valentin KleinandClaude Opus 5.5 9d2ae09a79 OFFSET Erase/Layer/Multiple/Undo and MOVE/COPY Displacement take effect (fixes #244) (#246)
OFFSET's Erase and Layer keywords open their Yes/No and Current/Source
prompts (remembered in OFFSETERASE/OFFSETLAYER and shown in the current
settings line); Multiple keeps offsetting the newest copy until Enter and
Undo takes back the last offset. The JSON form takes `erase` and `layer`.
MOVE and COPY ask for a displacement after the Displacement keyword or
Enter at the base point prompt.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:12:14 -04:00
Valentin KleinandClaude Opus 5.5 c04a769ef2 -INSERT asks for X and Y scale; Basepoint/Scale/Rotate take effect (fixes #245) (#249)
After the insertion point, the command-line INSERT now asks "Enter X
scale factor, specify opposite corner, or [Corner] <1>", then "Enter Y
scale factor <use X scale factor>", then the rotation, so scripts like
`-INSERT B1 20,20 1 1 0` place the block as written instead of rotating
it by the Y scale and running the rotation as a command. A point (or
Corner) at the X scale prompt gives both factors from the opposite
corner.

The Basepoint, Scale and Rotate options at the insertion point prompt
now work: Scale and Rotate preset the value and skip that prompt later,
Basepoint picks a new base point on the block at its definition
position.

The JSON form takes `scale: [x, y]` and `basePoint` too.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:12:09 -04:00
Valentin KleinandClaude Opus 5.5 9524f9a5c2 -RENAME prompts; RENAME renames blocks (fixes #264) (#266)
Typed RENAME / -RENAME (and REN) asks for the object type
[Block/Dimstyle/LAyer/LType/Material/multileadeRstyle/Plotstyle/
textStyle/Tablestyle/Ucs/VIew/VPort], the old name and the new name,
then runs the rename command body, so the checks and the single undo
step match the JSON form. An unknown old name or a refused new name
re-prompts. Material, Plotstyle and VPort read both names and report
"not available yet".

The JSON form now renames blocks, as its params doc promised. Every
INSERT follows the new name, in model space, layouts and nested block
definitions, including inserts that carry attributes. INSNAME and
dimension-style arrowheads follow too. Duplicate names (any case),
invalid names and anonymous blocks are refused. Multileader styles,
table styles, named views and UCSs can be renamed as well. A layer
rename no longer records two undo steps.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:12:03 -04:00
Valentin KleinandClaude Opus 5.5 824ae58d91 Object snaps find the geometry inside block references (fixes #314) (#318)
Running snaps only saw a block reference's insertion point. Now the objects
inside it snap where they are drawn: nested references and MINSERT arrays are
expanded, placed exactly by the reference's transform (non-uniform scale
turns circles and arcs into ellipses), and hidden objects or objects on layers
that are off are left out. Insertion also snaps to visible attributes.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:11:58 -04:00
Valentin KleinandClaude Opus 5.5 3e5e5ac3ce -BLOCK erases the selection and asks before redefining (fixes #307) (#310)
-BLOCK was an alias of BLOCK, so the selected objects were always
converted to a block reference; a script doing -BLOCK then INSERT ended
up with an extra reference. -BLOCK is now its own command: the command
line erases the selection, and its JSON form defaults to keep "delete".
BLOCK (menu Make..., typed BLOCK) and the "block" JSON form still
convert by default.

Both forms ask 'Block "<name>" already exists. Redefine it? [Yes/No]
<N>:' when the name is in use; No asks for the name again.

Session::start resolves a dashed name to a command with that id (the
same change as #280's -ATTEDIT).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:11:19 -04:00
Valentin KleinandClaude Opus 5.5 f55559e5bb OFFSET keeps a closed spline closed (fixes #303) (#305)
The offset copy of a spline was always rebuilt with the open fit-point
interpolation, so a closed spline came out open with a corner at its start
point. Use Spline::from_fit with the source's closed flag, which gives a
periodic spline for closed input.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:11:13 -04:00
Valentin KleinandClaude Opus 5.5 dd8764a7b5 WBLOCK honours base when writing a block (fixes #297) (#300)
wblock {name, base} ignored base and always measured from the block's
own base point. base (in block coordinates) now overrides it, the way
it already worked for selected objects. In both modes the base point
becomes the written drawing's origin, and $INSBASE is 0,0,0.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:11:07 -04:00
Valentin KleinandClaude Opus 5.5 b90d8cf644 Metric drawings load library linetypes in millimetres (fixes #294) (#299)
The linetype library is defined in inches, and LINETYPE loaded it
unchanged into metric drawings: DASHED was 0.5 mm on, 0.25 mm off at
LTSCALE 1, so dashed lines looked solid. CAD programs load ISO-scaled
definitions when MEASUREMENT is 1 (the same patterns with lengths times
25.4). library::standard_linetypes_for(d) does the equivalent with our
own definitions: in a metric drawing every length (dashes, gaps, text and
shape size, offsets) is scaled by 25.4 as it is loaded. LINETYPE and the
CENTER linetype that CENTERMARK/CENTERLINE load use it. Definitions
already in a drawing are not changed.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:11:02 -04:00
Valentin KleinandClaude Opus 5.5 fc1810f5d9 Array commands: ARRAY, and edit-array option loops for ARRAYRECT/ARRAYPOLAR (#230) (#232)
* Array commands: ARRAY, and the edit-array option loops for ARRAYRECT/ARRAYPOLAR (#230)

ARRAY (alias AR) selects objects, asks for the array type and continues as
ARRAYRECT, ARRAYPATH or ARRAYPOLAR. ARRAYRECT no longer creates a fixed 4x3
grid right after selection: it previews the array and loops on
ASsociative/Base point/COUnt/Spacing/COLumns/Rows/Levels/eXit. ARRAYPOLAR
asks for the center (or Base point), then loops on Items/Angle between/
Fill angle/ROTate items/eXit. The copies are created on eXit or Enter.
Associative arrays, levels and polar rows report "not available yet" and
return to the loop without consuming the next input.

The array commands move to cmd/array.rs. Arrays stay non-associative
copies, and the JSON forms are unchanged. A full clockwise polar fill
(-360) no longer puts the last item on top of the source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs/gaps.md: narrow F5 (array option loops, #230)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Arrays: refuse arrays over MAXARRAY instead of hanging or shrinking them (#230)

`arrayrect {"rows":1000000,"cols":1000000}` was clamped to 1000x1000 and
then created a million copies of every selected object, which hangs the
app. ARRAYRECT, ARRAYPOLAR and ARRAYPATH (JSON and interactive, also via
ARRAY) now count items x selected objects and refuse an array over the
new MAXARRAY session variable (default 100000, settable 100..10000000)
with a message naming the limit, instead of clamping the counts and
silently creating fewer copies. The interactive count prompts accept at
most what still fits under MAXARRAY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:34:30 -04:00
Valentin KleinandClaude Opus 5.5 c55feb79da SPLINE Close is periodic, options report; POLYGON typed radius has a flat bottom; CIRCLE rejects bad input (fixes #238) (#240)
SPLINE Close now builds a smooth closed (periodic) curve through the fit points
instead of repeating the first point. The geometry gets Spline::from_fit_points_closed:
chord-length parameters including the closing chord, a periodic knot vector, and the
first three control points repeated at the end. The fit data no longer duplicates the
first point. DXF writes group 70 bit 2 (periodic) for it, and a file whose closed spline
needs refitting is refitted closed. SPLINEDIT Close/refit/move and fit-point grips keep
a closed spline periodic.

SPLINE Method asks Fit/CV, and CV continues as SPLINE by control vertices. Knots, Object,
start/end Tangency and toLerance report "not available yet" and stay at the prompt.
Garbage text reports "Point or option keyword required.".

POLYGON with a typed radius puts the bottom edge horizontal for Inscribed and for
Circumscribed. The JSON form uses the same orientation when no angle is given.

CIRCLE reports "Point or option keyword required." for an invalid token at the centre
prompt, and "Invalid point." at the 3P/2P point prompts, then asks again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:34:24 -04:00
Valentin KleinandClaude Opus 5.5 a15d0e5b8e DIMCONTINUE/DIMBASELINE: one snap index per chain, cap the points (#279) (#281)
With DIMASSOC on, every chained dimension looked up the objects under
its definition points by scanning the whole space, and each point adds
an entity, so a long `points` list took quadratic time (16,000 points:
2.7 s continue, 10 s baseline). The chain now builds one x-sorted
SnapIndex up front. Dimensions are no snap targets, so the index stays
valid while the chain adds them, and each lookup is a binary search. The
index picks the same snap as find_snap: lowest rank first, then the first
object in space order. A call with more than 100,000 points is refused.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:34:07 -04:00
Valentin KleinandClaude Opus 5.5 1d1fe4fc6d BLOCK refuses a definition that references itself; names are case-insensitive (fixes #250) (#252)
Redefining a block with a selection that holds a reference to that block,
directly or through nested blocks, is now refused with "Block "B"
references itself." and changes nothing. Before, the block ended up
containing itself and lost its original geometry. At the command line
the message ends -BLOCK, as for any definition that can't be made.

Redefining with a name that differs only in case (Door, then DOOR)
replaces the existing block instead of adding a second one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:34:01 -04:00
Valentin KleinandClaude Opus 5.5 5eb7fd452a Programmatic SPLINE limits the degree to 1..10 (fixes #268) (#269)
spline {control, degree} took any degree up to the control-point count
minus one; tessellation and extents grow with its square, so a few
thousand control points with a high degree hung the app on every redraw.
The Degree prompt, spline.cv and DXF input already limit it to 1..10.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:33:31 -04:00
Valentin KleinandClaude Opus 5.5 6d7502abf9 UNDO asks for the number of operations; U undoes one (fixes #241) (#242)
UNDO now prompts "Enter the number of operations to undo or
[Auto/Control/BEgin/End/Mark/Back] <1>" and undoes that many; Enter undoes
one. Its other options say they are not available yet (Auto and Control
still take their value), so nothing that follows is run as a command.
The JSON form (`undo {count?}`) is unchanged.

U is its own command, as in AutoCAD: it undoes one operation without
asking and is what Edit > Undo, Cmd+Z and the toolbar button run.

An interactive command outside undo that changed the drawing (UNDO)
records no undo step when it finishes, matching `execute`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:37:58 -04:00
Valentin KleinandClaude Opus 5.5 ca882b38e2 TEXTEDIT prompt, Undo and Mode; MTEXT takes lines until an empty one (#234) (#239)
TEXTEDIT's prompt no longer reads "or or". Undo reverts the last edit made
in this TEXTEDIT, and Mode switches between Multiple (keep selecting) and
Single (end after one edit). At the command line and in scripts, MTEXT
takes one paragraph per input line until an empty line, instead of ending
after the first line and running the next one as a command. The Text
Editor window sends its contents and then Enter, so it still ends MTEXT.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:37:52 -04:00
Valentin KleinandClaude Opus 5.5 a6b728b0f7 AREA Object/Add area/Subtract area and DIST Multiple points take effect (#233) (#237)
AREA's Object option measures picked objects; Add area / Subtract area keep
a running "Total area" over areas measured by points or by object, and
Enter at the first-corner prompt ends the command. Length continues the
last segment by a typed length in AREA and DIST point sequences. DIST's
Multiple points measures a path and reports the running and total
distance. Arc now says it is not available yet instead of being ignored
(or ending AREA as Total).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:37:46 -04:00
Valentin KleinandClaude Opus 5.5 98a1be7455 ROTATE: Reference option (fixes #235) (#236)
"Specify the reference angle <0>" takes a typed angle or two points;
"Specify the new angle or [Points] <0>" takes a typed angle, a point
measured from the base point, or two points after `P`. The objects turn by
the new angle minus the reference angle. Previously Reference only said
"not available yet" and the values that followed were misread or run as
commands.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:37:40 -04:00
Valentin KleinandClaude Opus 5.5 ba6af44d36 ELLIPSE Rotation/Parameter and BREAK First point take effect (fixes #219) (#222)
ELLIPSE: after Rotation the next value is the rotation around the major
axis (ratio = cos angle, 0 to 89.4 degrees; a picked point gives the
angle from the centre), instead of the other-axis distance. At the
elliptical arc's start/end prompts, Parameter reads the values as
ellipse parameters and Angle switches back to true angles.

BREAK: First point asks for a new first break point; the selection pick
then only selects the object.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:37:34 -04:00
Valentin KleinandClaude Opus 5.5 89799f48d4 Keep raster images through DXF save; report entities a save can't keep (#208) (#217)
Raster images: the reader now follows an IMAGE's group 340 to its IMAGEDEF
and keeps the file path and the definition's name (its ACAD_IMAGE_DICT
key), pixel size and resolution unit. It also keeps the clip boundary (in
pixel coordinates), the clipping state, the display flags and
brightness/contrast/fade. The writer emits the IMAGE entity, plus one
IMAGEDEF per path and name, an IMAGEDEF_REACTOR per image,
RASTERVARIABLES, the ACAD_IMAGE_DICT and ACAD_IMAGE_VARS entries in the
named object dictionary, and the four CLASS records, as the DXF Reference
describes. An image without a clip boundary gets the full-image rectangle.
DWG save keeps the same data, except the resolution unit, which the DWG
bridge drops. The pixels are still not loaded.

Entity types CADCraft doesn't model (MLINE, MULTILEADER, 3DSOLID, REGION,
MESH, ...) are still not written. Writing their raw groups back into the
R2000 file would leave references to objects that aren't kept
(MLINESTYLE, MLEADERSTYLE, ACIS data in later versions), and some of the
types don't exist in R2000 at all. Another CAD program could then reject
the whole file. Instead, qsave, saveas and document.bytes now say on the
command line which ones weren't saved ("Not saved (CADCraft can't write
these object types yet): 1 3DSOLID, 2 MLINE") and list them by type in the
result's `notSaved`. `cadcraft-cli run --save` and `convert` print the
same line.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:37:26 -04:00