* Properties: a dimension's text height, arrow size, overall scale and precision (#1259)
The Properties palette showed only a dimension's text override and style,
so the only way to change its text height was a new dimension style.
It now shows Text height (DIMTXT), Arrow size (DIMASZ), Dim scale overall
(DIMSCALE) and Precision (DIMDEC) with the values the dimension draws
with. Editing one sets a per-dimension override (DIMOVERRIDE) on the
selected dimensions, as AutoCAD's Properties palette does; the style is
left alone. properties.set takes textHeight, arrowSize, dimScale and
precision for dimensions and refuses bad values before changing anything.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* props: keep DIMOVERRIDE's doc comment on dim_override
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
ID always printed Z = 0, DIST's Delta Z and angle from the XY plane were
0, and LIST printed Z = 0 for lines, circles, arcs and polyline vertices,
though the drawing kept every Z: a topographic drawing's levels could not
be read in CADCraft.
Points reach these commands in 2D, so the Z is that of what is drawn at
the point, as AutoCAD reports a snapped point's: the object a pick finds
there (a point, a 3D line or 3D polyline interpolated, a polyline's
elevation, down Z for a mirrored one, a circle, arc, text, block or the
plane of a 3D face), else the current elevation (ELEVATION). A Z given
with a JSON point wins. DIST gives the 3D distance, Delta Z and the angle
from the XY plane; LIST the Z of lines, circles, arcs and text, a line's
3D length and deltas, and a polyline's elevation.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Paper-space units followed the drawing's MEASUREMENT only, so a layout
whose page setup is in millimetres (plot paper units 1), common in
drawings from AutoCAD with MEASUREMENT 0, was taken as inches: its PDF
drew everything 25.4 times too large, off the page.
PageSetup gains `paper_mm`, read from LAYOUT group 72 = 1; 72 = 0 still
leaves the units to MEASUREMENT, since earlier CADCraft versions wrote 0
for every layout. The layout's sheet, its PDF/raster plots, PREVIEW and
the pens of what its viewports show use the layout's units, and the
writer stores 72 = 1 for layouts in millimetres (it wrote 0 always).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
acadrust writes every paper-space entity with entity mode 1 ("paper
space"), including those of *Paper_Space0, *Paper_Space1… and relies on
each block's entity list; AutoCAD reads mode 1 as the *Paper_Space
block's, so what was drawn on a second layout (and its viewports, in
drawings from AutoCAD with several layouts) landed on the first one, and
the second stayed empty. The DWG bridge now writes those entities owned
by their block (entity mode 0 with the owner handle), as the public DWG
specification describes; the first layout's entities stay mode 1.
The io tests read CADCraft's DWG back with acadrust (a dev-dependency,
the same version as cadcraft-dwg's) to check the modes as written.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* DIMASSOC: no standard reference to a polyline's vertex or segment (#1049)
A dimension on a polyline vertex was written with an AcDbOsnapPointRef
naming the polyline but no subentity (groups 73 and 91 both 0); AutoCAD
then resolved it to the polyline's first segment when it regenerated
the dimension, and the value changed without warning (210 became 5).
Such points (LWPOLYLINE, 3D polyline, MLINE, also as the second object
of an intersection) are now left out of the DIMASSOC object, as the
report suggests: AutoCAD sees them as non-associative and the values
stay right. CADCraft keeps every link through its own xdata, and
references to single-curve objects (lines, arcs, circles, points) are
written as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DIMASSOC: reference polyline vertices by segment, as AutoCAD does
Following the black-box observation of AutoCAD 2026 in the PR review: a
polyline vertex is referenced as an end of one of its segments,
subentity type 73 = 2 (edge) and the 1-based segment in 91 (segment k
runs from vertex k-1 to vertex k; a closed polyline's closing segment is
the last), with osnap 1 / parameter 1.0 for a segment's end and 13 / 0.0
for its start. Vertex 0 is written as the start of segment 1, vertex k
as the end of segment k, falling back to the other neighbour when that
one is an arc; a vertex between two arcs (where AutoCAD writes an angle
parameter, not yet tested) still gets no reference, nor do 3D polylines
and multilines.
Reading uses the segment and osnap for polylines: AutoCAD leaves the
reference point at 0,0, so the nearest-vertex fallback attached
AutoCAD's associative dimensions on polylines to the vertex nearest the
origin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* AutoCAD interop: justified text, DIMPOST, current dimension style, *D block colours
What AutoCAD takes from CADCraft's DWG/DXF as it is:
- Justified TEXT/ATTRIB/ATTDEF (#1050): group 10 is now where the
baseline starts, from the alignment point and the text's width, not
the alignment point itself; AutoCAD drew centred and right-aligned
text from DWG starting at its alignment point. A stored point close to
CADCraft's own (AutoCAD's, exact for its fonts) is kept.
- DIMPOST (#1053) is written even when empty: the DWG bridge defaulted a
missing one to "<>", and AutoCAD then dropped the "R" of radius
dimensions when it regenerated them.
- The current dimension style (#1051): the DXF header now carries its
DIM* variables (AutoCAD draws new and updated dimensions from them);
the DWG bridge points $DIMSTYLE at the style by name (its handle was
renumbered, so the writer fell back to Standard), and reading a DWG
gives the current dimension and text styles their names back.
- *D blocks (#1052): lines, arrows, extension lines and text take the
style's DIMCLRD/DIMCLRE/DIMCLRT instead of ByBlock.
#1054 (group 42 of a rotated linear dimension) was already fixed on
main by #855; acad_interop.rs keeps it covered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test: angular dimensions store their angle in radians (group 42)
Three-point (60°, 174°) and two-line (120°) angular dimensions, read back
from the DWG as DXF. Already right on main since #855; this guards it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Justified text made here: Left Middle and Left Bottom write their baseline start too
text_insert kept a stored insertion point that was within 0.1 x an
estimated width + 0.5 x height of CADCraft's baseline start, taking it for
AutoCAD's own. The width was estimated from the alignment point's offset,
which for Left Middle is only h/2 (vertical), so a text made here, whose
stored point is the alignment point, passed and kept it: AutoCAD drew it
half a height high (Left Bottom: a third). Reported with a test by
@lee-cyrille-maskell.
A stored point equal to the alignment point is now always stale. Otherwise
the offset is split along and across the baseline: along it the same slack
as before (a tenth of the text's width plus half its height, as fonts and
their substitutes differ most in width), across it a fifth of the height.
Multiline attribute values keep the distance-only check, since their lines
are placed by the embedded MTEXT.
On 12 drawings made in AutoCAD (1,055 justified texts) the insertion points
written are the same as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A render of a large drawing (a set of sheets in model space) fits every
sheet in one image, too small to read. `render` takes `window`
[[x0,y0],[x1,y1]]: just that area, 1600 px wide with the window's shape
unless sized (the app's and the headless `ui.render` both; the display list
is built for that view only). `View::of_window` sizes it.
`find_text` (engine command `texts`, read-only) returns the readable text of
TEXT, MTEXT (formatting removed), block attributes and multileaders with
insertion point, height, rotation, style, layer and handle, filtered by
content, layer and a window. FIND selects and can replace; this only
reports, so an agent can find room names, sheet numbers and titles without
reading all the geometry.
On a 39,600-object permit set: 5,861 texts in 0.02 s; one 90 x 59.4 sheet
rendered at 2400 x 1584 in 0.5 s.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Bump acadrust to 0.6.4 and read DWG in its failsafe mode: an object
record the reader can't decode is skipped instead of failing the whole
file. The drawing's new read_notes (never saved) say how many records
were left out and of which types; OPEN echoes them, returns them as
`warnings`, and the app shows the first in the status bar. A file whose
objects stream can't be read at all is still an error.
acadrust 0.6.4 writes a merged table cell's anchor with 173=0 and the
span, so the DXF reader now takes a merge from the span alone. Unknown
records the DWG writer can't write back are dropped before writing.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Drawing (#674): in stroke-font (SHX) text, East Asian wide characters
from a fallback font fill a square cell from the baseline to the text
height, as a big font's do, instead of being scaled by the fallback
font's Latin cap height (about 1.3-1.5x too large and too widely spaced,
so CJK labels overran their space). A line no longer ends with the
stroke font's letter gap after a fallback glyph.
Interface (#513): a CJK font is installed after the Latin one in every
interface language, not only Chinese, so Japanese or Chinese file,
layout and layer names stop showing as boxes. A Japanese font comes
first unless the interface is Chinese; a craft-fonts face embedded in
the build comes last, for systems (and the web build) without one.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Another program's file can name a $HANDSEED below handles it uses (a DWG
read through the bridge did: its seed was under the handles of its
dimension blocks). The faces a polyface mesh becomes were numbered from the
seed while reading, so they could take handles of records further on in
other lists (another block); within one list duplicates were already
renumbered. A DWG keeps one object per handle, so saving one lost the
duplicates: 15 faces of a block's mesh in a permit set.
Before entities are read, every handle a file uses is now reserved. A file
CADCraft wrote names its own next handle (#495), above which its rebuilt
records are numbered, so it is left to that and resaves stay identical.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Saving regenerated every dimension's anonymous block, even dimensions read
from another program's file and never touched: the definition points went
and other programs then showed CADCraft's drawing of each dimension (a
dimension line split around its text came back whole, for instance).
A dimension still linked to the block it was read with hasn't changed
since: every edit drops the link, and the screen draws that block while it
is there. Its block is now written as read, under its name and handles;
edited and new dimensions are generated as before.
On a 1,013-dimension permit set saved DWG -> CADCraft -> DWG, every
dimension's block now matches the original (before: none).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
From AutoCAD 2013 on, a 3DSOLID, REGION or BODY doesn't carry its ACIS
geometry: the binary SAB lives in the file's data store section (ACDSDATA),
one ACDSRECORD per entity bound by a 320 handle. CADCraft kept those
entities (as unknown entities, with their handles) but dropped the section,
so a drawing saved as DWG or DXF had empty solids, and saving reported them
as not saved.
- doc: Foreign::data_store keeps the section's groups as read;
data_store_entities() lists the entities it has records for.
- io: the reader keeps the section; the writer puts it back for files of
2013 and later (and in the DXF the DWG bridge converts, which acadrust
reads into the DWG), without the records of entities no longer written.
Nothing is written for older files or when no record's entity remains.
- engine: not_saved_as(d, version): from 2013 a solid, region or body with a
data store record isn't reported; saves and document.bytes pass the version
being written.
On a 39,600-object permit set with 625 3D solids and 2 regions, DWG ->
CADCraft -> DWG now keeps all 627 bodies with byte-identical SAB (before:
none), and the save no longer reports them.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MCP clients start their servers when they start, usually before CADCraft is
open, and `--connect` refuses to start then. `--lazy` starts the bridge
anyway and reaches the app at the first tool call; until the app answers,
each call fails with how to open it. `--launch` (implies --lazy) opens
CADCraft on macOS with that control port at a call that finds nothing, and
waits up to 30 s. Neither falls back to a headless session (#103).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>