The command menu now follows the commercial solutions' layout row for row: Enter, Cancel,
Recent Input ▸, the command's keywords, Snap Overrides ▸ (every object snap
with its glyph, Mid Between 2 Points, None, Osnap Settings…), then Pan and
Zoom. Pan / Zoom run transparently while a command is active ('PAN,
'ZOOM …): navigation modes leave the command untouched and an interactive
ZOOM Window parks it and resumes it afterwards. Rows carry an icon gutter
so labels align; the Enter row shows a default value ("<0.5000>") as its
hint instead of a second finish row.
ARC gets the commercial solutions' prompt flow with all keywords: start [Center], second
point [Center/End], end; after Center: end [Angle/chord Length]; after
End: center [Angle/Direction/Radius]; Enter continues tangent. The
previous default (Center/Start/End with method codes) stays as ARC_CSE.
Object snaps gain a one-shot "None" override. New strings in all locales.
Co-Authored-By: Claude Opus 5 <[email protected]>
A right-click while a grip is hot opens the grip menu: Enter places the
grip, Stretch / Move / Rotate / Scale / Mirror switch how the gripped
objects are edited (the latter four hand the selection to the matching
command with the grip as its base point), Base Point re-bases the gesture
at the next click, Copy leaves the originals in place and adds the moved
shapes as new entities while keeping the grip hot, Undo snaps the shapes
back to where the gesture started, Exit drops the edit.
The grip commit path moves out of the left-release handler into
`commit_active_grip_edit` so the menu, Enter and the placement click share
it; Enter with a hot grip now places it instead of repeating the last
command.
Co-Authored-By: Claude Opus 5 <[email protected]>
MOVE: Displacement, Enter at the second point uses the base as the vector.
COPY: Displacement, mOde (Single/Multiple), Undo of the last copy.
FILLET: Undo of the last fillet (the command refreshes its entity cache
through the new CadCommand::on_document_undone hook after the host undoes
the document step). CHAMFER: Multiple and Undo. OFFSET: Erase (the offset
replaces its source), Layer (Current/Source), Undo, Exit.
POLY3D, TRACE, MIRROR and MEASUREGEOM expose the keywords they already
accepted as options, so the context menu and option buttons list them.
The pinned prompt now keeps informational bracket groups such as
"[3 objects]" and only drops real option listings when the buttons take over.
Co-Authored-By: Claude Opus 5 <[email protected]>
The viewport context menu is now built from a pure model
(ui::popup::context_menu) rendered by the overlay: while a command runs it
offers Enter / Cancel / every keyword of the command's options() with the
keyword shown as a hint, a Recent Input submenu and M2P; when idle it offers
Repeat, Recent Input, Clipboard, Undo/Redo with history labels, the edit
block (Erase/Move/Copy/Scale/Rotate/Mirror/Draw Order), Isolate, Pan/Zoom,
selection tools, Properties and Options. The default row is anchored under
the pointer and the menu is keyboard-navigable (arrows, Enter, mnemonic
letters; any other key closes it and types into the command line).
Right-click behaviour is a user preference (Options → User Preferences,
SHORTCUTMENU / SHORTCUTMENUDURATION): shortcut menu (commercial solutions default),
time-sensitive, or the previous Enter-first cycle.
LINE gains options() (Close/Undo/eXit).
Co-Authored-By: Claude Opus 5 <[email protected]>
- IMAGEEMBED: pack raster images into OLE2FRAME entities so a .dwg is
self-contained; 'Embed Image' entry in the XREF attach menu; control/MCP
op embed_image for external tools; dxf_name for OLE2FRAME queries.
- Scene: populate_images_from_document now rebuilds OLE2FRAME and PDF
underlay models too (they used to vanish after any XREF op).
- Printing: add PRINTER_ENUM_NETWORK pass when listing printers; GDI
spooler fallback (hayro raster at 300 DPI + StretchDIBits) for hosts
whose PDF handler registers no print verb; DEVMODE orientation with a
90-degree raster rotation fallback for v4 drivers; fix the fit-scale
precedence bug that printed blank pages.
- PDF export dialogs on Windows use the async file dialog so the native
overwrite confirmation no longer deadlocks the UI thread.
- Plot dialog: persist the chosen printer (saved on selection) and the
picked plot window across sessions; page setups with no device no
longer reset the output device.
- Community fonts: detect unresolvable .shx references on open and offer
a download from the repository's fonts/ folder (contribution guide in
fonts/README.md); custom font source URL setting for intranet font
servers; downloaded fonts resolve from the per-user fonts directory.
Refresh associations after complete SpaceMouse camera updates and recognize imported paper-space owners without Layout objects. Use kernel containment, intersection, perpendicular and spline tangent solving; pin cadkernel 7ce27f2 and add regressions.
XREF command with status, reload, unload, detach, path, pathtype, bind, overlay, and attach options, plus guided XATTACH with unique naming and placement prompts.
Removes PR3's `viewport_frame` seam in `viewport_ref.rs`; the frame is
PR1's camera-derived one everywhere.
The five dimension commands (DIMLINEAR / DIMALIGNED / DIMRADIUS /
DIMDIAMETER / DIMANGULAR) now record a real association when they
measure through a viewport. The commit path's one association gate
(`dimension_association_allowed`, added when PR2 was stacked) gains an
else-branch: where PR2 committed the dimension unassociated and PR1's
inference guard returned an empty source list, `Scene::attach_viewport_
dimension_association` now stores the whole chain — dimension ->
viewport -> block path -> entity -> feature — from the AcceptedSnaps PR1
captured, in definition-point slot order.
* `Scene::dimension_association_slot_points` names that order, including
the radial dimensions `dimension_reference_points` does not cover.
* `OpenCADStudio::viewport_dimension_snaps` builds the slot array. A snap
whose paper point is no longer the definition point it fills has its
model point recomputed from that definition point through the frame:
the object pick records one click for both extension origins, and a
rotated DIMLINEAR projects its origins onto the measuring axis.
* Radial dimensions take their own path inside
`attach_viewport_dimension_association`. Their single reference is an
osnap type 10 carrying the radial marker (which circle / which polyline
arc segment) and the angle around it; `source_reference` would hand
back the generic curve-parameter marker -2, which
`radial_source_for_marker` cannot find.
One measurement rule, documented in `viewport_ref::measurement_rule`:
`actual_measurement` is always the RAW measurement in the dimension's own
space (paper, for a viewport dimension), and the viewport compensation
lives only in the magnitude of the negative DIMLFAC override, applied
once by the formatter. PR2's persistence already followed it; PR3's
refresh did not — it substituted the model distance times
`model_factor()`, which made a refreshed dimension read `user_lfac` times
too much versus an identical freshly-created one. Refresh now writes the
paper measurement (a debug assert pins resolved paper points to the model
points through the frame), and `axis_distance` goes with it.
Block sub-entity snaps: verified that `block_cache` stamps the top-level
INSERT's handle onto every expanded and clipped sub-wire, so
`wire_source` can only ever report the INSERT — nested inserts included.
`OpenCADStudio::resolve_source_block_path` now descends from that INSERT
at accept time (using the object-pick path's existing
`resolve_measurable_entity`, so arbitrary depth, not just one level) and
fills `SnapSourceRef.block_path` with the real inner entity. Where the
descent cannot identify one — a non-planar-pickable type such as text,
hatch, spline or solid, or nesting past eight levels — the source stays
the INSERT with an empty path, which is coarser but never wrong. Both the
limitation and where it bites are documented on
`Scene::resolve_block_snap_source` and `SnapResult::source`.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Removes PR2's two seams now that PR1 (viewport snapping) is underneath.
* `Scene::viewport_frame` is PR1's camera-derived implementation. The
viewport-record version is gone; PR1's returns `None` for oblique /
perspective viewports, which is what we want — every entry point in
`viewport_dim_seam` then declines rather than guessing a planar
mapping. `viewport_dim_seam` is now just the dimension object-pick
path, and its module doc says so.
* There is one accepted-snap store: PR1's `OpenCADStudio::accepted_snaps`.
PR2's parallel `dim_accepted_snaps` (and its command-name ownership
tracking, which PR1's clear-on-command-start/end already does) is
deleted, along with the `record_dimension_accepted_snap` seam.
`accepted_snap_for_point` reads PR1's list.
Two things layer on top:
- the explicit object pick is not a point step, so it still records its
own snaps, now through `push_accepted_snap` into PR1's list, and now
carrying the picked entity handle + block path as a `SnapSourceRef`
so PR3 can associate it;
- a typed coordinate carries no snap, so `measure_snap` re-attaches the
content viewport under a snap that is free *and* source-less. That
keeps `DIMLINEAR` with typed coordinates inside a 1:10 viewport
measuring the model, without ever reinterpreting a real paper-sheet
snap that happens to sit over a viewport.
* One association gate, not two. `dimension_association_allowed` is the
single predicate, derived from the same `dimension_measure_space`
classification that decides whether to compensate the measurement, so
the two can never disagree. `infer_dimension_sources_guarded` delegates
to it, and the commit path's "commit unassociated when measured through
a viewport" condition is now that predicate rather than a second,
independently-computed bool. It also covers the explicit-pick and
explicit-source-list branches, which the old inference-only guard did
not. Because the classification ignores the trailing dimension-line
placement click, an ordinary paper dimension whose placement click lands
inside a viewport rectangle keeps its association.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Colocated unit tests pin the persisted representation: a 1:10 viewport with
no user factor writes DIMLFAC -10, a 10-unit paper span then formats as 100,
and the same dimension read back in model space does not scale. A positive
user factor folds into the magnitude, and a drawing-wide negative DIMLFAC
that already carries the compensation writes no override at all. Another
test pins the viewport model->paper affine against ViewportFrame's own
mapping, so object-pick projection and snap projection cannot drift.
Also clears the recorded snaps when a multi-dimension command commits.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
DIMLINEAR / DIMALIGNED / DIMRADIUS / DIMDIAMETER / DIMANGULAR created on a
sheet now report the model measurement of the geometry seen through a
viewport, at any viewport scale, while definition points, the dimension line
and the text stay in paper coordinates and the annotation keeps its sheet
size (DIMSCALE is untouched).
A viewport frame is a similarity - uniform scale plus in-plane twist - so
projecting the model span onto the paper axis and compensating gives exactly
the raw paper measurement times 1/scale. No geometry is rewritten: the
compensation is persisted once as a negative DIMLFAC override on the
dimension and applied once by the linear formatter, so preview text,
dimension text, the properties measurement row and DXF/DWG export all agree.
User measurement multipliers fold into the same magnitude, and an explicit
text override still bypasses formatting entirely, so it is never re-scaled.
Angular dimensions are never compensated - a similarity preserves angles.
Mixed input (one point through viewport A, another through viewport B or on
the sheet) is refused rather than silently mis-measured: the command line
explains why and the dimension falls back to plain paper measurement.
A dimension measured through a viewport is left unassociated: its definition
points are projected paper coordinates, so inferring a paper-space
association would bind it to geometry that is not there. PR3 records the real
dimension -> viewport -> entity chain.
The result is one ordinary dimension entity carrying one style override, so
undo/redo works through the existing history with no new out-of-document
state.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Point input now keeps an `AcceptedSnap` alongside each point a command
collects: the paper point, the model point it came from, the viewport and
frame it was seen through, and the geometry identity the snap engine
attributed. Interactive picks record in the click handler, typed / dynamic /
headless points record in `feed_command`, and the list is cleared whenever a
command starts or the active space changes. Commands that don't care keep
consuming the plain paper point unchanged; PR2 reads the model point and
frame, PR3 the source.
Dimension association inference is suppressed when the dimension's points came
through a viewport. Inference matches geometry against the dimension's own
definition points in whatever space it is handed, so paper definition points
projected from a viewport would associate with unrelated paper-sheet geometry
at those coordinates, or match model geometry using paper coordinates. An
empty source list makes `attach_dimension_association` a no-op. PR3 replaces
this with real viewport-aware associations.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
PropValue::ParamRow/ParamAddRow, the PropParamInput/Commit/Delete/AddNew
messages, their command_driver handlers (on_prop_param_commit/_delete/
_add_new, resolve_named_parameter_edit), and their render functions all
already existed — but nothing ever constructed the "Parameters" section
that would route data to them, so the whole inline add/rename/redefine/
delete workflow was unreachable from the UI. Adds that missing section
to the no-selection page in refresh_properties, alongside a new
PropValue::ParamsVisibilityToggle header row (mirrors the existing
show_constraint_values setting) for toggling whether constraint pills
show their driven value/parameter name.
Users can now manage named parameters inline in the Properties panel
(live resolved values, add/rename/redefine/delete) as an alternative to
the separate PARAMETERS modal, which continues to work unchanged.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Constraint delete: a Properties-panel row delete button, viewport
click-to-select on a constraint glyph pill with Delete-key support, and a
right-click "Delete Constraint" menu item — all routed through one
undoable Scene::selected_constraint-aware path.
Fixes two named-parameter bugs found while testing that feature:
- DIMCONSTRAINT: a parameter named like a mode-switch keyword ("r", "d",
"a", "x", "y") could never be referenced, since the keyword check ran
before the parameter-name check.
- PARAMETERS modal Apply rebuilt Scene::named_parameters but never
refreshed the Properties panel, leaving renamed/deleted rows stale
until an unrelated refresh (e.g. a selection change) happened to occur.
Co-Authored-By: Claude Sonnet 5 <[email protected]>