mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-31 06:16:51 -06:00
9a1f200795f9b2618cf197b2bb2e8c6acbb833ea
91
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5654240729 |
refactor(cli): parse cadcraft-cli arguments with clap, following RFC-0012 (#2268)
* test(cli): pin today's command-line behaviour
The later commits change these tests on purpose, so each behaviour change shows in their diff.
* refactor(cli): split main.rs into modules, move only
- output: outln!, stdout state, atomic write, export helpers, view/window helpers
- args: USAGE, option validation, framing options, MCP argument parsing and its tests
- info, convert, sample, mcp: one subcommand each
- perf: synthetic drawing, Rng, perf subcommands
- run: run subcommand and {last} handle chaining
- main: crate docs, mod lines, install_io, open, main, cli, unit tests
No behaviour change.
* refactor(cli): typed errors and run(args, out, err)
cadcraft-cli is now a library plus a thin binary. `cadcraft_cli::run(args, out, err)
-> i32` is the whole program; main.rs only passes `args_os()` and the process
streams. Handlers write through a `Streams` wrapper (output.rs) instead of the
process streams; it keeps the closed-stdout semantics (drop later output, carry
on, a closed pipe is not an error, other write errors are reported once and make
the exit code 1). `mcp` still speaks MCP on the real stdin/stdout.
Errors are typed (thiserror): CliError (error.rs) wraps EngineError, DocError,
IoError, io::Error and the per-module WindowError, ArgsError, RunError,
OutputError and PerfError. `run` prints `error: {e}` and exits 1.
The usage text and the "unknown option/argument" messages stay as one
transitional CliError::Usage(String), printed without the `error: ` prefix.
USAGE stays a const; step 4 (clap) removes both. The engine's IoHooks take
`Result<_, String>`, so the three `.map_err(|e| e.to_string())` in install_io
remain. The hooks are set-once (OnceLock), so installing them on every `run`
call is harmless.
tests/it/usage.rs calls `run` in-process with Vec<u8> writers; the headless mcp
case runs through the binary (it reads the real stdin) and a non-Unicode
argument test is new.
Behaviour changes (old -> new):
- Every error line other than the usage-class ones gains the prefix `error: `
(e.g. `--view needs ...` -> `error: --view needs ...`, `unknown command
`x`` -> `error: unknown command `x``, `invalid parameters for `open`: ...`
-> `error: invalid parameters ...`, `unsupported file type` likewise).
- `cadcraft-cli: cannot connect to HOST:PORT: e` -> `error: cannot connect to
HOST:PORT: e` (the old prefix is replaced).
- A non-Unicode argument: panic, exit 101 -> `error: argument "..." is not
valid Unicode`, exit 1.
- Unchanged: usage text, `X: unknown option --y`, `unknown option --y`,
`mcp: unknown argument ...` (no prefix); warnings and `wrote ...` lines;
the `cadcraft-cli: can't write to stdout: e` report; exit codes.
* refactor(cli): parse arguments with clap
Replace the hand-written parser (USAGE, validate_options, OptionSpec,
framing_option, parse_mcp_args, CliError::Usage) with clap derive types
in args.rs: a Cli parser and one Args struct per subcommand, with the
framing flags (--window, --view) in a FramingArgs struct flattened into
convert and run. USAGE's framing and --cmd chaining text moves into the
subcommands' and flags' doc comments and run's long help, so --help
carries it. `commands` moves from an inline closure to commands.rs.
`command()` exposes the clap definition for man pages and completions.
clap reads the arguments as OsStrings. Help and --version go to stdout
with exit 0; usage errors are clap's, on stderr with exit 2. A bare
--help or -h before `--` shows the subcommand's help even after a
mistake on the line (`run --script --help`). run opens its drawings
(FILEs, --sample, --metric) and runs its steps (--script, --script-file,
--cmd) in command-line order, from ArgMatches::indices_of. Values stay
strings checked by the handlers, with their messages and exit 1
(--window, --view, --cmd JSON, sample names, perf counts). The last of a
repeated flag wins (args_override_self on every subcommand but mcp).
Behaviour changes:
Help and version
- --help, -h, help: the USAGE page -> clap's help, first line
"cadcraft-cli: headless CADCraft".
- bare cadcraft-cli: USAGE on stderr, exit 1 -> clap's help on stderr,
exit 2.
- <subcommand> --help / -h, help <subcommand>: "unknown option --help"
(exit 1), the USAGE page (convert, help <subcommand>) or a file named
-h (info -h, run -h) -> that subcommand's help, stdout, exit 0.
- <subcommand> --version: "unknown option --version", exit 1 -> clap's
unexpected argument, exit 2.
Usage errors (exit 1 -> exit 2, clap's messages)
- Unknown subcommand, empty subcommand, unknown top-level flag: USAGE ->
"error: unrecognized subcommand 'foo'" / "error: unexpected argument
'--bogus' found".
- Unknown flag on any subcommand: "info: unknown option --bogus",
"unknown option --bogus", "mcp: unknown argument --bogus" or USAGE
(convert) -> "error: unexpected argument '--bogus' found".
- A short flag where a file is expected (info -x): opened as a file ->
unexpected argument; `--` before it keeps it a file name.
- A value flag at the end or followed by a flag (--window, --view,
--script, --script-file, --cmd, --save, --export, --connect): "error:
--script needs text" and the like, or the next flag taken as the
value (--window --view, --script --sample, --cmd --sample,
--script-file --sample) -> "error: a value is required for '--script
<TEXT>' but none was supplied". --connect followed by an unknown flag
(--connect --foo, --connect -v) names that flag instead (clap reports
the unknown argument, not the missing value).
- mcp --connect with an empty value: "error: --connect needs HOST:PORT
..." -> "error: invalid value ' ' for '--connect <HOST:PORT>':
expected HOST:PORT (...)"; --connect=-v likewise.
- mcp --connect twice: "error: --connect given more than once" ->
"error: the argument '--connect <HOST:PORT>' cannot be used multiple
times".
- mcp --lazy / --launch without --connect: "error: --lazy and --launch
need --connect HOST:PORT" -> "error: the following required arguments
were not provided: --connect <HOST:PORT>".
- Missing positionals (info, convert with 0 or 1 files, sample with 0
or 1 arguments): USAGE -> "error: the following required arguments
were not provided".
- Extra positionals: convert with three files (USAGE), and info,
commands, perf, sample, which ignored the extra argument -> "error:
unexpected argument 'extra' found".
- perf -5: opened as a file -> unexpected argument '-5'.
- A non-Unicode argument where text is expected: "error: argument ... is
not valid Unicode", exit 1 -> "error: invalid UTF-8 was detected in
one or more arguments", exit 2.
Accepted where they were errors
- --x=v is a spelling of --x v for every value flag (--window, --view,
--script, --script-file, --cmd, --save, --export, --connect); before
it was an unknown option or USAGE. A value starting with - needs the
= form (--cmd=-x).
- commands --: "unknown option --" -> `--` ends the options (all
commands listed).
- run --view extents --view current: "use only one of --window and
--view" -> the last wins, like every other repeat.
Other
- run --sample --sample opens one sample drawing (was two): repeated
flags are last-wins.
- run checks --window/--view values before opening any drawing, so a
bad framing value is reported ahead of a missing file.
- `-h` after a mistake shows the long help (`--help`): the retry asks
for --help.
Unchanged: --window with --view, framing without an image output,
--view's case-insensitivity, all value messages (--window numbers,
--view values, --cmd JSON, {last}, sample names, --script-file read
errors), multiple FILEs, --sample with --metric or a FILE, last-wins
for --save, --export, --window, --script/--cmd order, and `\n` in
--script.
clap forced: exit 2 for usage errors, its message wording, and the
unknown-flag-over-missing-value precedence above.
* refactor(cli): reject repeated flags, long version (photocraft#7071 decisions)
Behaviour changes, old -> new:
- --save, --export, --window, --view, --sample, --metric, --lazy, --launch given twice:
last one won -> usage error, exit 2 ("the argument ... cannot be used multiple times").
--connect was already rejected. --script, --script-file, --cmd and run's FILEs still repeat.
- --version / -V: "cadcraft-cli <version>" -> "cadcraft-cli <version> (<sha>)", sha from
CADCRAFT_BUILD_SHA or "dev", as the desktop app prints it.
- "cadcraft-cli version" is an unrecognized-subcommand usage error (as before; now tested).
- A value starting with "-" needs the = form for every value flag (as before, tested).
--window -10,-10,10,10 stays a usage error ("unexpected argument -1"); write
--window=-10,-10,10,10. clap's allow_negative_numbers does not accept a comma list, and
allow_hyphen_values would let --window swallow the next flag, so neither is set. The
--window help text names the = form.
* refactor(cli): typed values and flag relations
Step 6 of #2228: values are parsed into their types and flag relations
are checked at parse time, so a bad value or combination is a usage
error (exit 2) naming the flag, before any work starts. The handlers
receive typed values only. Runtime failures (files, the engine, a
`{last}` with no earlier handle) stay exit 1.
- --window parses to a Bounds2 (parse_window plus cadcraft_io's
check_window); its WindowError is the value parser's error.
- --view is a ValueEnum (extents, current), case-insensitive; the long
help lists what each value does.
- --window conflicts_with --view. In run, --window/--view require
--export (an ArgGroup "framing" with requires). A framing flag with
an output that isn't an image (convert's OUT, run's --export) is a
UsageError turned into a clap error by into_clap (ArgumentConflict).
- sample's name is a ValueEnum (bracket, floorplan); `floor` stays a
hidden alias.
- perf's N|FILE is one typed positional: a value that parses as a
count is one, clamped to 1..=20000000 as before (so `perf 0` still
times one entity); anything else is a drawing.
- --cmd 'id {json}' parses to an id and a serde_json::Value (default
{}); `{last}` is still filled in when the command runs.
- Paths are PathBufs (info FILE, convert IN/OUT, run FILEs,
--script-file, --save, --export, sample OUT, perf FILE). The engine
and io crates take file names as text, so a path that isn't valid
Unicode is refused by the handler, naming the file (exit 1).
- Deleted: check_framed_output, the --view match, ArgsError,
CliError::Window/Args/UnknownSample, RunError::CmdJson,
PerfError::BadCount.
- run's FILEs, --sample and --metric still combine (each opens another
drawing, the last is current): neither photocraft#7071 nor RFC-0012 requires
otherwise.
Behaviour changes (old -> new):
- --window with a bad value (wrong count, not a number, NaN/inf,
reversed corners, beyond 1e12): exit 1 `error: --window ...` ->
exit 2 `error: invalid value 'V' for '--window <x0,y0,x1,y1>':
<reason>`. The reasons no longer repeat the flag or the value.
- --view with a bad value: exit 1 `error: --view needs `extents` or
`current`` -> exit 2 `error: invalid value 'V' for '--view
<extents|current>'` plus clap's possible-values line.
- --window with --view: exit 1 `error: use only one of --window and
--view` -> exit 2 `error: the argument '--window <x0,y0,x1,y1>'
cannot be used with '--view <extents|current>'`.
- run --window/--view without --export: exit 1 `error: --window/--view
need an image output ...` -> exit 2 `error: the following required
arguments were not provided: --export <OUT.png>`.
- A framing flag with a non-image output: exit 1 `error:
--window/--view only apply to image output (png, svg, pdf), not
`OUT`` -> exit 2 `error: --window only applies to image output (png,
svg, pdf), not `OUT`` (or --view: the message names the flag given).
- sample with an unknown name: exit 1 `error: unknown sample `X`
(bracket, floorplan)` -> exit 2 `error: invalid value 'X' for
'<bracket|floorplan>'` plus the possible values. sample's help lists
the possible values.
- --cmd with JSON that doesn't parse: exit 1 `error: --cmd ID:
<serde error>`, after the earlier steps ran -> exit 2 `error: invalid
value 'V' for '--cmd <id {json}>': <serde error>`, before anything
runs.
- --cmd with an empty id (`--cmd=` or `--cmd ' {}'`): exit 1 `error:
unknown command `` ` -> exit 2 `... expected a command id, then
optionally its JSON parameters`.
- An empty path argument (info "", sample bracket "", perf "", ...):
exit 1 from the engine or io -> exit 2 `error: a value is required
for '<FILE>' but none was supplied` (clap's PathBuf parser).
- A path that isn't valid Unicode: exit 2 `error: invalid UTF-8 was
detected ...` -> exit 1 `error: PATH: the file name is not valid
Unicode` (text values that aren't Unicode stay exit 2). A non-Unicode
--save or --export is refused before any drawing opens.
- perf's help gives the count range.
Tests: usage.rs rows above moved to exit 2 with clap's messages; new
rows for --view with a non-image output, run's framing with a dxf
--export, an empty --cmd id, bad --cmd JSON after a good step, a
`{last}` with no earlier handle, --window beyond 1e12, case-sensitive
sample names and empty paths. The non-Unicode test covers both a path
(exit 1) and text (exit 2). Unit tests: the --window parser tests moved
to args.rs; new ones for --cmd values and perf targets; framing_options
checks the clap error kinds.
* refactor(cadcraft): move launch-argument parsing into launch_args
Moves the hand-written parser (the argument loop, control_port_from, control_flag and their tests) from main.rs into launch_args.rs, which returns LaunchArgs. No behaviour change.
* refactor(cadcraft): parse launch flags with clap (RFC-0012)
Replaces the hand-written loop in launch_args.rs with clap derive types, as photocraft's launch_args.rs does. --help is generated from the doc comments and lists the other CADCRAFT_* variables.
Behaviour changes (old -> new):
- An unknown flag (--sampel) was opened as a file; it is now a usage error on stderr, exit 2, before the window opens. A file starting with "-" goes after "--".
- A repeated --control or --sample was accepted (last wins); it is now exit 2.
- A bad port (--control abc, 70000, -1) was logged and the control channel left off; it is now exit 2. The same for CADCRAFT_CONTROL_PORT, and the error names the variable when the value came from it.
- A bare --control (no value) kept CADCRAFT_CONTROL_PORT's port with a warning; it is now exit 2 ("a value is required"). A following flag is never taken as the value.
- An empty or blank CADCRAFT_CONTROL_PORT was logged as a bad port; it is now unset.
- --control=7979 is accepted (the equals form was opened as a file before).
- --version / -V and --help stop parsing at once and exit 0; version text is unchanged ("cadcraft <version> (<sha>)").
- -psn_* (Finder process serial number) is dropped instead of opened as a file.
- A file name that is not valid Unicode used to panic in std::env::args(); it is now read from args_os and reported with a log warning, not opened, because CadApp::open_path takes text.
Copied from photocraft's crates/automation/src/args.rs: blank_env_is_unset and name_env_vars unchanged (name_env_vars gets a local use for ContextKind/ContextValue); control_port and NotAPort unchanged. allow_negative_numbers is set on --control as in photocraft, so -1 is a bad value rather than a missing one.
* feat(cli): shell completions and man pages
Adds examples/man-and-completions.rs, which writes cadcraft-cli's bash, zsh, fish, elvish and
PowerShell completions (clap_complete) and man pages (clap_mangen: cadcraft-cli.1 and one per
subcommand) into a directory. clap_complete and clap_mangen are dev-dependencies, so the binary
is unchanged.
packaging/linux/package.sh builds the example in its own cargo run, runs it (through $EMULATOR
for cross builds; warns and leaves the files out without one) and installs the man pages and the
bash, zsh and fish completions into the staged tree. nfpm.yaml already ships the whole
usr/share tree, so it needs no change.
CADCraft has no FreeBSD packaging, so there is no FreeBSD script to update. macOS and Windows
packaging are untouched.
tests/man_and_completions.rs checks the generated file names.
* ci(cli): check the command-line code with ast-grep (RFC-0012)
Adds ast-grep rules from RFC-0012, run by craft-common-actions'
reusable ast-grep workflow (Linux, no Rust build, path-filtered):
- clap-error-message: a string literal, format!, .to_string() or
SCREAMING_CASE const passed to Command::error / Error::raw
- clap-error-message-variable: the same message built into a variable
first, in the same function
- string-error: Result<_, String>, Err(format!(..)), Err(".."),
Err(x.to_string())
- args-os: std::env::args(), which panics on non-Unicode arguments
Scan paths: apps/cadcraft-cli/src, apps/cadcraft/src/launch_args.rs,
apps/cadcraft/src/main.rs. Rule tests run first in CI.
Source: photocraft#9750 at d16686cd. Only the scan paths and the
workflow path filters differ from it.
Fixes: none needed. The rule tests pass (4 passed) and the scan finds
nothing in the scanned paths.
* docs(cli): cadcraft-cli page, following RFC-0012
docs/cli.md: --help is the reference; exit codes, streams, usage
errors, values starting with -, paths, completions and man pages; the
environment variables (none for cadcraft-cli, CADCRAFT_CONTROL_PORT for
the desktop app's --control); and the rules not followed yet, with the
reason. The README links it.
* feat(cli): refuse an output that replaces an input
RFC-0012 rule 1.6: nothing destructive by default.
Behaviour change: `convert a.dxf a.dxf`, `run a.dxf … --save a.dxf` and
`run a.dxf … --export a.dxf` used to overwrite the input. They are now a
usage error (exit 2, `error: …` on stderr, nothing opened or written)
unless `--in-place` is given. "Same file" is file identity (same-file
crate), so `dir/./a.dxf` and a symlink to the input count. An output
that does not exist, or a failed identity check, is not refused; the
open or write reports the real error. Replacing an existing file that
is not an input is still allowed.
Scope: `--save` and `--export` only. A save that a --cmd or script runs
(for example `saveas`) is the engine's and is not checked.
Every write the CLI makes (convert OUT, --save, --export, sample OUT) resolves
an existing output first, so writing through a symlink updates the file it
points to and the link stays a link; the atomic rename used to replace the
link with a regular file, leaving the target (with --in-place, the input)
unchanged.
* feat(cli): --compact for JSON output
RFC-0012 rule 1.3: info and commands pretty-print their JSON by
default; --compact prints it on one line. run prints one JSON line per
--cmd result (JSON lines, one result per step), as its help and the
README document, so it takes no --compact. mcp's stdout is the MCP
protocol and is unchanged.
Behaviour change: info and commands accept --compact (it was an
unknown flag, exit 2). Their default output is unchanged.
docs/cli.md says which subcommand prints which form, and lists perf's
text table (no --json) and saves a --cmd or script runs (not checked
by --in-place) under "Not followed yet".
* fix(cadcraft): one Switch parser for boolean environment switches
CADCRAFT_VSYNC, CADCRAFT_SYSTEM_CURSOR, CADCRAFT_WAYLAND and
CADCRAFT_NO_NATIVE_MENU each had their own idea of "on". They now share
cadcraft_ui_egui::switch::Switch: 1/on/true/yes is On, 0/off/false/no is
Off (case- and space-insensitive), unset or blank is Default, and any
other value is Default and logged with the variable name. The ENVIRONMENT
help text says the same for all four.
Behaviour changes (old -> new):
- CADCRAFT_WAYLAND=0 counted as on (any value did); now only On keeps
native Wayland.
- CADCRAFT_NO_NATIVE_MENU=0 counted as on (any value did); now only On
keeps the menus inside the window.
- CADCRAFT_SYSTEM_CURSOR treated every value except ""/0/false/off as on;
now only the On spellings do, and an unknown value is logged.
- CADCRAFT_VSYNC is unchanged except that an unknown value is logged.
* fix(cli): keep paths as paths until a file is opened
A path is now text only where text is needed.
- cadcraft: FileNames.unreadable is Vec<PathBuf>, logged with {:?}
(escaped bytes) instead of a lossy rendering.
- cadcraft-cli: unicode() is called only inside open(), the one place
that sends a path to the engine as JSON. convert, sample and run
--save/--export pass the output as &Path. write_atomic, export,
export_framed and OutputError::Write take or hold a Path/PathBuf;
errors use .display(), and the `wrote ...` line uses .display().
- Dropped the dead find_subcommand lookup in launch_args (LaunchArgs has
no subcommands).
- docs/cli.md: non-Unicode names are refused only where a drawing is
opened.
Behaviour change: an output name that is not valid Unicode used to be
refused (exit 1); it is now written under that exact name. Opening such
a drawing is still refused.
Lossy strings that remain (all feed APIs that take text and only look at
the extension, never at the path the bytes are written to):
- cadcraft_io::write / write_framed (name picks the format) in sample.rs
and output.rs::export_framed;
- cadcraft_io::is_image in convert.rs, run.rs and args.rs (framing flag
check);
- cadcraft_engine::cmd::file::drops_unknown in export_framed.
These go away when cadcraft_io and the engine take impl AsRef<Path>
(the A1 follow-up). A non-Unicode extension is not a known format either
way, so it is rejected as an unsupported type, as before.
* refactor(cadcraft): read the environment once into Env
The desktop app reads its CADCRAFT_* variables once at startup into
apps/cadcraft/src/env.rs `Env` and passes the values on; no library crate
reads a CADCRAFT_* variable at run time any more.
- One `VARS` table (name, help line) feeds both the reader and the
Environment section of `cadcraft --help`. A test checks that every
variable the reader asks for is in the table, that every table entry
sets a field, and that the help lists every entry.
- vsync goes to `gpu::surface_config`, the system cursor to
`CadApp::set_system_cursor_default` (Options reset and Reset Palettes go
back to it), FONTALT/FONTFALLBACK to the `cadcraft_fonts::ttf` setters
(re-exported as `cadcraft_engine::fonts`), the settings directory to the
new `cadcraft_engine::settings_dir::set`, and to `Workspace::load`.
- The three copies of the settings-directory rule (main.rs, workspace.rs,
the engine's plotter folder) are one: `settings_dir::platform`.
- `Switch::from_env` is gone; `Switch::parse` stays.
- The child-process tests for CADCRAFT_CONTROL_PORT are replaced by tests
that inject the variable lookup (`blank_env_is_unset`) or point
`--control`'s fallback at PATH.
cadcraft-cli: plotter configurations matter to its output (`run --cmd plot`
or a script's PLOT with a user device, and the same through `mcp`), and the
font variables change how convert/run/sample draw text. So, following
RFC-0012 rule 1.7, the variables become fallbacks of flags declared with
`env =`: --config-dir (CADCRAFT_CONFIG_DIR), --fontalt (CADCRAFT_FONTALT)
and --fontfallback (CADCRAFT_FONTFALLBACK) on convert, run, mcp and sample.
Behaviour changes:
- CADCRAFT_CONFIG_DIR set but empty counts as unset everywhere: the
workspace file and the plotter folder use the platform folder (was:
workspace persistence and the plotter folder off). Logs and autosave
already treated it so.
- The workspace file and the plotter folder ignore a relative
XDG_CONFIG_HOME, as logs and autosave did (was: used it).
- A blank CADCRAFT_LOCALE means the system's languages (was: [""]).
- cadcraft-cli: the three variables apply only to convert, run, mcp and
sample, as flag fallbacks (was: read by the engine and fonts in every
subcommand, perf included). A font variable that isn't valid Unicode is
a usage error naming the flag (was: ignored).
* refactor(io): one Format enum for file extensions
`cadcraft_doc::Format` (re-exported as `cadcraft_io::Format`) is the one
place that maps a file name's extension to a format: Dxf, Dwg, Dwt, Svg,
Svgz, Png, Pdf. `from_path` is ASCII case-insensitive; no extension (or a
trailing dot) is Dxf; an unknown extension is None. `READ` and `WRITE`
list the formats `read` and `write` accept. It lives in cadcraft-doc
because the engine depends on cadcraft-io only for tests.
Each place used to match on its own lower-cased extension string and now
matches on Format, with the same results:
- io::read: svg/svgz -> SVG reader, dxf/none -> DXF reader, other ->
try DXF, else Unsupported(ext).
- io::write_with: dxf/none -> DXF, dwg -> DWG bridge, other with binary
-> error, rest -> write_framed.
- io::write_framed: dwt -> DWG; dxf/none/dwg -> write_with; svg, png,
pdf -> image writers; other -> Unsupported(ext).
- io::is_image: svg, png, pdf (not svgz, which is read only).
- engine file.rs is_dxf, drops_unknown (dwg, dwt), is_drawing_file
(dxf, dwg, dwt, none).
Behaviour change (help text only): the CLI help lists the formats from
Format::READ / Format::WRITE instead of hand-written lists. `info` and
`convert IN` now name svg and svgz as readable, and `convert`, `sample`,
`run --save` and `run --export` name dwt (and `sample` pdf) as writable.
A test checks each file argument's help names every format of its list.
* refactor: one version string for the app and the CLI
cadcraft_engine::build_version() builds "<version> (<commit>)" once; the
desktop app's --version, its start-up log line and cadcraft-cli --version
all use it. No change in output.
* refactor(cli): share the environment helpers with the CLI
Move blank_env_is_unset and name_env_vars from the desktop app into
cadcraft_mcp::args and apply them in the CLI parser.
Behaviour changes (old -> new):
- Empty or blank CADCRAFT_FONTALT / CADCRAFT_FONTFALLBACK: reached clap as
Some("") -> counts as unset.
- A bad value from an environment variable: the error named the flag ->
names the variable.
- CADCRAFT_CONFIG_DIR is unchanged: empty still means no settings folder.
- Desktop app: unchanged; it uses the shared helpers.
* refactor: pass paths, not text, through io, engine and open_path
cadcraft_io::{read, write, write_with, write_framed, is_image}, the engine's
drops_unknown/is_drawing_file/read_text_file and CadApp::open_path take
paths (impl AsRef<Path> / &Path). The desktop launch arguments and the CLI
no longer convert paths to text to call them.
Behaviour changes (old -> new):
- A dropped file whose name is not valid Unicode opened with a "can't
read" error naming a mangled path; it now opens natively. (Its path is
not remembered for QSAVE, which then asks where to save.)
- A launch argument that is not valid Unicode was logged and skipped; it
now opens like any other.
- A .dwt export builds the .dwg name as a path, not formatted text.
- On the web, a non-Unicode path passed to open_path reports "the path
is not valid Unicode" instead of a lossy name.
- Opening errors print path.display().
FileNames and LaunchArgs::file_names are removed; the launch tests assert
the parsed PathBufs. New io test: non-Unicode names pick the format by
extension.
* docs(cli): CADCRAFT_CONFIG_DIR is the one flag fallback where empty isn't unset
* fix(engine): refuse a drawing path that isn't valid Unicode
read_file opened such a path with no saved path, so QSAVE could not save back to it.
It now returns an open error naming the file before reading it. The ui-egui opener
already reports read_file errors like any other failed open.
* fix(cli): report a failed write of help or version
A write error on help or version output (other than a closed pipe) now exits 1 and
tries to say so on stderr; it was ignored and exited with the clap code. A closed pipe
keeps the clap exit code.
* fix(cli): --connect needs HOST:PORT
--connect example.com was accepted and failed later. It is now a usage error (exit 2)
unless the value is a non-empty host (or a bracketed IPv6 address) and a u16 port.
The host is not resolved.
* docs(cli): run prints no line for a command with no result
* refactor(cli): explicit imports and smaller functions for the stricter lints
Lints: clippy::wildcard_imports, too_many_arguments, cognitive_complexity (30).
Replaced the non-test `use super::*;` in args, commands, convert, info,
output, perf and run with explicit imports; lib.rs keeps only the imports
its non-test code uses, and its test module imports the rest itself. The
`super::*` in the test modules stays.
A clippy run with all three lints and threshold 30 reports nothing in
apps/cadcraft-cli or in code this branch added, so no function needed
splitting.
|
||
|
|
2721aa8982 |
Enforce Rust coding standards via Clippy (#2239)
* chore(clippy): enforce workspace Rust quality gates * refactor(doc): use explicit entity imports * refactor(constraints): split residual evaluation * refactor(fonts): split mtext layout * refactor(render): use explicit entity imports * fix(fonts): retain height for empty paragraphs * fix(doc): repair dimension and dynamic block links * refactor(render): split entity drawing helpers * docs: document workspace lint inheritance * refactor(render): isolate dimension geometry * refactor(engine): use explicit imports * refactor(engine): split complex edit paths into helpers * refactor(io): split DXF read and write helpers * refactor(ui): split canvas interaction and drawing * refactor(ui): split command and drawing helpers * test(i18n): split locale preference checks * fix(ui): use current egui scope id API * fix(dim): preserve text offset and ellipse grip behavior |
||
|
|
af9798e200 | refactor(doc): move entity.rs items to entity/ | ||
|
|
d1cf06cbea |
refactor: split complex fillet, model, dimstyle and rectangle code into helpers (#2188)
Pure extraction, no behaviour change: same order of side effects, same early returns and error values. Clears cognitive-complexity findings on tangent_circles, Model::build_budgeted, DimStyle::apply_fields and run_rectang2 (S3776), and replaces a redundant closure with a method reference in menus.rs (S1612). |
||
|
|
2994b52e03 |
Read dynamic block element tags from the entity's own xdata (#942) (#967)
* Read dynamic block element tags from the entity's own xdata (#942) * dynblock test: an untagged object at index 1 is keyed 1_000_001 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> |
||
|
|
a0a691e1f7 |
Reject leading '+' in hex handle and colour parsing (#2067)
Found while testing crates/doc/src/entity.rs:21 Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com> |
||
|
|
313fab8827 | Merge storytold/cadcraft#2101 | ||
|
|
1271b06b02 |
Refuse negative values for whole-number DIM* variables
DimStyle::apply_fields rounded a value for a whole-number field such as DIMDEC or DIMTMOVE and clamped it to 0..255, so DIMDEC -1 became 0 and DIMALTD "300" became 255. DIMSTYLE, DIMOVERRIDE and the SETVAR overrides that new dimensions take all accepted these values. A value below 0 or one that rounds above 255 is now refused, as an out-of-range value is. DIMADEC still takes -1. A lineweight given as -1, -2 or -3 (ByLayer, ByBlock, Default) is no longer read as 0 when the style's lineweight is positive, and a lineweight above 211 given as text is refused instead of read as 211. |
||
|
|
8a64e42100 |
DIMTFILL: dimension text gets a background in the drawing's background color or in DIMTFILLCLR
DIMTFILL and DIMTFILLCLR were not modeled, so a dimension's text never had a background and lines under it stayed visible. Dimension styles now carry both variables. DIMTFILL 1 hides what is drawn under the text in the drawing's background color, and DIMTFILL 2 fills the box in DIMTFILLCLR. The box is the text box widened by DIMGAP on every side. The arc length symbol over the text and the DIMINSPECT frame are drawn over the background. The variables work with SETVAR, DIMOVERRIDE and the style fields. DXF stores them as DIMSTYLE groups 69 and 70 from R2007 and in the style's CADCRAFT xdata in older files; the reader skips the record's flags, which also use 70. For DIMTFILL 2, the dimension's *D block and EXPLODE add a SOLID in DIMTFILLCLR under the text. A dimension drawn from the *D block it was read with gets a background around each of the block's texts, before the block's first text or first segment of the text's marks, except for a text that a SOLID of the block already covers. The background stays after the drawing is saved and reopened. |
||
|
|
63394e872d |
chore: resolve SonarCloud maintainability issues (#2025)
* chore: resolve SonarCloud maintainability issues * style: format with rustfmt * fix(engine): reference DocState instead of DrawingState * fix: restore cmd wildcard imports and remove unused Arc import * Unit tests for the extracted mleader alignment steps and spacemouse filter * test: fix assertions flagged in review and apply rustfmt Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test: initialise drift filter in struct literal for clippy Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Dylan Taylor <dylan@craftmaker.dev> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
73e7486dc9 |
DXF: proxy objects and entities keep their class through a save (#1812) (#1858)
* dxf: proxy objects and entities keep their class through a save (#1812) A proxy (ACAD_PROXY_OBJECT, ACAD_PROXY_ENTITY) names its class by number in group 91: 500 plus the class's place in the CLASSES section (DXF Reference). Saving rewrites CLASSES (CADCraft's own classes, then the kept ones), but kept proxies were written with their read number, which then named another class: an R14 file's visual styles came back as SCALE, TABLESTYLE or MLEADERSTYLE, and numbers past the last class stayed. The reader now keeps the file's class order (Foreign::class_order) when the file has proxies, and the CLASS records its proxies name. The writer renumbers each kept proxy to its class's place in the written CLASSES and drops a proxy whose number names no class (with what it owns and its dictionary entry). The DWG bridge writes through the same path. Test proxies_keep_their_class_when_classes_are_rewritten (a file with VISUALSTYLE and WIDGETX first in CLASSES, proxies of both and of a number past the last class, and a proxy entity; also through a second save). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP * docs: the proxy class fix is for DXF save (#1812) From the local CodeRabbit review: DWG save drops proxy entities (acadrust) and proxy objects through DWG weren't checked, so the progress log says DXF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP * dxf: a proxy's class number is found outside 102 groups (#1812) From the PR review: an application's 102 {NAME ... 102 } group before a proxy's own groups can hold a group 91, which was read as the class number and overwritten on save. class_group finds the first 91 outside 102 groups (before xdata), for reading and renumbering alike (test proxies_keep_their_class_when_classes_are_rewritten, now with a {MYAPP group holding a 91 that stays as it was). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP --------- Co-authored-by: Tyrel Haveman <1434399+tyrel@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
1c35d7f8c1 |
Keep the active layout when saving DWG/DXF (#1735)
* Keep the active layout when saving DWG/DXF The layout a file marks active (block *Paper_Space) was not kept on read, and on write *Paper_Space always went to the first tab, so every save made the first sheet active. Store it as Drawing::active_layout and write it as *Paper_Space again; tab order is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Match the active layout by name without regard to case paper_space_layouts and layout.rename now compare the active layout's name case-insensitively, as Drawing::layout() does, so an active layout stored as "s2" still resolves the "S2" tab. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Test case-insensitive active layout matching Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: ffishman <20211677+ffishman@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
7c72ec45a9 |
security: bound drawing document-model work and allocation (CWE-400/407) (#1850)
* security: bound drawing document-model work and allocation (CWE-400/407) Several document-model operations let crafted drawing data cause quadratic or exponential work, or multiply retained allocations: - Polyline grip generation rebuilt the whole segment vector inside the loop over every vertex, so enumerating one large polyline's grips was quadratic. Compute the segments once before the loop (row 35). - Associative-array name sync searched the whole replacement vector for every placement. Index replacements by placement coordinate once (last entry wins), turning the per-item lookup O(1) (row 44). - Dimension-extent bounds recursively expanded the referenced block without the shared bounds cache the Insert path uses, so a branching or self-referencing dimension block cost one pass per reference. Route it through the same `(name, depth)` cache (row 37). - Dynamic-block parsing parsed every graph-member occurrence into its own owned parameter, so a graph repeating a member multiplied one record into gigabytes. Deduplicate member handles before resolving them (row 7). - Stretch-action application linearly searched the vertex list and cloned the index vector for every selection occurrence. Index vertices by handle and skip handles already handled in the action's selection (row 30). New tests for polyline grips; all doc tests pass, clippy clean. Addresses findings rows 7, 30, 35, 37, 44. * fix: retain first dynamic stretch vertex selection --------- Co-authored-by: skylartaylor <26588250+skylartaylor@users.noreply.github.com> |
||
|
|
81f0aa53ff | Keep a layout's plot window corners when saving DXF (#1667) | ||
|
|
c68420221c | New metric drawings get metric Standard multileader and table styles (fixes #1171) (#1195) | ||
|
|
56d73ceb21 |
Dynamic blocks: show a distance's grip only at an end an action follows, and the grips of parameters a visibility state lists (#1090)
* Dynamic blocks: a distance's grip shows only at an end an action follows A linear parameter's grip at an end no action is attached to moves nothing, and AutoCAD doesn't show it on the reference: a stretch that follows only the end's change (EndXDelta, EndYDelta) leaves the base without a grip, and the other way round. A connection to the distance itself follows both ends, as does a distance measured from its midpoint; a parameter no action is attached to keeps the grips the block gives it. (A block saved with one grip has no base grip at all, which was already honoured.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic blocks: a visibility state that lists a parameter shows its grips AutoCAD shows a parameter's grips in a visibility state that lists the parameter, though the state doesn't list the grips themselves (a label block's Label state lists Position1 and Distance2 but not their grips, and AutoCAD shows the square and the arrow). A grip now shows when the state lists it or its parameter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
691bdbe0b8 |
Layouts in millimetres plot in millimetres whatever MEASUREMENT says (#1058) (#1085)
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> |
||
|
|
26179f7428 |
Draw object transparency on screen, in exports and in plots (#764)
* Draw object transparency on screen, in exports and in plots Transparency was read and saved (entity group 440, layer xdata, ByLayer and ByBlock) but never drawn. Display lists now carry each object's effective transparency (its own, its layer's, or the block reference's), and: - the canvas (GPU and CPU paths), ui.render and PNG/SVG exports blend it; TRANSPARENCYDISPLAY turns it off on screen and is now on by default, as in AutoCAD (initial value 1); - plots draw it only when "Plot transparency" is on: a page setup option (PAGESETUP `plotTransparency`, a checkbox in the Page Setup Manager and the Plot dialog), overridable per plot, with PLOTTRANSPARENCYOVERRIDE (0 never, 1 the setting, 2 always). PDF gets /ExtGState alpha, PNG plots blend, SVG uses opacity. Plots without it are byte-identical to before. The public DXF Reference has no plot layout flag for plot transparency, so the option is kept in the session and in JSON, not in DXF/DWG. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Transparency fixes: attributes, PDF overlaps, drag previews - Attributes draw with their own transparency (their definition's, layer 0 meaning the reference's layer, ByBlock the reference's) instead of the block reference's. - PDF plots stroke overlapping transparent objects one by one, so they build up where they cross as in PNG plots; opaque output is unchanged. - Drag and rubber-band previews follow TRANSPARENCYDISPLAY. - Effective transparency is worked out only when it is drawn, reusing the layer the property lookup already found. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Transparency: one PDF stroke per transparent primitive; opaque rebuilt previews - PDF plots never batch two transparent primitives into one path. Block and viewport contents share their reference's handle, so the per-object flush missed them; each primitive now blends on its own, as in PNG plots and on screen. Opaque output is unchanged. - Previews the session rebuilds with the current properties (rubber bands, copies of objects) are drawn opaque again: they carry CLAYER's transparency, not the objects'. The GPU drag of the objects themselves keeps following TRANSPARENCYDISPLAY. - Tests for attributes on a transparent reference layer, attributes without a definition, and the drag cache across the TRANSPARENCYDISPLAY toggle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Selection like AutoCAD: colour highlight and the Options Selection tab Selecting or hovering an object outlined each filled primitive (TrueType glyphs, dimension and leader arrowheads, solid hatches, wide polylines) by the rectangle around its triangles, and dashed everything else. A selected dimension's text became a row of dashed boxes, one per glyph, and its closed arrowheads small rectangles. Selected objects are now redrawn in the selection colour: lines solid, fills filled half see-through (so the lines and text on a highlighted hatch stay visible), and the whole object reads as selected, text and arrowheads included. A wipeout is always outlined, since filling it would hide the drawing under it. The Options dialog's Selection tab is laid out as AutoCAD's, with the settings CADCraft can act on, each one AutoCAD's variable, kept in the user profile: - Selection modes: noun/verb selection (PICKFIRST), Shift to add (PICKADD), Object grouping (PICKSTYLE bit 1, now saved in the profile). - Selection effect color (selectionColor, an interface preference; null is the theme's). - Grips: Show grips (GRIPS) and the object selection limit for display of grips (GRIPOBJLIMIT, 100; 0 no limit): grips are neither drawn nor picked beyond it. - Selection preview (SELECTIONPREVIEW): the object under the cursor is highlighted when a command is active (2) and when none is (1). - Visual Effect Settings: the selection area effect (SELECTIONAREA, WINDOWAREACOLOR 150, CROSSINGAREACOLOR 100, SELECTIONAREAOPACITY 25) and the Selection Preview Filter (PREVIEWFILTER 3: 1 objects on locked layers, 2 xrefs, 4 tables, 8 multiline text, 16 hatches, 32 groups). As in AutoCAD these are typed, not on the dialog: HIGHLIGHT (0 shows only the grips of selected objects; not saved, so every session starts highlighted), SELECTIONEFFECT (1 the selection colour, 0 dashed lines with fills outlined along their real boundary) and PREVIEWEFFECT (the preview dashed 0, thicker 1 or both 2). Left for later, as CADCraft lacks what they control: lasso, grip tips and menus, group grips, grips within blocks, associative hatch selection, command and property preview, PICKDRAG, PICKAUTO and PROPOBJLIMIT. DisplayList::fill_outlines returns the boundary of the region a filled primitive covers as closed polylines (edges only one triangle has; the slab filler's horizontal cuts, which need not line up between trapezoids, are split at every x on their line and kept where an odd number cover them), falling back to the bounding box over 600,000 vertices. The GPU and CPU highlight paths and the drawing comparison overlay share the same looks. The new labels have Ukrainian rows (the required catalog); other languages fall back to English. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: EP995LAB <293597958+EP995LAB@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
62fb5ae79a |
Attributes keep Verify, Preset and Lock position (stacked on #854) (#876)
* Multiline attributes: read, draw and save their text A multiline attribute (DXF attribute type 2, or 4 for a definition) has its text as embedded MTEXT after its own groups (`101 Embedded Object`); its own group 1 is empty. CADCraft read only the own groups, so the attribute drew nothing, and a save wrote it back empty: its text was lost. - `Attrib::multiline` keeps the multiline text; `text.value` holds its plain text, which ATTEDIT and the other value editors change; an edited value replaces the contents (`Attrib::multiline_text`). - DXF read: ATTRIB and ATTDEF split at the marker; the MTEXT parsing is shared with MTEXT entities (`mtext_from`). - Drawn as multiline text; ZOOM Extents counts it. - DXF write (2007 and later; earlier versions get one line): attribute type, alignment point and the embedded MTEXT, only what differs from unrotated text, as AutoCAD writes it. - INSERT and ATTSYNC give references the definition's multiline text placed with them; MOVE/ROTATE/SCALE, Properties rotation and scale, annotative scaling and FLATTEN carry it. - Tests: synthetic multiline ATTDEF and ATTRIB (read, drawn, saved, edited, pre-2007 save, damaged embedded groups); engine INSERT, MOVE, ATTEDIT and ATTSYNC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Multiline attributes: read MTEXT direction from its own groups (#667, as #834 does) #834 moves these lines' fix into the MTEXT reader; the same fix in the shared mtext_from keeps the two compatible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Attributes keep Verify, Preset and Lock position ATTDEF/ATTRIB flags 4 (verify) and 8 (preset) and Lock position (group 280 after the tag, DXF 2007 and later) were dropped on save. Without Lock position AutoCAD lets an attribute be dragged out of its reference, and its dynamic block actions no longer carry it. A reference's attributes take Lock position from their definitions, as AutoCAD saves them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
3199de4951 |
Multiline attributes: read, draw and save their text (they were lost on save) (#854)
* Multiline attributes: read, draw and save their text A multiline attribute (DXF attribute type 2, or 4 for a definition) has its text as embedded MTEXT after its own groups (`101 Embedded Object`); its own group 1 is empty. CADCraft read only the own groups, so the attribute drew nothing, and a save wrote it back empty: its text was lost. - `Attrib::multiline` keeps the multiline text; `text.value` holds its plain text, which ATTEDIT and the other value editors change; an edited value replaces the contents (`Attrib::multiline_text`). - DXF read: ATTRIB and ATTDEF split at the marker; the MTEXT parsing is shared with MTEXT entities (`mtext_from`). - Drawn as multiline text; ZOOM Extents counts it. - DXF write (2007 and later; earlier versions get one line): attribute type, alignment point and the embedded MTEXT, only what differs from unrotated text, as AutoCAD writes it. - INSERT and ATTSYNC give references the definition's multiline text placed with them; MOVE/ROTATE/SCALE, Properties rotation and scale, annotative scaling and FLATTEN carry it. - Tests: synthetic multiline ATTDEF and ATTRIB (read, drawn, saved, edited, pre-2007 save, damaged embedded groups); engine INSERT, MOVE, ATTEDIT and ATTSYNC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Multiline attributes: read MTEXT direction from its own groups (#667, as #834 does) #834 moves these lines' fix into the MTEXT reader; the same fix in the shared mtext_from keeps the two compatible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
48352f2baf |
Dynamic block grips: show and drag a dynamic reference's grips (stacked on #878) (#883)
* Dynamic blocks: read parameters and actions from AutoCAD files A dynamic block's evaluation graph (kept as foreign data since #528) is read into a typed model, `cadcraft_doc::dynblock`, without changing the kept data: parameters (linear with value sets, point, visibility states, lookup, base point; polar, XY, rotation, alignment and flip by geometry) and actions (stretch with its frame and moved vertices, lookup tables; move, scale, rotate, flip, array, polar stretch by kind), each with its graph id and the parameters it follows. - `blocks.dynamic {name}` returns the model as JSON (handles as hex); `blocks.list` marks dynamic blocks. - Counts and values come from the file: capped by the groups present, non-finite numbers and missing members skipped. - Tests: a synthetic DOOR block (visibility, linear list, stretch, lookup), its survival through a save, damaged data, and the engine commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: their values and custom properties A dynamic block reference's extension dictionary says which dynamic block it shows (AcDbBlockRepresentation ▸ AcDbRepData) and the values it was given (AppDataCache ▸ ACAD_ENHANCEDBLOCKDATA, one XRECORD per parameter graph id). `dynblock::reference` reads them; a reference to the dynamic block itself has the block's values. - `dynblock::custom_properties`: the Properties palette's Custom section, in parameter order, only parameters shown in Properties: visibility state, lookup value worked out from the lookup tables (their "no match" name, e.g. Custom, when the inputs match no row), distances and point positions in drawing units (block units × the reference's scale), with the choices of each (states, lookup values, a distance's list). - Properties: a dynamic block reference shows its dynamic block's name instead of its anonymous block's, and a read-only Custom section. - `blocks.reference {handle}` returns the definition, values and properties. - Tests: synthetic references (own values, an anonymous block scaled 2, a width no lookup row has), survival through a save, the engine command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: change the visibility state Choosing a visibility state (or a lookup value whose tables set only visibility states) in Properties ▸ Custom, or `blocks.reference.set {handle, name, value}`, changes a dynamic block reference as AutoCAD does: - The reference gets a new anonymous block: its current one with the state's objects shown and the parameter's other objects hidden (matched to the dynamic block's objects by their AcDbBlockRepETag element tags). The old anonymous block goes when nothing else uses it. - AutoCAD's link data is written: the new block's record carries AcDbBlockRepBTag (the dynamic block's record), copied objects keep their element tags, and the reference's AcDbBlockRepresentation data records the value (an existing value record is updated; a new one copies another reference's record for the same parameter when there is one). - An attribute whose definition the state hides is written hidden (60). - Changes CADCraft can't make yet (distances, positions, lookups that set them) are refused with a message and change nothing; Properties shows only what can change as a dropdown (`CustomProperty::editable`). - Tests: the synthetic DOOR references (own values and an anonymous block), survival through a save, refusals, and the engine command with undo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: distances, positions, stretch and move actions, lookup chaining Linear distances (snapped to their value sets) and point positions can be changed; the stretch and move actions that follow them run (listed vertices or whole objects, multiplier and angle offset), parameters in an action's selection are carried along, associative hatches refill from their stretched outlines and the reference's attributes move with their definitions. Lookup tables run both ways (a changed input gives its table's lookup value, which sets the other tables' inputs). Connections are read in either group order. Checked against AutoCAD's own references (*U10, *U12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: record changes in the history as AutoCAD does; full value data for new references A changed reference's ACAD_ENHANCEDBLOCKHISTORY says what was done to it: a visibility state or lookup value by property name, a distance or position as the drag of its grips (GRIPLOC, how far each grip is from where the dynamic block has it). A reference that had no data gets a record for everything other references of the same dynamic block have, with its own values. Templates come only from references of the same dynamic block; anonymous copies have no units; boundary reactors follow the copies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block history: record only the grips a change moves AutoCAD's history holds the user's own drags; a grip that only moved with another parameter (a stretch carrying it) gets no entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block history: a distance by name, as typed in Properties AutoCAD records a distance typed in Properties as the parameter's name and value in block units (1071 id, 300 Distance2, 40 40.0), replacing drags of its grips; positions stay grip drags. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DWG: dynamic block lookup parameters keep their name and lookup action AutoCAD writes a lookup parameter's subclass marker as AcDbBlockLookUpParameter; the DWG bridge reads AcDbBlockLookupParameter and matches it exactly, so the parameter's name (303) and lookup action (94) were lost in DWG files and AutoCAD's lookups stopped working (a visibility change no longer resized the block). The bridge DXF now uses its spelling. Found by driving AutoCAD on CADCraft's output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic blocks: carried parameter points are in a stretch frame where the block has them AutoCAD evaluates actions from the dynamic block itself, so a parameter point another action has moved out of a stretch frame still follows it. Testing the current position instead changed a stretched label's width when its visibility changed (Distance2 183.75 became 184.45; AutoCAD keeps 183.75). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block grips: show and drag a dynamic reference's grips as AutoCAD does A selected dynamic block reference shows its grips: an arrow at each end of a distance, a square at a position, a down-pointing triangle for a visibility or lookup list, only those its visibility state lists. Dragging an arrow changes the distance along the parameter (snapped to its values; a typed number sets it), a square moves the position, a list grip offers its choices; actions and lookups follow, with a live preview. doc: dynblock::grips, drag_grip; grip objects by graph id. engine: blocks.reference.grips, blocks.reference.grip, Session::start_dyn_grip. ui: canvas dynamic grips (GRIPDYNCOLOR-like token), list popup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: read the shared DOOR fixture with LF line ends whatever the checkout gives Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic blocks: carried parameter points are in a stretch frame where the block has them AutoCAD evaluates actions from the dynamic block itself, so a parameter point another action has moved out of a stretch frame still follows it. Testing the current position instead changed a stretched label's width when its visibility changed (Distance2 183.75 became 184.45; AutoCAD keeps 183.75). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic grips: Ukrainian labels for the new commands; copy the hot grip instead of cloning Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
0d138c2954 |
Dynamic block references: distances, positions, stretch and move actions, lookups both ways (stacked on #860) (#878)
* Dynamic blocks: read parameters and actions from AutoCAD files A dynamic block's evaluation graph (kept as foreign data since #528) is read into a typed model, `cadcraft_doc::dynblock`, without changing the kept data: parameters (linear with value sets, point, visibility states, lookup, base point; polar, XY, rotation, alignment and flip by geometry) and actions (stretch with its frame and moved vertices, lookup tables; move, scale, rotate, flip, array, polar stretch by kind), each with its graph id and the parameters it follows. - `blocks.dynamic {name}` returns the model as JSON (handles as hex); `blocks.list` marks dynamic blocks. - Counts and values come from the file: capped by the groups present, non-finite numbers and missing members skipped. - Tests: a synthetic DOOR block (visibility, linear list, stretch, lookup), its survival through a save, damaged data, and the engine commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: their values and custom properties A dynamic block reference's extension dictionary says which dynamic block it shows (AcDbBlockRepresentation ▸ AcDbRepData) and the values it was given (AppDataCache ▸ ACAD_ENHANCEDBLOCKDATA, one XRECORD per parameter graph id). `dynblock::reference` reads them; a reference to the dynamic block itself has the block's values. - `dynblock::custom_properties`: the Properties palette's Custom section, in parameter order, only parameters shown in Properties: visibility state, lookup value worked out from the lookup tables (their "no match" name, e.g. Custom, when the inputs match no row), distances and point positions in drawing units (block units × the reference's scale), with the choices of each (states, lookup values, a distance's list). - Properties: a dynamic block reference shows its dynamic block's name instead of its anonymous block's, and a read-only Custom section. - `blocks.reference {handle}` returns the definition, values and properties. - Tests: synthetic references (own values, an anonymous block scaled 2, a width no lookup row has), survival through a save, the engine command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: change the visibility state Choosing a visibility state (or a lookup value whose tables set only visibility states) in Properties ▸ Custom, or `blocks.reference.set {handle, name, value}`, changes a dynamic block reference as AutoCAD does: - The reference gets a new anonymous block: its current one with the state's objects shown and the parameter's other objects hidden (matched to the dynamic block's objects by their AcDbBlockRepETag element tags). The old anonymous block goes when nothing else uses it. - AutoCAD's link data is written: the new block's record carries AcDbBlockRepBTag (the dynamic block's record), copied objects keep their element tags, and the reference's AcDbBlockRepresentation data records the value (an existing value record is updated; a new one copies another reference's record for the same parameter when there is one). - An attribute whose definition the state hides is written hidden (60). - Changes CADCraft can't make yet (distances, positions, lookups that set them) are refused with a message and change nothing; Properties shows only what can change as a dropdown (`CustomProperty::editable`). - Tests: the synthetic DOOR references (own values and an anonymous block), survival through a save, refusals, and the engine command with undo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: distances, positions, stretch and move actions, lookup chaining Linear distances (snapped to their value sets) and point positions can be changed; the stretch and move actions that follow them run (listed vertices or whole objects, multiplier and angle offset), parameters in an action's selection are carried along, associative hatches refill from their stretched outlines and the reference's attributes move with their definitions. Lookup tables run both ways (a changed input gives its table's lookup value, which sets the other tables' inputs). Connections are read in either group order. Checked against AutoCAD's own references (*U10, *U12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: record changes in the history as AutoCAD does; full value data for new references A changed reference's ACAD_ENHANCEDBLOCKHISTORY says what was done to it: a visibility state or lookup value by property name, a distance or position as the drag of its grips (GRIPLOC, how far each grip is from where the dynamic block has it). A reference that had no data gets a record for everything other references of the same dynamic block have, with its own values. Templates come only from references of the same dynamic block; anonymous copies have no units; boundary reactors follow the copies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block history: record only the grips a change moves AutoCAD's history holds the user's own drags; a grip that only moved with another parameter (a stretch carrying it) gets no entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block history: a distance by name, as typed in Properties AutoCAD records a distance typed in Properties as the parameter's name and value in block units (1071 id, 300 Distance2, 40 40.0), replacing drags of its grips; positions stay grip drags. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DWG: dynamic block lookup parameters keep their name and lookup action AutoCAD writes a lookup parameter's subclass marker as AcDbBlockLookUpParameter; the DWG bridge reads AcDbBlockLookupParameter and matches it exactly, so the parameter's name (303) and lookup action (94) were lost in DWG files and AutoCAD's lookups stopped working (a visibility change no longer resized the block). The bridge DXF now uses its spelling. Found by driving AutoCAD on CADCraft's output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic blocks: carried parameter points are in a stretch frame where the block has them AutoCAD evaluates actions from the dynamic block itself, so a parameter point another action has moved out of a stretch frame still follows it. Testing the current position instead changed a stretched label's width when its visibility changed (Distance2 183.75 became 184.45; AutoCAD keeps 183.75). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
f8521ab299 |
Dynamic block references: change the visibility state (stacked on #853) (#860)
* Dynamic blocks: read parameters and actions from AutoCAD files A dynamic block's evaluation graph (kept as foreign data since #528) is read into a typed model, `cadcraft_doc::dynblock`, without changing the kept data: parameters (linear with value sets, point, visibility states, lookup, base point; polar, XY, rotation, alignment and flip by geometry) and actions (stretch with its frame and moved vertices, lookup tables; move, scale, rotate, flip, array, polar stretch by kind), each with its graph id and the parameters it follows. - `blocks.dynamic {name}` returns the model as JSON (handles as hex); `blocks.list` marks dynamic blocks. - Counts and values come from the file: capped by the groups present, non-finite numbers and missing members skipped. - Tests: a synthetic DOOR block (visibility, linear list, stretch, lookup), its survival through a save, damaged data, and the engine commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: their values and custom properties A dynamic block reference's extension dictionary says which dynamic block it shows (AcDbBlockRepresentation ▸ AcDbRepData) and the values it was given (AppDataCache ▸ ACAD_ENHANCEDBLOCKDATA, one XRECORD per parameter graph id). `dynblock::reference` reads them; a reference to the dynamic block itself has the block's values. - `dynblock::custom_properties`: the Properties palette's Custom section, in parameter order, only parameters shown in Properties: visibility state, lookup value worked out from the lookup tables (their "no match" name, e.g. Custom, when the inputs match no row), distances and point positions in drawing units (block units × the reference's scale), with the choices of each (states, lookup values, a distance's list). - Properties: a dynamic block reference shows its dynamic block's name instead of its anonymous block's, and a read-only Custom section. - `blocks.reference {handle}` returns the definition, values and properties. - Tests: synthetic references (own values, an anonymous block scaled 2, a width no lookup row has), survival through a save, the engine command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: change the visibility state Choosing a visibility state (or a lookup value whose tables set only visibility states) in Properties ▸ Custom, or `blocks.reference.set {handle, name, value}`, changes a dynamic block reference as AutoCAD does: - The reference gets a new anonymous block: its current one with the state's objects shown and the parameter's other objects hidden (matched to the dynamic block's objects by their AcDbBlockRepETag element tags). The old anonymous block goes when nothing else uses it. - AutoCAD's link data is written: the new block's record carries AcDbBlockRepBTag (the dynamic block's record), copied objects keep their element tags, and the reference's AcDbBlockRepresentation data records the value (an existing value record is updated; a new one copies another reference's record for the same parameter when there is one). - An attribute whose definition the state hides is written hidden (60). - Changes CADCraft can't make yet (distances, positions, lookups that set them) are refused with a message and change nothing; Properties shows only what can change as a dropdown (`CustomProperty::editable`). - Tests: the synthetic DOOR references (own values and an anonymous block), survival through a save, refusals, and the engine command with undo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DWG: dynamic block lookup parameters keep their name and lookup action AutoCAD writes a lookup parameter's subclass marker as AcDbBlockLookUpParameter; the DWG bridge reads AcDbBlockLookupParameter and matches it exactly, so the parameter's name (303) and lookup action (94) were lost in DWG files and AutoCAD's lookups stopped working (a visibility change no longer resized the block). The bridge DXF now uses its spelling. Found by driving AutoCAD on CADCraft's output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
73b8edac61 |
Dynamic block references: custom properties in Properties (stacked on #838) (#853)
* Dynamic blocks: read parameters and actions from AutoCAD files A dynamic block's evaluation graph (kept as foreign data since #528) is read into a typed model, `cadcraft_doc::dynblock`, without changing the kept data: parameters (linear with value sets, point, visibility states, lookup, base point; polar, XY, rotation, alignment and flip by geometry) and actions (stretch with its frame and moved vertices, lookup tables; move, scale, rotate, flip, array, polar stretch by kind), each with its graph id and the parameters it follows. - `blocks.dynamic {name}` returns the model as JSON (handles as hex); `blocks.list` marks dynamic blocks. - Counts and values come from the file: capped by the groups present, non-finite numbers and missing members skipped. - Tests: a synthetic DOOR block (visibility, linear list, stretch, lookup), its survival through a save, damaged data, and the engine commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic block references: their values and custom properties A dynamic block reference's extension dictionary says which dynamic block it shows (AcDbBlockRepresentation ▸ AcDbRepData) and the values it was given (AppDataCache ▸ ACAD_ENHANCEDBLOCKDATA, one XRECORD per parameter graph id). `dynblock::reference` reads them; a reference to the dynamic block itself has the block's values. - `dynblock::custom_properties`: the Properties palette's Custom section, in parameter order, only parameters shown in Properties: visibility state, lookup value worked out from the lookup tables (their "no match" name, e.g. Custom, when the inputs match no row), distances and point positions in drawing units (block units × the reference's scale), with the choices of each (states, lookup values, a distance's list). - Properties: a dynamic block reference shows its dynamic block's name instead of its anonymous block's, and a read-only Custom section. - `blocks.reference {handle}` returns the definition, values and properties. - Tests: synthetic references (own values, an anonymous block scaled 2, a width no lookup row has), survival through a save, the engine command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DWG: dynamic block lookup parameters keep their name and lookup action AutoCAD writes a lookup parameter's subclass marker as AcDbBlockLookUpParameter; the DWG bridge reads AcDbBlockLookupParameter and matches it exactly, so the parameter's name (303) and lookup action (94) were lost in DWG files and AutoCAD's lookups stopped working (a visibility change no longer resized the block). The bridge DXF now uses its spelling. Found by driving AutoCAD on CADCraft's output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
fbc7037a10 |
Dynamic blocks: read parameters and actions from AutoCAD files (#838)
* Dynamic blocks: read parameters and actions from AutoCAD files A dynamic block's evaluation graph (kept as foreign data since #528) is read into a typed model, `cadcraft_doc::dynblock`, without changing the kept data: parameters (linear with value sets, point, visibility states, lookup, base point; polar, XY, rotation, alignment and flip by geometry) and actions (stretch with its frame and moved vertices, lookup tables; move, scale, rotate, flip, array, polar stretch by kind), each with its graph id and the parameters it follows. - `blocks.dynamic {name}` returns the model as JSON (handles as hex); `blocks.list` marks dynamic blocks. - Counts and values come from the file: capped by the groups present, non-finite numbers and missing members skipped. - Tests: a synthetic DOOR block (visibility, linear list, stretch, lookup), its survival through a save, damaged data, and the engine commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * DWG: dynamic block lookup parameters keep their name and lookup action AutoCAD writes a lookup parameter's subclass marker as AcDbBlockLookUpParameter; the DWG bridge reads AcDbBlockLookupParameter and matches it exactly, so the parameter's name (303) and lookup action (94) were lost in DWG files and AutoCAD's lookups stopped working (a visibility change no longer resized the block). The bridge DXF now uses its spelling. Found by driving AutoCAD on CADCraft's output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dynamic blocks: a work budget on the record cursor, so counts promising missing groups can't make nested loops rescan a hostile record (with regression test) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cody Vogt <18035288+codyvogt@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
dc92f38772 |
DXF: count the records the reader can't place; info reports them, convert warns (part of #865) (#872)
Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1c4d0a543f |
Open damaged DWGs partially and say what was left out (#800) (#859)
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> |
||
|
|
2f5bbbd68d |
doc: Space::tab_name / layout / is_paper replace repeated hand-written matches (#888)
* 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
966356545d |
ACAD_TABLE from other writers: keep style text height, read cell text height, lay out cell text as MTEXT (#802) (#806)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
63b448bee9 |
Extents: count the visible attributes of a reference to a missing block (fixes #707) (#746)
Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
79efd97515 |
MTEXT: annotative scaling and SCALE on multileader text scale the text width and columns too (fixes #708, fixes #709) (#743)
Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
724c18c6af |
Constraint point names: s2 names segment 2, as documented (fixes #710) (#734)
Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d134079f11 |
Xdata travels with its object: COPY, MIRROR, ARRAY and paste keep it (#890)
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 <noreply@anthropic.com> |
||
|
|
9464945f82 |
REFEDIT, REFSET and REFCLOSE: edit block references in place (#818)
* REFEDIT, REFSET and REFCLOSE: edit block references in place (#813) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Move REFEDIT translation lines into the middle of the catalogs (avoids PR merge conflicts) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
e9d8462828 |
Keep the ACIS geometry of 3D solids, regions and bodies on save (ACDSDATA) (#781)
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> |
||
|
|
565cadaa51 | Extents: cache block bounds per depth so a deep visit can't hide a shallow reference (fixes #706) (#778) | ||
|
|
6ca8cec97d |
BRICK: draw each vertical joint once (#664)
Squash of PR #664 by @aldozar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
135c928fea |
Hatches: draw the pattern definition stored in the file; scale library patterns in metric drawings (#643)
Squash of PR #643 by @aldozar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f967dc7aca |
Persist SNAPUNIT and GRIDUNIT in DXF (fix #607) (#614)
Squash of PR #614 by @PedroGuille. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
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 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
9fd1b8988d |
Support files: user .lin linetypes and .pat hatch patterns (part of #552) (#557)
Squash of PR #557 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
58eefa8432 |
Constraints: DOF report, G2 Smooth, ellipse/spline/block geometry (part of #546) (#550)
Squash of PR #550 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9ca0dcf7d4 |
Tables: table breaking and CSV data links (fixes #534) (#553)
Squash of PR #553 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
784a7937ef |
Tables: cell styles, formulas, TABLEDIT and TABLEEXPORT (part of #534) (#548)
Squash of PR #548 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8005201db9 |
feat(text): MTEXT paragraph formatting (indents, tabs, lists) and columns (#547)
Squash of PR #547 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a4504cc098 |
DXF: keep what we don't model on save — unknown entities and objects, foreign xdata, extension dictionaries, reactors, colour books (fixes #528) (#545)
Squash of PR #545 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
afae36cb60 |
REGION: region entity, UNION/SUBTRACT/INTERSECT, MASSPROP (fixes #510) (#526)
Squash of PR #526 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0230250bf5 |
Xrefs: XATTACH, -XREF, XBIND, XOPEN; load at open, save only the reference (part of #525) (#530)
Squash of PR #530 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c334fedf31 |
Dimension edits: DIMEDIT Oblique/New/Rotate/Home, DIMJOGGED, DIMJOGLINE, DIMBREAK, DIMINSPECT (part of #507) (#522)
Squash of PR #522 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |