Resolve conflicts from main's rustfmt pass by taking main's versions of
command.rs, style.rs, view/mod.rs, scene/mod.rs, ui/window/mod.rs and
ui/properties.rs and re-applying this branch's Graphic Attributes changes.
Add the Graphic Attributes strings to the new lo-LA catalog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1QnME6WJbD5iGFae1eeCb
Conflicts were additions on both sides, kept together: the Count palette
and Sheet Set toggles next to the Graphic Attributes toggle in the ribbon,
main's new locale groups and catalog entries next to graphic-attributes,
and module declarations. The palette code follows main's crate renames
(acadrust -> codec, cadkernel -> kernel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VgkJN8DEojuPTumAXLHotu
Turning a 141k-entity layer off/on took 1.5-5 s; it now takes ~0.6-0.9 s.
- Off/freeze toggles go through invalidate_layer_visibility: entities on
the layer keep their tessellation memo (visibility gates them before
tessellation); only block users re-expand. No mesh recolor.
- The resident patch reuses a surviving memo entry instead of
re-tessellating, guarded by the same display-context hash as the full
build.
- The patch looked up `layout.order` linearly per re-shown entity, which
made turning a big layer back on quadratic.
- recolor_meshes_for_handles resolved materials for handles with no mesh.
- Wire arena: a hidden slab keeps its instances and sets a `hidden` flag
in its consts (collapsed in the vertex shader); an unchanged re-show only
clears the flag. Const writes are batched into merged spans.
- The `layer_visible` automation action indexed the document's layer
table instead of the sortable panel rows the Layer* messages use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The codec and kernel repositories and crates are renamed opencadcodec and
opencadkernel. Inside OCS they are depended on under short keys, so code
reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node
graph engine). File-format and protocol spellings stay as they were: the
ACIS product id `acadrust`, the ACADRUST_* switches and the plugin
version keys `acadrust_version` / `acadrust_source`.
A button left of MCP on the command line opens a translucent node graph
over the viewport, with a palette of object, input, math, logic, list,
text and geometry nodes added by click or drag. An object node owns
drawing entities and its rows are the Properties panel's own rows for
them: the single-object page is extracted from refresh_properties as
entity_property_sections, values use the control protocol's
property_json, and writes go through set_property_value with a property
target override, so the panel, MCP and the graph share one read/write
path. A list reaching an object row makes one entity per element. Each
graph action is one undo step; the canvas pans and zooms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sync current main and companion codec changes before preserving unsupported frame rows, rendering in the entity plane, and sharing final layout metrics with placement preview.
DWGUNITS reframed every viewport by the conversion factor, on the
reasoning that a viewport's framing is measured in model units and has to
follow them. That holds for a window onto the model; it does not hold for
a layout's sheet viewport, whose view is the paper itself, in paper
units. Scaling it left the sheet the size it was while the view of it
moved by a thousand.
Only a paper-space viewport can be a sheet — the model-space scale
reached everything else, and what it reached frames the model by
definition. Among the rest, the layout names its sheet outright, and a
file arriving without that link still gives itself away by sitting at the
paper origin, where only the sheet sits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The status-bar units button wrote INSUNITS, which nothing on screen reads.
INSUNITS only tells the drawing how to scale content arriving from
elsewhere, so picking a unit relabelled the drawing and changed nothing
visible — the selector looked broken because the variable it moved was
invisible.
Point the button at LUNITS instead, the setting that decides how every
coordinate and distance is written, and give the three questions the word
"units" runs together a place each:
- The button offers the five length formats, each shown beside a sample of
itself, so Engineering and Architectural are told apart by sight.
- UNITS / DDUNITS opens a real dialog: length and angle format and
precision, the direction angle zero points, whether positive angles run
clockwise, and the insertion unit. Its sample comes from the same
formatters the drawing uses, so it cannot drift from the readout.
- DWGUNITS converts. Model geometry, viewport framing, the header's length
defaults and each dimension style's scale all move together in one undo
step; paper space is left alone, since the sheet is still the same sheet.
Locked layers are converted too — a half-converted drawing would be at
two scales at once.
The coordinate readout formatted at four decimal places whatever the
drawing asked for; it now goes through the same format_length the
properties panel uses. That made the angle formats reachable in turn, so
degrees-minutes-seconds now carries the d/'/" marks that say which
convention it is, and surveyor's units are written at all.
Typed input is still plain decimal: a length the readout writes as
5'-6 1/2" cannot yet be typed back. ANGBASE and ANGDIR are stored but not
yet applied to displayed directions.
Closes#668
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drawings arrive from elsewhere carrying someone else's layer names, and
folding them into a set of house layers meant renaming and merging by
hand, one layer at a time.
LAYTRANS reads a layer set out of another drawing and moves this one onto
it. Given a file it maps every name the two already share and translates
at once, which is all most incoming drawings need. Bare, it opens a
translator: the drawing's layers on one side, the loaded set on the
other, mappings built up between them and applied only on Translate, so a
mapping can be reconsidered and the whole translation is a single undo
step. Mappings can be saved and read back, objects can be forced to
ByLayer so the target layer's properties are what actually shows, and the
result can be written out as a log.
The move itself is the one LAYMRG already made for a single pair, so
LAYMRG now goes through it too rather than keeping its own copy.
Three things fell out for free or nearly so. A standards file is a
drawing, so .dws and .dwt need nothing but a place in the file filter.
The entity list is flat and holds block definitions, so objects inside
blocks travel with everything else instead of being stranded on a layer
that no longer exists. And a target layer that this drawing already has
is left alone: the drawing has an opinion about it, and restyling it
would reach past what was asked for -- only newly created targets take
the standard's properties.
Mappings are saved beside the drawing rather than inside a DWS. That is
where AutoCAD keeps them, but putting them there would mean inventing a
place in the drawing that nothing else reads back.
Closes#624
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Avoid full document, cache, and GPU rebuilds for entity deltas so add, delete, history, selection, and live polyline edits stay responsive on large drawings.
Convert the remaining bucket-B commands to prompt step by step instead of
requiring arguments or printing usage:
- KeywordCommand (buttons): VIEW, LINETYPE, SCALELISTEDIT, UCSICON, VSCURRENT.
- ValuePromptCommand (one value): LAYDEL (layer), ATTSYNC (block).
- TwoValuePromptCommand (new; two values): LAYMRG (source + target),
SETVAR (variable + value, blank second reads).
Each bare form now starts the stepped front-end and delegates to the existing
inline handler; the argument form is kept and is the dispatch target. The
direct visual-style shortcuts (HIDDENLINE / XRAY / …) and UCSICON's argument
form are untouched.
COUNT is intentionally left alone: bare COUNT already reports the tally (a
useful inquiry action, not a usage line), so prompting would only add friction.
Add KeywordCommand, a generic stepped front-end for keyword sub-verb
commands: bare NAME shows the sub-verbs as clickable buttons, and picking
one either dispatches `NAME <verb>` (action verbs like ATTDISP On/Off/Normal)
or prompts for an argument and dispatches `NAME <verb> <value>` (LAYERSTATE
Save/Restore/Delete <name>). It delegates to the existing inline handler, so
the logic stays in one place. First two of bucket B converted; the rest
(VIEW, LINETYPE, SCALELISTEDIT, SETVAR, LAYDEL/LAYMRG, and the selection-first
XDATA/UNDERLAY/ADJUST/DRAWORDER/CHPROP) can follow the same pattern.
Ortho and the running object-snap set were app-global (settings.txt only) and
Isolate/Hide was a session-only view state, so none survived save/reopen and
none were per-drawing. Bind all three to the file like AutoCAD:
- Ortho -> header.ortho_mode ($ORTHOMODE); running OSNAP <-> header
.object_snap_mode ($OSMODE bitmask) via a new SnapType<->bit map in settings
(osmode_from_snaps / snaps_from_osmode, round-trip tested). The header is the
per-drawing store, live state mirrors the active drawing: adopt header->live
on open and tab-switch, stamp live->header on save and when leaving a tab, a
fresh drawing inherits the current values, and SETVAR ORTHOMODE/OSMODE now
also update the live state. settings.txt stays as the boot/new-drawing
default; the per-file header overrides it on open.
- Isolate/Hide now set each entity's persisted invisible flag (DXF code 60,
which acadrust round-trips in DXF and DWG) in addition to the session set;
End Isolation clears it. The set is reseeded from invisible entities on load
and after undo/redo, the commands are undoable and mark the tab dirty. The
renderer already skipped invisible entities, so no render change was needed.
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>
Modify commands invoked with nothing pre-selected (ERASE, MOVE, COPY,
ROTATE, SCALE, MIRROR, ARRAY*, STRETCH, EXPLODE, GROUP, BLOCK, the
clipboard cuts, LAYOFF/FRZ/LCK/ULK, ...) ran the command on the FIRST
picked object and terminated. You could not build up a selection.
SelectObjectsCommand now keeps gathering: each pick accumulates into the
set (Shift removes), the prompt shows the running count, and the command
fires only when the user presses Enter or right-clicks. Reuses the
existing JOIN gather pattern, so no viewport/host changes are needed.
LAYMCUR acts on a single object's layer, so it keeps the immediate
first-pick behaviour via the new SelectObjectsCommand::instant().
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>
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>