62 Commits
Author SHA1 Message Date
4257674dfa Fix polyline offset arc bulge after joining moved endpoints (#817) (#825)
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-13 05:17:25 -04:00
MarekSpodBiedryandClaude Opus 5.5 69ea207617 Cache active viewport highlights per zoom band so zoom and pan stay smooth (#687) (#695)
Squash of PR #695 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 03:13:04 -04:00
MarekSpodBiedryandClaude Opus 5.5 c5ebf4d1cd Keep inter-word spaces in stroke-font MTEXT (#628) (#644)
Squash of PR #644 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:52:51 -04:00
MarekSpodBiedryandClaude Opus 5.5 83b8a84050 Add PTYPE as an alias of the Point Style command (#622) (#645)
Squash of PR #645 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 19:52:37 -04:00
MarekSpodBiedryandClaude Opus 5.5 5ef32b3afa Select: pick pattern hatches on their visible strokes (#489) (#599)
Squash of PR #599 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:28:14 -04:00
MarekSpodBiedryandClaude Opus 5.5 04aa716920 Page Setup: report unsupported layout plot areas as not available yet (#491) (#597)
Squash of PR #597 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:28:00 -04:00
MarekSpodBiedryandClaude Opus 5.5 5e71a75383 Web: ?cpu selects the CPU canvas instead of installing the GPU canvas (#500) (#592)
Squash of PR #592 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:27:46 -04:00
MarekSpodBiedryandClaude Opus 5.5 d431b485bd STYLE typed with no arguments opens the Text Style dialog instead of an error (#535) (#590)
Squash of PR #590 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:27:32 -04:00
MarekSpodBiedryandClaude Opus 5.5 41c3c83fb9 AREA: refuse an open elliptical arc instead of measuring the whole ellipse (#487) (#601)
Squash of PR #601 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:04:40 -04:00
MarekSpodBiedryandClaude Opus 5.5 5cb54c3969 Include multileader text in its bounds so zoom, window selection and viewports keep the whole text (#488) (#600)
Squash of PR #600 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:04:24 -04:00
MarekSpodBiedryandClaude Opus 5.5 0b5a0a2eb8 Plot: apply an explicit scale to non-fitted layout plots (#490) (#598)
Squash of PR #598 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:04:10 -04:00
MarekSpodBiedryandClaude Opus 5.5 73c91cbac5 Command line keyboard input works while the command line is hidden (#497) (#595)
Squash of PR #595 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:03:55 -04:00
MarekSpodBiedryandClaude Opus 5.5 d51971b6d7 Parameters Manager: keep a pending expression when the window loses focus (#498) (#594)
Squash of PR #594 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:03:40 -04:00
MarekSpodBiedryandClaude Opus 5.5 0fcf46d487 OFFSET: full ellipse copy goes to the side of the picked point (#501) (#591)
Squash of PR #591 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:03:24 -04:00
MarekSpodBiedryandClaude Opus 5.5 f1919e31a5 Text style rename: update text inside block definitions (#472) (#588)
Squash of PR #588 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:03:07 -04:00
MarekSpodBiedryandClaude Opus 5.5 6ef1e62755 Properties: turn a block reference's attribute text with its Rotation edit (#473) (#587)
Squash of PR #587 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:02:52 -04:00
MarekSpodBiedryandClaude Opus 5.5 9e22617679 layer.delete: clear the deleted layer from viewport freeze and colour overrides (#474) (#586)
Squash of PR #586 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:02:36 -04:00
MarekSpodBiedryandClaude Opus 5.5 c245e1de35 INSERT with explode records one undo step (#475) (#585)
Squash of PR #585 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:02:21 -04:00
MarekSpodBiedryandClaude Opus 5.5 f59b32cac7 properties.set rejects a linetype that is not loaded (#476) (#584)
Squash of PR #584 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:02:06 -04:00
MarekSpodBiedryandClaude Opus 5.5 3f781c3395 Align Text > Angle asks for the angle instead of failing with a missing-parameter error (#478) (#582)
Squash of PR #582 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 14:01:51 -04:00
MarekSpodBiedryandClaude Opus 5.5 b23b45e976 Properties: reject a malformed lineweight instead of setting 0.25 mm (#480) (#580)
Squash of PR #580 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:41:25 -04:00
MarekSpodBiedryandClaude Opus 5.5 fc30f2c2ea Properties: changing a block's scale moves and resizes its attributes (#481) (#579)
Squash of PR #579 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:41:10 -04:00
MarekSpodBiedryandClaude Opus 5.5 63a0ab4a32 dimstyle.rename and delete: cover dimensions inside block definitions (#482) (#578)
Squash of PR #578 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:40:55 -04:00
MarekSpodBiedryandClaude Opus 5.5 d3ae8f3a8b Dimension Space asks for the base, the dimensions and the spacing instead of failing (#483) (#577)
Squash of PR #577 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:40:39 -04:00
MarekSpodBiedryandClaude Opus 5.5 3363d4a78c SETVAR keeps text variables such as CLAYER as text, so a layer named 10 can be selected (#484) (#576)
Squash of PR #576 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:40:25 -04:00
MarekSpodBiedryandClaude Opus 5.5 a5a9e3ef63 SETVAR accepts point objects for SNAPBASE, LIMMIN, LIMMAX and INSBASE (#485) (#575)
Squash of PR #575 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:40:10 -04:00
MarekSpodBiedry 6d8af2e5a4 Keep egui on the System theme preference so the OS appearance is followed live (#301) (#363) 2026-10-10 18:08:41 -04:00
MarekSpodBiedry 7f84f9c874 Linetype rename keeps layer, object and current-linetype references (#74) (#195) 2026-10-10 14:40:13 -04:00
MarekSpodBiedry 3d125493f9 Layer rename re-points paper-space and block-definition entities (#75) (#191) 2026-10-10 14:39:43 -04:00
MarekSpodBiedry e13de2c2f1 Layer delete and purge keep layers used by block definitions (#72) (#189) 2026-10-10 14:39:28 -04:00
MarekSpodBiedry e5a91f2eee Keep viewport layer colour overrides through DXF save and reopen (#99) (#197) 2026-10-10 14:36:53 -04:00
MarekSpodBiedry 932ba8cc58 Respect locked layers in Clear, Cut, Stretch, Trim, Extend, Fillet, Explode and SplineEdit (#87) (#196) 2026-10-10 14:36:48 -04:00
MarekSpodBiedry 6957ef8756 Keep the active drawing when closing an earlier inactive one (#76) (#192) 2026-10-10 14:36:37 -04:00
MarekSpodBiedry da4aa413c6 Reject two-line angular constraints over 180 degrees that can never be satisfied (#73) (#190) 2026-10-10 14:36:27 -04:00
MarekSpodBiedry 5a214a0cac Reject layer.set atomically: a refused call no longer applies other changes (#71) (#188) 2026-10-10 14:36:18 -04:00
MarekSpodBiedry d648e4d1f1 Show Ctrl/Shift shortcuts on Windows and Linux, symbols only on macOS (#53) (#54) 2026-10-10 13:16:01 -04:00
1dfa3660a7 Write Wipeout entities to DXF so saving keeps them (#79) (#147)
* Write Wipeout entities to DXF so saving keeps them (#79)

* Wipeout: clip vertices are in image pixel space (origin top-left, y down)

Write and read the boundary with y flipped, so other programs show the same
shape and wipeouts from other programs are no longer mirrored vertically.
Tested with an asymmetric boundary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Wipeout: collapse nested if (clippy)

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]>
2026-10-10 13:15:07 -04:00
a82f6ff306 Keep arc-length dimensions as arc-length through DXF save and reopen (#100) (#140)
* DIMANGULAR: accept polyline segments (fixes #10)

Picking a side of a rectangle or a PLINE segment was rejected with
"Select a line or an arc." because only LINE and ARC entities were
accepted. The picked object is now resolved to the segment under the
cursor: straight polyline segments work like lines and arc segments
like arcs.

* Redraw edits made while a command is still running (fixes #4)

LINE and PLINE add geometry on every point, but the canvas only rebuilt
its display list when the drawing revision or the document pointer
changed. The revision was bumped when a command finished, and the
pointer changed only on the first copy-on-write edit, so only the first
segment appeared until the command ended. Session::feed now bumps the
revision after each input once the running command has changed the
drawing.

* Fix #14: include DWG in the Open drawing filter

* Blocks window: click a block to insert it (#8)

Each block name in the Blocks window is now a button. Clicking it starts INSERT with that block already chosen, so the command line asks for the insertion point.

* SCALE: Reference option (#17)

SCALE > Reference printed "not available yet". It now asks for the reference length (typed, or two points) and the new length (typed, or a point measured from the base point), and scales by new / reference. Zero, negative or non-finite lengths are rejected and asked again.

* Print and Ctrl+P ask where to save the PDF (#5)

PLOT/EXPORTPDF started from the menu, the toolbar, Ctrl+P or the command line had no path,
so the PDF was built in memory and dropped. They now open the save dialog (PDF filter first)
and write the file there. JSON calls from scripts, the control channel and MCP still never
open a dialog. Ctrl+P was shown in the menu but not bound; it is now.

* Scripts: split a line at keyword prompts so POLYGON 6 200,20 I 15 works (#43)

* Layer Properties Manager: stop the window growing every frame (fixes #29)

The vertical separator between the filter tree and the table takes all the
height it is offered, and a resizable window offers its previous height, so
the window grew 30px per frame until it filled the screen. Cap the row at the
table's fixed height. Regression test runs the dialog headless for 30 frames
and checks the height stays put.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* ui.render: fit the drawing and return pngBase64 like the headless render (#49, #50)

* Linux: default to XWayland for file drag-and-drop on Wayland desktops

* Fonts: per-character fallback, FONTALT and .ttc for CJK text in SHX styles (#130)

CJK text in a style that uses an SHX font (romans.shx + chineset.shx big font)
drew every ideograph as an empty box: the SHX name resolves to our stroke font,
which has no CJK glyphs, and the font scan skipped .ttc collections, where most
CJK system fonts live.

- Per-character fallback: a character the text's font lacks (stroke font or
  TrueType) is drawn from an installed font that has it, checked by cmap.
  Wide-coverage fonts go first (Noto Sans CJK, Source Han, WenQuanYi, Droid
  Sans Fallback, PingFang, Microsoft JhengHei/YaHei, MS Gothic, ...), regular
  weights before others. Fallback glyphs are sized like TrueType text (cap
  height = text height). Lookups are cached; the search is bounded (24 files,
  160 MiB, 256 faces per character, files over 96 MiB never read) and skips
  spaces, controls and private-use code points. No fallback: the box as before.
- FONTALT (sysvar, seeded from CADCRAFT_FONTALT): font used for a style font
  that can't be found (an SHX file, a missing TTF). Empty = our stroke font.
- FONTFALLBACK (sysvar, seeded from CADCRAFT_FONTFALLBACK): fonts tried first
  for missing characters, e.g. "Noto Sans CJK TC".
- .ttc/.otc are scanned; TextFont::Outline carries a ttf::Face (bytes + face
  index). Fonts are also found by family name among files with a similar
  stem, so "Noto Sans CJK TC" picks face 3 of NotoSansCJK-Regular.ttc.
- Cap height falls back to the height of "H" when OS/2 has none (DejaVu
  Sans), which fixes ttf_tests::system_font_outlines_when_available and
  render's truetype_text_is_filled_or_outlined on machines without Arial.
- README: a Fonts section for the two settings; ROADMAP M4 note.

Native SHX / big-font rendering stays on the roadmap (M4).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Fonts: cap the total bytes of system font files loaded from disk

A drawing naming many text styles could otherwise load every installed font
into memory through family-name lookups.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Pan with a two-finger trackpad swipe; keep wheel and pinch zoom

Trackpad two-finger swipes arrive as scroll events, and the canvas turned
every scroll into a zoom, so trackpad users had no realtime pan (middle-drag
needs a middle button).

Scroll now follows the device: precise point deltas (trackpad, Magic Mouse)
pan the view by x and y; notched line deltas (mouse wheel) zoom about the
cursor as before. Pinch and Cmd+scroll still zoom through zoom_delta, and
middle-drag still pans.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019uMCkRAB8DMd8MEmoY9Vyr

* Add Tru10 to contributors/people.toml

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019uMCkRAB8DMd8MEmoY9Vyr

* Trackpad pan: only on macOS, where point deltas mean a trackpad

Browsers and Wayland smooth-scroll mice report ordinary wheels in points too,
so the wheel keeps zooming there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Keep arc-length dimensions as arc-length through DXF save and reopen (#100)

* PLINE: Halfwidth is centre-to-edge, and Width answers taper the next segment (fixes #83, #104)

Interactive PLINE kept one width value: Halfwidth answers were stored as
the total width, and the ending-width answer overwrote the starting one,
so a taper became a constant width. The next segment's starting and
ending widths are now tracked separately, Halfwidth answers are doubled,
and each new segment (including the closing one) gets its widths on its
first vertex; later segments are uniform at the ending width, as in
AutoCAD. Uniform polylines are still stored as const_width, so their DXF
output is unchanged.

Fixes #83. Fixes #104.

* Arc-length dimensions: write a standard aligned record plus CADCraft xdata

DXF has no arc-length DIMENSION type, so write type 1 and mark the kind and
arc centre in CADCRAFT xdata (like ASSOC). Other programs read an aligned
dimension; CADCraft restores the arc length, through DXF and DWG.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Desktop: write log records to standard error and a rotating log file

The desktop app installed no logger, so every log::warn!/log::error!/
log::info! in the workspace was discarded: a broken app icon, the GPU
canvas set-up, the engine's command-line echo at debug level. A launch
from a desktop menu has no terminal either, so there was nothing to
attach to a bug report.

apps/cadcraft/src/logging.rs (no new dependency):
- standard error plus <settings dir>/logs/cadcraft.log; each launch
  rotates the previous log to cadcraft.1.log and .2.log, so the log of a
  run that crashed survives the next start; the file is capped at 16 MiB
- records logged before the settings directory is known are buffered
  (bounded) and written once the file is attached
- default levels: info for cadcraft*, warn for everything else;
  RUST_LOG replaces them with env_logger-style directives
- never panics: an unusable directory or a failed write leaves standard
  error as the only sink; a poisoned lock is tolerated

CADCraft had no per-user directory yet. config_dir in main.rs adds one:
CADCRAFT_CONFIG_DIR, else $XDG_CONFIG_HOME/cadcraft or ~/.config/cadcraft
(Linux, BSDs), ~/Library/Application Support/CADCraft (macOS),
%APPDATA%\CADCraft (Windows).

The logger is installed first in main and the file is opened after
argument parsing (--version leaves no file). The control server's
eprintln!s now go through the logger. A --control or
CADCRAFT_CONTROL_PORT value that is not a port is now logged instead of
silently leaving the control channel off. Documented in README.md
(Quick start > Logs).

---------

Co-authored-by: SBaksa <[email protected]>
Co-authored-by: DWAA1660 <[email protected]>
Co-authored-by: lloyd <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Dylan M. Taylor <[email protected]>
Co-authored-by: vangel76 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Tru10 <[email protected]>
Co-authored-by: Gert Pellin <[email protected]>
2026-10-10 12:58:45 -04:00
MarekSpodBiedry 3886add967 ui.render: fit the drawing and return pngBase64 like the headless render (#49, #50) (#52) 2026-10-10 12:57:53 -04:00
MarekSpodBiedry 415da86d39 Scripts: split a line at keyword prompts so POLYGON 6 200,20 I 15 works (#43) (#45) 2026-10-10 12:57:42 -04:00
MarekSpodBiedry 8995fb9174 Blocks window: click a block to insert it (#8) (#28)
Each block name in the Blocks window is now a button. Clicking it starts INSERT with that block already chosen, so the command line asks for the insertion point.
2026-10-10 12:57:36 -04:00
MarekSpodBiedry a1df2f2c8c SCALE: Reference option (#17) (#27)
SCALE > Reference printed "not available yet". It now asks for the reference length (typed, or two points) and the new length (typed, or a point measured from the base point), and scales by new / reference. Zero, negative or non-finite lengths are rejected and asked again.
2026-10-10 12:57:31 -04:00
MarekSpodBiedry 986307f764 Print and Ctrl+P ask where to save the PDF (#5) (#26)
PLOT/EXPORTPDF started from the menu, the toolbar, Ctrl+P or the command line had no path,
so the PDF was built in memory and dropped. They now open the save dialog (PDF filter first)
and write the file there. JSON calls from scripts, the control channel and MCP still never
open a dialog. Ctrl+P was shown in the menu but not bound; it is now.
2026-10-10 12:57:25 -04:00
MarekSpodBiedry 882c4d67a6 Keep dimension text rotation when saving DXF (#78) (#153) 2026-10-10 12:26:11 -04:00
MarekSpodBiedry 3e0de7e4eb Keep a free measured origin in place when its source moves (#109) (#152) 2026-10-10 12:26:01 -04:00
MarekSpodBiedry b7f83a6687 Keep viewport frozen-layer lists through DXF save and reopen (#98) (#150) 2026-10-10 12:14:21 -04:00
MarekSpodBiedry 576e6f9020 Write LWPOLYLINE elevation (group 38) to DXF (#77) (#142) 2026-10-10 12:14:13 -04:00
MarekSpodBiedry 56c39f1c17 Keep ATTDEF prompt when saving DXF (#81) (#137) 2026-10-10 12:14:06 -04:00
MarekSpodBiedry df1c074fff Keep an ordinate dimension's datum fixed when its feature moves (#105) (#157) 2026-10-10 12:14:00 -04:00
MarekSpodBiedry 947535158a Store canonical autoTypes names so accepted spellings take effect (#95) (#156) 2026-10-10 12:13:53 -04:00
MarekSpodBiedry 0af1528f4c AREA of a normal hatch no longer counts unfilled islands (#91) (#155) 2026-10-10 12:13:47 -04:00
MarekSpodBiedry 9882b85a41 Reject nonpositive MTEXT height on creation (#84) (#154) 2026-10-10 12:13:39 -04:00
MarekSpodBiedry bd46065c27 HATCHEDIT: clear the gradient when a named pattern is chosen (#107) (#151) 2026-10-10 12:13:31 -04:00
MarekSpodBiedry 52f05f1514 Fence selection now includes crossed XLINE and RAY objects (#92) (#149) 2026-10-10 12:13:24 -04:00
MarekSpodBiedry 74cbdf7e2f Keep selected objects outside the joined chain when using JOIN (#86) (#148) 2026-10-10 12:13:16 -04:00
MarekSpodBiedry a983cf1ed0 MCP bridge: do not resend a mutation whose reply was lost (#102) (#146) 2026-10-10 12:13:06 -04:00
MarekSpodBiedry 24f074a9ad Reject deleting a dimension that a remaining dimension refers to (#94) (#145) 2026-10-10 12:12:58 -04:00
MarekSpodBiedry fabc5bacd9 Fix justified TEXT extents and picking (#89) (#144) 2026-10-10 12:12:50 -04:00
MarekSpodBiedry 279c1bed6a Convert typed ellipse arc angles to ellipse parameters (#82) (#143) 2026-10-10 12:12:41 -04:00
MarekSpodBiedry 3ef52e31d8 Apply DIMOVERRIDE text together with dimension variables (#108) (#141) 2026-10-10 12:12:33 -04:00
MarekSpodBiedry 50b9a23a98 Reject out-of-domain values in PARAMETERS instead of dropping the constraint (#93) (#139) 2026-10-10 12:12:23 -04:00
MarekSpodBiedry fadbea5dfe Keep elevations when EXPLODE splits a 3D polyline (#88) (#138) 2026-10-10 12:12:16 -04:00