- #2587: a frame turned by anything but a quarter turn keeps its size about its turned
centre; the box of its two mapped corners collapsed it (a 10 x 10 square turned 45° became
14.14 x 0, and the OLE header with it) for EXPLODE, REFEDIT, paste and array placement
(austin62jake, CodeRabbit). Moves, scales, mirrors and quarter turns keep the exact
corner mapping. `recase` reads only the rewritten digits and a few after them, not the
whole 310 line (austin62jake). The PR's rotated-frame test expects the kept size.
- #2585: an `output` that contradicts the path's extension is refused, as a device that
does is; it lost silently to the extension.
- #2608: the window's state is dropped when another dialog took its place (it missed a UI
pass), so reopening reads the drawing instead of an old unsent edit (CodeRabbit); TEXT and
ATTDEF get a one-line field (Enter commits), so a line break can't turn into a space on
save; the module comment says the shortcut menu opens it.
The folders are made by a new writer only user-named targets use, so autosave
and a dangling symlink's target keep the strict write_atomic. Save, Save As,
Export, Plot, WBLOCK, layout templates, render and the CLI report the topmost
new folder at the command line and in createdFolder.
* plot and exportpdf help name the `device` parameter (#2557)
`cadcraft-cli commands plot` didn't list `device`, so the only way to find it was the `unknown plotter` error.
PLOT's help now lists `device` with the built-in devices (CADCraft PDF, PNG, SVG), PLOTTERMANAGER plotters and "None". It also says the default is the page setup's device and that the device picks the output format and the papers. EXPORTPDF's help names `device` too, and notes that its output stays PDF. A test checks that both help texts name `device` and that PLOT's names every built-in device.
* plot help: say what Model, "None" and an omitted paper do
In model space, and with "None", no device is used and the plot is a PDF. When `paper` is omitted, a layout keeps its page setup's paper and model space takes the device's default.
device None and a null or unknown output no longer bypass the refusal
and write a PDF under the odd name; only a recognised output or a
device that resolves does. Test: plot_path_extension_picks_the_format_like_export.
layout picked the rendered space while layouts filled the reply, so a
request could render Model and report Layout1. Both selectors must now
agree, for every output. Test: plot_goes_through_the_hook.
A path like x.txt (or one with no extension) wrote a PDF under that name. Like EXPORT, PLOT now refuses it unless output or a device says what to write. Test: plot_path_extension_picks_the_format_like_export.
run_plot rendered the session space for image output while the reply named the layout. The one-entry list now resolves to the render space, as the PDF path does. Test: plot_goes_through_the_hook.
LAYOUT SAveas printed "LAYOUT SAveas is not available yet." It now asks
for the layout (the current one by default) and the template file
(<layout>.dwt beside the drawing by default), and writes the layout as a
template: its page setup and paper space objects, viewports included, no
model space objects and no other layout. Named objects nothing in it uses
are left out, as PURGE All removes them. Layers the layout's viewports
freeze or recolor are kept, with those settings. The open drawing is not
changed.
The JSON form is layout.saveas {name?, path}, also reached as LAYOUT
{option: saveas}. A path without an extension gets .dwt. The command
also writes .dwg and .dxf paths and refuses other file types.
* refactor: replace wildcard imports with explicit ones (S2208, batch_05)
* refactor: replace wildcard imports with explicit ones (S2208, batch_05)
* Format imports so the CI fmt gate passes
With ORTHOMODE on, the opposite corner of STRETCH's crossing window was bent
onto an axis, so the window collapsed and caught nothing. The prompt set only
the rubber-band base; it now marks it as a window corner (Prompt::corner), as
ZOOM Window and RECTANG already do.
Same fix for the other two-corner rectangles: the Window/Crossing/Box selection
modes, PLOTWINDOW, MVIEW (and its Restore), -VIEW Window and REVCLOUD
Rectangular. Regression tests in crates/engine/tests/stretch_window_ortho.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A model space view carries a twist (radians CCW, View.twist, serde default
0). VIEWTWIST reports it read-only. Each VPORTS tile keeps its own twist
(VportTile.twist, DXF VPORT group 51 read and written; a single untiled
Model view's twist is kept as a lone *Active VPORT tile). VIEW Save stores
and Restore applies NamedView::twist. ZOOM fits in the twisted screen frame;
zooms, pans, the 3D mouse and view.set keep it; ZOOM Previous turns it back.
DVIEW starts with "Select objects or <use DVIEWBLOCK>:" (the picked objects,
or the DVIEWBLOCK sample, are the only things drawn while it runs) and
repeats the "Enter option [CAmera/TArget/Distance/POints/PAn/Zoom/
TWist/CLip/Hide/Off/Undo]:" prompt. TWist, PAn, Zoom, Off and Undo work on
2D views; the 3D options say they are not available yet (docs/gaps.md F1).
DVIEW works inside an active layout viewport: the doc Viewport gains a view
twist (DXF VIEWPORT group 51), drawn turned on screen and in plots, mapped
through MSPACE views, snaps and CHSPACE; a twist made there is one undo
step. A layout with no active viewport refuses it, as does view.set's twist.
Canvas: the model view and every VPORTS tile draw turned by their twist.
`Xf` gains `twist`: `to_screen` turns (p - centre) by it before scaling,
`to_world` is its exact inverse, `world_bounds` is the box of the four
turned rect corners. The GPU needs no shader change: `Xf::gpu_xform` builds
the callback xform for the drawing, drag, highlight, viewport and mask
layers. Pans map screen deltas back through the twist; clicks, hover, snaps,
grips and wheel zoom go through `to_world`/`to_screen`. Grid lines and axes
stay world-aligned; the selection window is drawn as the world box it
selects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 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>
Layers and objects get a PlotStyle property (ByLayer/ByBlock/name) applied
through named tables (built-in default.stb and monochrome.stb, our own
definitions). DXF keeps the names in the ACAD_PLOTSTYLENAME dictionary of
ACDBPLACEHOLDER objects with group 390 on layers and objects; tables kept in
the drawing travel in a CADCRAFT_PLOTSTYLES XRECORD. PLOTSTYLE sets an object's
or the current (CPLOTSTYLE) style; STYLESMANAGER lists, creates, edits, deletes,
loads and saves tables in our own JSON format (docs/plot-styles.md). A Plot
Style Manager dialog edits a table's styles and the layout's table; the
Properties palette shows the current plot style and table.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Typed VPLAYER ran its JSON form with no parameters and ended, so its
options ran as commands (F started FILLET). It now loops on
?/Color/Freeze/Thaw/Reset/Newfrz/Vpvisdflt until Enter, asks for layer
names (wildcards, comma lists) and then which viewports to change:
All/Select/Current/Except current (Current by default inside a viewport,
Select by point on the sheet in paper space). Changes go through
viewport.set, layer.new and layer.set; the session is one undo step.
The Model tab refuses it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Typed LAYOUT ran its JSON default and ended, so the option and the
layout names typed after it ran as commands. It now asks for an option
(Copy/Delete/New/Template/Rename/SAveas/Set/?, Enter = Set) and the
layout name(s) the option needs, with the current layout as the default.
The options run the layout.* command bodies, so a refused name re-prompts
and the command is one undo step. Template and SAveas report that they
are not available yet and end.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>