Commit Graph

14 Commits

Author SHA1 Message Date
Jeremy Yao a2b6c9ac4a
Animation effects follow the operating system's setting; View ▸ Accessibility ▸ Animation Effects (#759)
With Windows set to show no animations (Settings ▸ Accessibility ▸ Visual
effects ▸ Animation effects), dialogs and menus still faded in and wheel
input glided. All of that motion is egui's default behaviour, and nothing
asked the operating system.

- crates/ui-egui/src/animation.rs: AnimationPref (System, On, Off). Off sets
  egui's animation_time to 0 and scroll_animation to none (window, menu,
  tooltip and popup fades, collapsing sections, scroll bars, scroll-to
  glides), hands each wheel notch to scroll areas whole, and lets
  Cmd/Ctrl+wheel zoom reach its zoom in one step.
- System, the default, follows the OS live: UISettings::AnimationsEnabled on
  Windows, NSWorkspace's Reduce motion on macOS, the XDG Desktop Portal's
  reduced-motion (then GNOME's enable-animations) on Linux and the BSDs,
  prefers-reduced-motion on the web.
- View ▸ Accessibility ▸ Animation Effects ▸ Use System Setting / On / Off,
  the same choice in Options ▸ Display, and `ui.animations`, `options` and
  `ui.set` {"animations"}; `ui.inspect` reports whether animations show.

Drafting behaviour is untouched: tracking's acquisition pause, the
time-sensitive right-click, previews, the caret blink, realtime pan and
zoom. Plain wheel zoom already moved a whole notch per frame (#692).

macOS and Linux are compile-checked (clippy -D warnings for
aarch64-apple-darwin and x86_64-unknown-linux-gnu), not run.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-13 05:34:56 -04:00
Books 8166e4a809
AutoLISP: load and run .lsp routines (fixes #758) (#855)
* AutoLISP: load and run .lsp routines (#758)

CADCraft runs AutoLISP routines. `.lsp` files load with APPLOAD, (load)
or by dropping them on the window, and at startup from the support folder
(acad.lsp once per session or per drawing with ACADLSPASDOC, acaddoc.lsp
per drawing, then S::STARTUP; never from a drawing's own folder).
(defun c:NAME …) defines commands, (autoload) defers loading to first
use, and (expression) and !variable work at the command line and at
prompts. docs/autolisp.md lists what is supported.

The interpreter (crates/engine/src/lisp) reads source with its own
stack and evaluates on an explicit-stack machine, so a routine can stop
for input (getpoint, pause in command) and carry on later on one thread,
deep recursion is an error rather than a crash, and long loops run a
slice per frame (Esc stops them). It follows AutoLISP: 32-bit wrapping
integers, dynamic scope, *error*, vl-catch-all-apply, AutoLISP's error
wording. About 200 built-ins cover the language, command (with pause and
commands left open), entity lists (entget/entmake/entmod/entdel/entlast/
entnext/handent, through the DXF reader and writer's groups, exposed by
cadcraft-io and handed to the engine through a hook), ssget with modes
and filters, tables, system variables, get… input with initget, and
units. A routine is one undo step; each drawing has its own symbols.

Also: keywords typed as _KEYWORD match at prompts; TEXT run by a
routine's command takes one line; DIMENSION records carry their
measurement (42); Prompt::raw shows a get… prompt as written; the
interface asks Session::prompting() (a command's or a routine's prompt)
where it asked whether a command runs, so clicks, right-click and Delete
work at a routine's prompts; getcorner and based get… prompts drag a
rubber band.

Tests: the language, errors, startup files, interactive input, hostile
input (lisp/tests.rs); the #648 corpus, 18/18, when CADCRAFT_LISP_CORPUS
points at it (tests/lisp_corpus.rs); drop, frames and canvas clicks in
the app (ui-egui/tests/lisp_files.rs).

* AutoLISP: bound memory, drop nested values iteratively, guard the per-frame tick

- Cons cells drop with a work list over both car and cdr (and function
  bodies), so a list nested a million deep in its car no longer overflows
  the stack when it goes away.
- Memory caps so a runaway routine fails with a LISP error instead of an
  uncatchable out-of-memory abort: at most 5,000,000 live cons cells
  (counted in Value::cons / Drop, checked every step; cells the drawing's
  variables already held at the run's start don't count against it), no
  string a built-in makes longer than 8 MiB (strcat checks before joining,
  printed text is cut), and a 2 GiB per-run budget for new strings,
  printed text and files read.
- Session::lisp_tick runs under catch_unwind, and so does every slice of
  the LISP machine: a panic ends the routine with "internal error", puts
  the drawing back and leaves the session usable.
- Regression tests for each (deep car nesting, nested functions, the cell
  cap, string length and budget, a panicking built-in in a blocking run
  and in a ticked one).

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 05:33:57 -04:00
Books 7fcdc6e3bc
Active viewport highlight: selection on the GPU, hover cached apart, kept inside a clipped outline (#770)
* Active viewport: redraw while panning and zooming inside it, without rebuilding the layout (#688)

Inside an active MSPACE viewport, middle-drag, trackpad pan and wheel or
pinch zoom change the viewport's view through set_view_quiet, which makes
no new revision. The layout's display list, which draws model space
through each viewport, was cached on (drawing, revision, zoom band), so it
kept showing the old view until a command such as ZOOM rebuilt it: the
viewport froze, or appeared to ignore the zoom.

The active viewport's contents are now drawn apart from the layout's list,
the way model space itself is drawn. They are built once for an area
around the viewport (half its larger side of margin all round), then moved
and scaled to the current view on the GPU and drawn under the layout's own
objects. They are rebuilt when the drawing changes, when the zoom has
moved 2x from the view they were built for, when the view leaves the built
area, and once a zoom comes to rest, so dash lengths and curve detail
follow the viewport's scale again. A pan step takes about 1 ms with 70,000
polylines, where rebuilding the layout takes about 140 ms per frame.

Rectangular viewports are clipped with the scissor rectangle. A clipped
viewport's outline is drawn into a stencil buffer as a triangle fan that
flips a bit (even-odd, as the outline clips on the CPU), and its contents
draw only where the bit is set; the native window now asks eframe for an
8-bit stencil buffer, and every canvas pipeline names its format. Where
there is no stencil buffer (the CPU canvas, the web runner), a clipped
viewport's contents stay in the layout's list, whose cache now follows the
viewport's view.

Render: Options::skip_viewport leaves a viewport's contents out of a
layout, and viewport_content builds them for any area. Viewport contents
are built on all cores, like model space (130 ms to 37 ms for 70,000
polylines).

* Active viewport highlight: selection on the GPU, hover cached apart, kept inside a clipped outline

Inside an active MSPACE viewport the selection highlight was drawn as
dashed egui shapes on the CPU every frame, about 8-13 ms per frame with
7,000 polylines selected, and its geometry was cached together with the
hover, so moving the cursor over objects tessellated the whole selection
again. The selection and the hover are now cached apart, and the
selection goes through the GPU highlight path model space already uses:
about 0.1 ms per frame. In a clipped viewport it is kept inside the
outline with the stencil mask its contents use; before, it was only cut
to the outline's bounding rectangle.
2026-10-13 04:34:40 -04:00
MarekSpodBiedry 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 <noreply@anthropic.com>
2026-10-11 14:27:46 -04:00
Valentin Klein 74e72c93f8
Plotters: device configurations with paper sizes and printable areas (fixes #564) (#570)
Squash of PR #570 by @vangel76.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:25:00 -04:00
Valentin Klein b13f1e6fa4
OPTIONS: preferences dialog, JSON form and a user profile that survives restarts (fixes #23) (#531)
Squash of PR #531 by @vangel76.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:34:34 -04:00
CyberFox-81 7dead193e4
Web: opt-in embedding bridge for host pages (?host=parent) (#509)
Squash of PR #509 by @CyberFox-81.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:47:27 -04:00
Valentin Klein b3a73626b2
SAVEAS version and binary DXF, default save version (fixes #421) (#431)
Squash of PR #431 by @vangel76.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:19:47 -04:00
Valentin Klein 3b73884abd
Deliver browser saves as downloads; keep unsaved state when they fail (#133) (#193)
On wasm, saving skipped the file write but still marked the drawing saved,
renamed its tab and reported success, so nothing reached the user. Saving
now hands the bytes to a host delivery hook (`file::set_deliver`); the web
app installs a small web-sys download helper (Blob + object URL + temporary
<a download>), reusable for other files such as plots. Without the hook, or
when the download can't be started, save errors and the drawing stays dirty.
A started download counts as saved and keeps the name for later QSAVEs.
The web app's Save/Save As (menu, close prompt) download under the drawing's
name, as there is no file picker in the browser.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:39:58 -04:00
Valentin Klein a560048dd0
Web: File > Open… shows the browser's file picker (fixes #55) (#199)
The web app ran CadApp with Services::default(), so OPEN found no file
picker and returned without doing anything: no picker, no message.

The web app now installs an open picker (apps/cadcraft-web/src/upload.rs,
web-sys only): a hidden <input type="file" accept=".dxf,.dwg"> is
clicked, the chosen file is read in the background (Blob.arrayBuffer)
and handed to the app on its next frame, which runs OPEN with
{data, name} through the new CadApp::open_bytes. Cancelling the picker
does nothing. Files dropped on the page, which eframe hands over unread
on the web and the UI ignored there, take the same route.

Files over 256 MB (CadApp::MAX_OPEN_BYTES; the browser holds the drawing
several times while decoding it) are refused before they are read.
Unreadable or rejected files report on the command line and in the
status bar; nothing panics.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:38:20 -04:00
Dmytro 659d0fa125
i18n: add Ukrainian interface translation (#36)
Rebase onto main: the interface language is saved with main's other
preferences (cadcraft.prefs, beside the theme) instead of its own storage
key, on desktop and web; strings main added or changed in the touched
places (Dynamic Input settings, Properties header and current-property
rows, block picker, theme and command-history menu items, open/save
filters) are wrapped and translated; main's native-menu poll order is kept.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 13:48:44 -04:00
by Truitt 3e0adadf94
Dynamic Input: pointer input boxes with Tab, comma and relative entry (#60)
* Dynamic Input: pointer input boxes, Tab separator, relative entry

While a command asks for a point and Dynamic Input is on, the cursor
tooltip shows a pair of value boxes, following AutoCAD's pointer input:

- The first point of a command is X and Y, absolute.
- Second and next points default to polar (distance and angle) measured
  from the last point, so no `@` is needed.
- Typing `,` after a value locks it as X and moves to Y; `<` locks it as
  the distance and moves to the angle. A locked value holds the cursor and
  shows a lock.
- Tab types the separator for the format on show, so `40` Tab `90` is 40
  units at 90 degrees, and with the Cartesian setting `40` Tab `40` is 40
  over and 40 up.
- Enter or Space sends the entry with `@` in front, filling an empty box
  from the cursor. A plain number and Enter is still direct distance entry;
  `@`/`#` entries, keywords and command names go through untouched.

The command-line text is the single source of truth: the boxes are a view
of it, so the command line always shows exactly what will be sent
(`40,`, `40<90`) and Backspace simply edits it.

Tab is taken from egui in the raw input hook (desktop and web) while no
widget has focus. Otherwise egui moves keyboard focus to the next button on
Tab and the command line stops receiving keystrokes.

Two new settings mirror AutoCAD's: DYNPIFORMAT (1 = second points start
Cartesian) and DYNPICOORDS (1 = absolute), through SETVAR and in Drafting
Settings under Dynamic Input. The tooltip uses the theme tokens, matching
the command line.

Scripts, the control channel and MCP never pass through the boxes and keep
the plain command-line rules. The parsing lives in a small pure module
(dyninput.rs) with unit tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019uMCkRAB8DMd8MEmoY9Vyr

* Dynamic Input tooltip: border colour from theme tokens

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>
2026-10-10 13:21:39 -04:00
Brandon Thomas 3da0219ac9 M1/M2/M6/M10: draw/modify depth, grips API, layouts/viewports/PLOT to PDF, GPU canvas, R-tree index
- All 2D Draw-menu items: ARC variants, CIRCLE TTR/TTT, DIVIDE/MEASURE, REVCLOUD, WIPEOUT, 3DPOLY,
  MLINE, HELIX, SPLINE CV, CENTERMARK/CENTERLINE, XLINE and RECTANG options.
- PEDIT, SPLINEDIT, LENGTHEN, ALIGN, BLEND, REVERSE, NCOPY, CHSPACE, FLATTEN, ARRAYPATH, TEXTEDIT;
  FILLET with arcs and polylines; TRIM ellipses and splines; grip editing commands.
- Layouts, MVIEW/VPORTS, viewport scale/lock/layers, PAGESETUP, PLOT/EXPORTPDF with our own PDF
  writer; paper-space rendering through viewports.
- GPU canvas (wgpu batches, CPU fallback); rstar spatial index for pick/select/osnap;
  cadcraft-cli perf harness.
- CAL, FIND/replace, MASSPROP, MEASUREGEOM; TrueType outlines in cadcraft-fonts; contributors
  standard (xtask contributors, About window credits); docs/mcp.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:40:03 -04:00
Brandon Thomas d9ab00c53c M0: CADCraft skeleton and vertical slice
Workspace (geom, dxf, color, doc, fonts, render, io, engine, ui-egui, mcp; apps cadcraft,
cadcraft-cli, cadcraft-web; xtask), AutoCAD-for-Mac style UI with code-drawn icons, command line
with prompts/keywords/AutoComplete, draw and modify commands, object snaps, polar/ortho,
selection, undo, layers and properties palettes, DXF read/write, SVG/PNG export, MCP server,
JSON control channel, CLI, wasm build, README, ROADMAP, AGENTS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:22:25 -04:00