* Add Chinese language support to preferences
* Add Chinese language support to preferences
* Chinese: register it as a reported language and draw it with an installed CJK font
The catalog didn't compile (Self::SimplifiedChinese) and egui's bundled
fonts have no Chinese glyphs, so the interface would have shown boxes.
Fonts are never bundled: the interface now looks for an installed CJK
font (PingFang, Microsoft YaHei, Noto Sans CJK, WenQuanYi...) and adds it
after the Latin fonts when Chinese is shown. Without one the interface
stays in English and says why. ATTRIBUTION row; language lists updated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Update Chinese translations in zh.tsv
* zh: drop a stray 外形 from the array limit message
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
`execute` and the interactive `finish` each called the group, constraint and
associativity passes in sequence, with slightly different arguments. They now
both call `Session::after_command(before, uid, ok)`, which lists the passes
once, so a pass cannot be added on one path and missed on the other. The
arguments each path passed are unchanged: `execute` passes the document uid and
`undoable && ok`; `finish` passes no uid and `true`.
tests/post_command_passes.rs pins that MOVE and ERASE, run through `execute`
and typed, prune groups, purge/re-solve constraints and update an associative
hatch identically, in one undo step that undo fully reverses.
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* doc: Space::is_paper/layout/tab_name helpers replace hand-written matches
Twenty-seven call sites in 15 files across engine, io and ui-egui matched Space by hand to get
"is this a layout", "which layout" or "Model or the layout's name". Route them
through three small helpers on Space instead. No behaviour change.
This also makes adding a space kind a few-file change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* doc: keep Space::Model comparisons that no helper shortens; test the helpers
Only the sites that repeated the same match (tab name, layout name,
paper lineweights) go through the helpers; plain Model comparisons and
plot_window's match on (space, plotArea) stay as they were.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* Property-based and hostile-input tests for geometry numerics (gaps.md S6)
A deterministic xorshift64* generator (no new dependency) drives a few
thousand cases per property in crates/geom/src/property_tests.rs:
- line/line: hits lie on both lines, parameters reproduce the point,
swapping the operands gives the same point, finite results agree with
the infinite parameters
- line/circle and circle/circle: hits lie on both curves, results are
symmetric, properly crossing circles match the closed form; hostile
inputs (NaN, infinities, subnormals, raw bit patterns, 1e300) never
panic and the guarded functions return only finite hits or none
- segments: extending only adds hits
- closest points: fixed point of closest, perpendicular inside the
segment, endpoints outside
- arcs: at(0)/at(1) are the endpoints at radii from 1e-6 to 1e6; the
three-point arc keeps {start, end} = {p1, p3} and contains p2 (the
CCW-only storage swaps the pair when p2 is on the clockwise side, as
DXF stores arcs) and matches the circumradius closed form; collinear
inputs are rejected
- bounds and vector identities
- known answers at 1e6-1e7 coordinates hold to 1e-9 relative
gaps.md S6 removed and geometry-parity.md's Tests section updated in
the same change.
* docs: drop the sentence the new numerics tests make stale
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* PDFBOOK: plot sheet frames in model space into one PDF per row of frames
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* PDFBOOK: no Cmd+Shift+P shortcut (AutoCAD's Quick Properties toggle); simpler uk label
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: simanov <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* MTEXT: paragraph indents and tab stops are in drawing units
AutoCAD writes \pi, \pl, \pr and tab stops in drawing units, not multiples of the text
height. A file with \pi27.485 on 6-high text in a 120-wide box laid out with a 165-unit first
line indent: the first line vanished and wrapped words landed elsewhere. The JSON mtext
parameters indent, firstIndent, rightIndent and tabs follow (a list's default indent is now
3 text heights, in drawing units).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* MTEXT editor: list indent of three text heights in drawing units, matching the new \p units
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: simanov <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
AutoCAD stores a TrueType text style's family (e.g. "GOST type A") in the style's ACAD xdata
and leaves the font file name empty. Styles without a file now take that family.
Family-name lookups also look in files whose name starts with the family's first word, so
"GOST type A" finds GOST.TTF (the stem shares only 4 characters with the family name).
Co-authored-by: simanov <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
BREAK and BREAKATPOINT (break_entity) and line/arc/polyline JOIN
(modify::join) removed their source objects and added replacements
without checking the source layer's lock, so a locked line could be cut
or merged into a polyline. ERASE, MOVE, TRIM, FILLET, EXPLODE and the
ellipse/spline JOIN already guard against this.
Both now refuse with the same errors the sibling commands use:
"The object is on a locked layer." (BREAK, BREAKATPOINT, like FILLET)
and "An object is on a locked layer." (JOIN, like the ellipse/spline
join). JOIN refuses the whole request when any selected object is
locked, so a mixed-layer pair keeps its locked original in either
order. The guards sit in the shared functions, so JSON calls with
explicit handles or the selection, the interactive prompts and the
command line all reach them. The interactive commands report the
refusal on the command line.
Tests (crates/engine/tests/break_join_locked_layers.rs, red before the
fix): BREAK and BREAKATPOINT on a locked line by handle and by
selection; BREAK on a locked arc and circle; JOIN of locked lines and
of a locked/unlocked pair in both orders; interactive BREAK and JOIN.
Each checks the drawing is unchanged while locked, and that the same
edit works unlocked (BREAK also undoes and redoes).
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
At "Select objects", CP and WP take a polygon (Enter closes it) whose
inside marks the vertices that move, as a crossing window does; F takes a
fence, and what it crosses moves whole, like objects picked one at a
time. BOX works too, Undo takes back the last point, and the polygon or
fence is drawn while its points are placed.
The JSON form takes `polygon` or `fence` in place of `window`; both are
capped at the selection modes' point limit and refuse non-finite points.
Co-authored-by: Chanthawat Kiriyadee <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
A colour or freeze set in the host on an xref's layers (xref|layer) was lost on save:
the saved view dropped every xref-dependent symbol, layers included, and the DWG save goes
through the same DXF tags. The reader already keeps such layers when the xref loads.
The xref's layers are now written as xref-dependent LAYER records (DXF Reference, LAYER
group 70: 16 externally dependent, 32 resolved), as AutoCAD does with VISRETAIN = 1, with
the xref linetypes they use (same flags): without them a DWG falls back to Continuous.
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
A hatch inside a block was written with no source objects (the writer looked
for them in model and paper space only), and no boundary object carried the
hatch as a persistent reactor. AutoCAD keeps the two linked both ways; without
it a stretched dynamic block in AutoCAD moved the outline but not the fill.
Co-authored-by: Cody Vogt <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
A layout VIEWPORT's frozen layers were saved only as CADCraft's own
1001 CADCRAFT / 1000 VPFROZEN xdata. Other programs ignore that, so in
any third-party viewer every viewport showed every layer.
The writer now also emits the standard group 341: one soft pointer per
frozen layer to its LAYER table record handle, after 90/340 and before
the xdata. A frozen name with no LAYER record is skipped for 341 only
(the xdata still carries it). The reader keeps preferring the xdata; a
VIEWPORT without it (a file from another program) resolves its 341
handles through the LAYER records' group-5 handles, ignoring unknown
handles and keeping the 341 order.
Not covered: the DXF-to-DWG converter does not carry group 341 yet, so
a DWG keeps per-viewport freezing only through the CADCraft xdata.
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* Command line: a bare system variable name runs SETVAR on it (part of U10)
Typing ORTHOMODE, PICKADD and the like at the command line returned
"unknown command". Session::start now falls back to SETVAR with the name
already entered when the text is not a command but is a known variable;
dash, dot and underscore prefixes never mean a variable, and real
commands still win. Read-only variables print their value, and the
transparent form ('OSMODE) resumes the interrupted command.
Checked: fmt, clippy, test, assets, layers and wasm pass; also driven
through the control channel in the running app.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Tests: every variable name, command collisions and hostile input for bare SETVAR
Covers each variable SETVAR lists: a name that is not a command starts SETVAR
(or prints a read-only value), a name that is also a command keeps the command,
and odd input (lone apostrophes, NUL, wide characters, a 1 MiB name) never
panics and leaves the session usable. Disabling the fix makes all three new
and old tests fail.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Add the 238 rows that still fell back to English, taking the catalog to
1,231 rows: it now covers the reference catalog and every marked UI
literal (commands, menus, tool groups and tooltips, object snaps,
drafting prompts, dialogs and Preferences), so the i18n tests report no
remaining gaps for pt-BR.
The translations follow the terminology already used in the catalog
(cota, hachura, camada, pinça, janela gráfica, referência a objetos) and
preserve placeholders ({n}, {name}) and trailing ellipses exactly.
Also add a bundled-font glyph test for the catalog's accented letters,
mirroring the German and Ukrainian ones, and record the completion in
docs/localization-parity.md (status line and revision history) and
docs/localization.md.
Written from CADCraft's English strings; no proprietary localization
resources were copied.
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Other applications' xdata on an entity was kept beside the drawing,
keyed by handle, so a copy (new handle) lost it: a copied hyperlink
(PE_URL) had no link, third-party tags stayed on the original only.
In AutoCAD xdata is part of the object.
Move entity xdata onto the entity as Common::xdata (the same raw
groups); every copy path already clones Common. The DXF reader puts it
there (handle references still noted for remapping), the writer takes
it from the entity, PDF hyperlinks come from the entities. EXPLODE's
pieces of an object don't inherit its xdata. Table record and block
xdata stay in Foreign.
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
A client could only learn the top-level methods from docs/control-protocol.md;
help, methods, ping and the like all came back as "unknown method". The app
and the headless backend now answer control.methods with each method they
serve ({method, params, description}) and ping with "pong", and an unknown
method's error points to control.methods. A test keeps the app's list equal
to the doc table and checks every listed method is dispatched.
`--control` as the last argument read `args.next()` as None and reset the
port taken from CADCRAFT_CONTROL_PORT, without logging anything, so
`CADCRAFT_CONTROL_PORT=47321 cadcraft --control` started no control
channel. A bare `--control` now keeps the variable's port and logs a
warning either way; a port given on the command line still wins. The
precedence is documented in docs/control-protocol.md.
An R2018+ MTEXT can carry an embedded object (group 101) in which 10 is the
X-axis direction and 11 the insertion point. AutoCAD writes one for MTEXT with
column settings, and so does the DWG bridge. The reader took the first group 11
anywhere in the entity as the direction, so such text turned towards its own
insertion point. Read 11/21 and 50 from the MTEXT's own groups only.
* Keep the layout tabs inside the status bar
With many layouts or long layout names the tabs ran under the coordinates and
the drafting toggles and off the window.
The tabs now stop before the right-hand controls, leaving room for the
coordinates: Model and "+" stay, then the run of layouts that fits, always
holding the current one. A name wider than 150 px is cut with "…" and shows in
full on hover. When some layouts don't fit, a button after the last tab lists
them all; the "Layout list" button at the left of the bar opens the same list
(it did nothing before).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Status bar test: expect the other layout tabs only when the bar has room
At 900 pt the layout tabs that don't fit move into the layout list (d2fe40b), so
only the current tab is painted there. The test still required "Layout2" at
every size and failed on main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Chanthawat Kiriyadee <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* REFEDIT, REFSET and REFCLOSE: edit block references in place (#813)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Move REFEDIT translation lines into the middle of the catalogs (avoids PR merge conflicts)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
* MLEADEREDIT, MLEADERALIGN and MLEADERCOLLECT (#812)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Move MLEADER translation lines away from the end of the catalogs (avoids PR merge conflicts)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Plain TEXT and MTEXT are picked anywhere inside their box, but a
dimension or multileader was only found near its lines: its hit geometry
is built without text. Hovering or clicking the words of a dimension found
nothing (on a survey plan, 2 of 34 letters of a dimension's text).
A point inside the box around the words, turned with the text, now picks
the object. A dimension with a block, as AutoCAD saves them, is drawn
from that block, so its text box comes from the TEXT and MTEXT in the
block, not from the text the dimension's own geometry would place (on the
same plan that was a different place, eight times smaller). A
multileader's text box comes from its MTEXT layout.
Co-authored-by: Cody Vogt <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
The view-limited path differs from full detail by ~3e-16 on Windows (float noise from the rotated-frame round trip); the test now compares structure exactly and coordinates within 1e-9.
Co-authored-by: Claude Sonnet 5.5 <[email protected]>