Make objects genuinely annotative — carrying real per-scale
representations that interoperate with other CAD apps — rather than just
a native flag. `create_annotation_context` synthesizes the extension-dict
chain (AcDbContextDataManager -> ACDB_ANNOTATIONSCALES -> per-scale leaf)
from the entity's placement; the editable Annotative toggle now covers
Text and block references as well as MText/MLeader; and a new
"Annotation Object Scale" dialog (OBJECTSCALE) adds/removes an object's
per-scale memberships. Requires the acadrust ObjectContextData encoder
(Cargo.lock bump).
Also fixes a render bug where objects carrying an *empty*
ACDB_ANNOTATIONSCALES (a single-representation marker with no per-scale
reps) were treated as annotative and (mis)scaled by the annotation factor
in scaled paper viewports, ballooning the text: an object is now
annotative-by-context only when its scale collection is non-empty, so
such objects render at their base geometry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes#286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TEXTFILL 1 (default) fills TrueType text; TEXTFILL 0 draws it hollow
(outline only). Settable directly or via SETVAR, 0/1 or ON/OFF. SHX/LFF
stroke fonts are unaffected.
Reuses the existing stroke-SDF path: bake_glyph treats a TTF glyph as a
stroke (outline) field instead of a signed fill field when TEXTFILL is
off. Toggling resets the glyph atlas and re-tessellates text so quads
pick up the re-baked tiles. Persisted in user settings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run EXTRIM, pick a cutting boundary (line/arc/circle/ellipse/spline/
polyline), then click a side: every object crossing the boundary is
trimmed on that side and objects wholly on it are erased. Side is decided
by a parity crossing count (a segment to the pick point crossing the
boundary an even number of times is on the pick side), reusing the trim
intersection machinery; line boundaries extend to infinity. Line/arc/
circle are trimmed exactly; polyline/ellipse/spline via dense sampling.
Live preview while choosing the side: boundary in yellow, surviving parts
in blue, the removed parts in red.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run QUICKPRINT (or QP), select objects, then Enter: the selection's
bounding box becomes the plot window and a PDF is written next to the
drawing (<name>_<timestamp>.pdf) using the active page setup, with no
dialog. Model space.
Implemented as a selection-gathering CadCommand (src/modules/view/
quick_print.rs) returning CmdResult::QuickPrint(handles); the host
(on_quick_print_handles) unions the picked entities' AABBs, sets the
plot window, and reuses the tested clipped window-export path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Autocomplete suggestions came only from all_registered_command_names() —
the compile-time inventory registry returning &'static str — so
runtime-loaded plugin commands could never appear. Typing a plugin's
command prefix (e.g. `LS_`) showed nothing even with the plugin loaded
and its ribbon tab active; dispatch worked, only discovery was blind.
Collect each enabled plugin's ribbon ToolDef command ids plus its
manifest command_prefixes into a dynamic candidate pool
(OwnedRibbonGroup::command_ids → PluginManager::command_names →
plugin_command_names), refreshed in rebuild_ribbon_modules on startup
load, settings reload, and every enable/disable toggle. ranked_matches
now merges this pool with the static registry and returns owned strings.
A plugin.toml `commands = [...]` list for sub-verbs with no ribbon button
(e.g. LS_AUTOLABEL) stays a follow-up — it's a plugin-API surface change.
Closes#272
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two follow-ups to ADDSELECTED:
- It no longer permanently changes the current layer/colour/linetype/
lineweight. The template's properties are captured as the drawing
defaults only while the launched draw command runs; a snapshot taken in
cmd_add_selected is restored the moment that command ends (central hook
in apply_cmd_result) or is interrupted by another command.
- Dimensions are supported: add_selected_verb maps each DimensionType to
its DIM* command, and the template's dimension style is adopted as the
current DIMSTYLE (restored afterwards). commit_entity_handle now stamps
every new dimension with the current DIMSTYLE — the DIM commands left the
default "Standard" on the entity, so a normal dimension also picks up
the active style now.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Take the first selected entity as a template, adopt its general
properties (layer, colour, linetype, lineweight, linetype scale) as the
current defaults, then start the matching draw command so the object
drawn next inherits them.
Supports Point, Line, Circle, Arc, Ellipse, all polylines (PLINE), Text,
MText, Spline, Hatch, Solid, Ray and XLine; other types report that
creation is unsupported. Registered for command-line autocomplete.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Layer color/linetype/lineweight and MATCHPROP edits set the
document dirty flag but never bumped geometry_epoch. The wire
caches key on that epoch and bake a ByLayer entity's resolved
color at tessellation time, so they returned stale wires until
a new entity forced a rebuild. Bump geometry on those four
handlers so the edit repaints immediately.
Also remove REGEN/REGENALL/REDRAW/REDRWALL: the GPU raster
pipeline keeps the display continuously in sync, so on-demand
regen/redraw is a no-op concept in OCS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- FLATSHOT: project the selected solid's edges to 2D Line entities at Z=0
- CONVTOSURFACE: convert the selected solid(s) to Surface entities
Both reuse the cached B-rep's edge wires. COMMANDS.md: 219/262.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Package the drawing plus its referenced files (xrefs + raster images)
into a sibling "<name>_archive" folder via std::fs, reusing the
existing block_records / entities iterators. COMMANDS.md: 217/262.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convert selected closed polylines and circles into Region entities,
one wire loop each (reuses acadrust Region/Wire). COMMANDS.md: 215/262.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Six more standard commands by reusing existing infrastructure:
- CLOSE: close the active tab (Message::TabClose)
- HIDE / VISUALSTYLES: per-viewport render mode (Message::SetRenderMode)
- QUICKCALC / QC: alias the CAL arithmetic evaluator
- HYPERLINK <url>: attach a hyperlink via the PE_URL XData record
- XOPEN: open a selected xref's source file (Message::OpenRecent)
COMMANDS.md updated to 214 done / 15 partial / 33 missing of 262.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).
New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
(DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
FINDNONPURGEABLE
Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
(correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count
Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the opt-in `rmb_enter` setting (and `RMBENTER` command) with
always-on behavior. While a command is active, the first viewport
right-click acts as Enter (commit / close) and a second consecutive
right-click opens the context menu instead. Idle right-click still opens
the menu; right-drag still orbits.
The cycle is tracked per tab (`right_click_entered`) and restarts on any
other interaction — a left-click pick, a new command, an orbit, or
leaving the viewport — so the next right-click is Enter again.
Refines #185.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a configurable right-mouse-button behavior without disturbing orbit
or the context menu.
- New persisted setting `rmb_enter` (command `RMBENTER [ON|OFF]`, default
off): when on, a right-click while a command is active acts as Enter
(commit/close); when idle it still opens the context menu. Right-drag
always orbits, so nothing is lost.
- Right-click vs orbit is now time-based: orbit engages only after the
button is held >=150ms and the pointer moves, so a quick right-click is
no longer swallowed by slight pointer jitter (was an 8px distance gate).
- The right-click Repeat menu now lists commands from every source
(command line, ribbon, context menu, shortcuts) via a unified
recents list recorded at the dispatch choke point, instead of only
typed commands.
Closes#185
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Typing a partial command (e.g. `bac`) and pressing Enter with no active
command ran into an "Unknown command" error. Now the top autocomplete
match is highlighted and Enter runs it (`BACKGROUND`), matching standard
DWG command-line behavior.
The fallback only fires when no command family matches the typed verb, so
complete aliases that resolve through a dispatch arm but are absent from
the autocomplete registry (`LT`, `ZO`, …) still run as typed. Programmatic
dispatch (ribbon, plugins, headless automation) is unaffected.
Closes#198
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>