- Pins: codec 8189f83, kernel a82f17d, graph d3558bb (all manifests and
the python plugin lock).
- MASSPROP reports volume, centroid, inertia, products, radii of
gyration and principal moments of 3D solids and bodies from their
B-rep (exact where analytic, else from a fine mesh).
- OFFSET of ellipses and splines through the kernel's curve offset.
- FILLET between circles, ellipses and splines through the kernel's
curve-curve fillet, trimming arcs and open splines to the tangents.
The kernel bump brings the DWG ACIS audit fixes: slices, booleans and
press/pull on cones, slanted cylinder sections, survey coordinates,
spline walls and touching parts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With objects already selected, OFFSET opens at the side step with the
preview following the cursor instead of asking for a distance first
(#422). At the side step a typed distance places the offset at once on
the cursor's side, and a negative one on the other side; Enter at the
start repeats the last distance (#418). Erase and Layer, which lived on
the skipped distance step, are offered there too. With nothing selected
the distance-first flow is unchanged.
The opposite side is found by mirroring the cursor through its nearest
point on the outline, capped at half the offset distance so it cannot
cross a narrow gap back to the cursor's own side. Two new prompts are
added to the catalog and every locale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A running command only knew the entities that existed when it started;
committed results are now handed back to it, and OFFSET adds them to its
pick set.
Fixes#673
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>
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 <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.
Two places were left describing geometry for themselves.
A fit-point SPLINE was drawn from a C² cubic solved in spline.rs while
TRIM and snap cut a separate interpolation from the kernel. Near enough
to look right, far enough apart that a trim landed beside the line it
was aimed at. It now draws the curve those commands work on, and falls
back to the local solve only where the kernel declines — a fit spline
through points in space is not a planar curve, so there is nothing for
it to say.
Closed fit splines were worse than that: the kernel's interpolation is a
clamped solve with no wrap in it, so the curve TRIM used never returned
to its start and the seam cut nothing. Repeating the first fit point
closes it. The seam is C¹ where the rest of the curve is C², which is
the honest limit of a clamped solve — the alternative was a curve with a
gap in it.
OFFSET's preview sampling had drifted the same way TRIM's had: an arc
and a circle read `center.x/.y` as world coordinates when the entity
stores them in its OCS, so an extruded one previewed on the wrong side;
every curved kind was cut at a fixed twenty segments per radian whatever
its size; and an ellipse rebuilt its axis frame from the major axis' X
and Y alone, dropping the Z the entity stores. A hundred lines of that
become one tessellation. A RAY now previews as far as an XLINE does
rather than as a stub the length of its direction vector.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The spline path ran on truck's BSplineCurve<Point3>, which is not
rational. Weights were dropped on the way in and cleared on every split,
so a NURBS circle — which is how most circles arrive once they have been
through a modeller — was evaluated as a parabola and stayed one after a
break. cadkernel's NurbsCurve carries the weights and applies them in
homogeneous coordinates, and `spline_cut` splits by raising the cut to
full knot multiplicity, so both halves are exact and both stay rational.
`spline_ops` keeps the knot-domain call shapes the commands were written
against — `spline_range` replaces reaching for `range_tuple`, `spline_cut`
replaces `cut` plus the rebuild — so TRIM, BREAK, LENGTHEN and OFFSET
changed only where they named those.
Knot vectors for a rebuilt control polygon come from the kernel too, so
the clamped uniform vector is defined once rather than assumed in two
places.
truck is now absent from the whole spline path. It remains for 3D solids.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The offset core moves out: normalising the source, the cavalier_contours
call, the sharp-corner join fixup and the conversion back. What stays is
entity work — reading an LwPolyline in, writing offset LwPolylines out,
and the per-type offsets for lines, arcs, circles, ellipses and splines,
which are analytic and never needed the polyline machinery.
`BulgeArc` moves with it, since the offset preprocessing splits over-half-
turn arcs and cannot work without it. `entities::common` now re-exports it
from the kernel, so the twelve modules already reaching for
`entities::common::BulgeArc` are untouched.
`norm_rad` was a fourth copy of angle normalisation, after the three
removed from trim, fillet and explode. It now aliases the kernel's.
cavalier_contours leaves this crate's manifest: nothing here calls it any
more, and it arrives through the kernel's `offset` feature instead, which
acadifc forwards.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The readout learned to write lengths as 5'-6 1/2" and angles as
N 45d0'0" E, but typed input was still a bare parse::<f64>() — a
coordinate could be displayed and not entered. Give the formatters
their inverses.
parse_length takes feet and inches separated by a dash, a space, or
nothing, with the closing quote optional, and whole-plus-fraction in
any of those forms: 5', 60", 5'-9", 5'9-1/2, 9-1/2, 1/2. A leading
minus is a sign; the one inside 5'-9" is a separator, since after feet
there is nothing left to subtract from.
parse_angle reads the mark that says which convention an angle is in —
g for grads, r for radians, d/'/" for degrees-minutes-seconds, compass
letters for a surveyor's bearing — and reads a bare number in whichever
convention the drawing is set to.
ANGBASE and ANGDIR now apply, but only where they mean something. Which
way a thing points is measured from the drawing's zero and runs the way
it says; how wide an arc opens is the same number whatever zero is
counted from. format_direction and parse_direction are the pair for
directions, and the polar readout and polar input both go through them,
so the bearing shown is the bearing accepted. format_angle stays for
sizes, where applying a base angle would corrupt the value.
The command prompts follow the same rule, each classified rather than
swept: OFFSET distance, FILLET radius, CHAMFER distances, ARRAY and
MINSERT spacing, TRACE width, HELIX, and EXTRUDE height read lengths;
the four dimension text-rotation prompts, ARRAY's total angle and
INSERT's rotation read angles. MLINE's scale and INSERT's scale factor
are left alone — a multiplier has no unit to be written in. INSERT read
both through one parse; they are now separate.
The typed variants also take a decimal comma, which a single-value
prompt has no other use for. parse_length itself does not: inside a
coordinate the comma separates the axes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keep OFFSET picks current across newly created entities and preserve the command after pick-first offsets.
Use palette-provided contrast colors for active status controls and Alias Editor removal buttons.
Refs #653
Refs #658
Refs #659
OFFSET now opens with "Specify offset distance or [Through] <last>":
a typed value locks the distance, bare Enter / Space accepts the last
one (also offered as a prompt button), and T switches to through mode
(#418). After each offset the command loops back to the object pick at
the same distance until Enter / Esc finishes — Space acts as Enter
throughout, where the old flow cancelled. The pick-first path (#422)
keeps working: pre-selected objects jump from the distance step
straight to the side click.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OFFSET now follows the pick-first flow of MOVE / COPY (#422): with
offsettable objects already selected it skips the pick step and goes
straight to the distance / side step, offsetting every selected object
toward the clicked side — each through the cursor point in through
mode, all by the typed magnitude once one is locked. Preview covers
every target and a multi-object result commits as one undo step; with
nothing selected the command gathers a pick exactly as before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Completes the f64 migration on the CPU data path: the only f32 left now
is the GPU/render/screen layer, which is architecturally required (wgpu
vertex buffers and iced are f32; f64 vertices would double memory).
- EntityTransform::Rotate.angle_rad and Scale.factor are now f64 (the
transform was already applied in f64 around the DVec3 center, so this
only preserves the typed angle/factor). apply_standard_transform and
apply_standard_scale take f64.
- CmdResult::ExtrudeEntity.height and AlignSelected.{angle_rad,scale}
are f64; the EXTRUDE height parses as f64.
- defaults.rs (last-used construction values: circle radius/diameter,
rotate angle, scale factor, offset/fillet/chamfer distance, array
spacing/counts/angle) moved from Cell<f32> to Cell<f64>. Array counts
round-trip through u32 (exact below 2^53).
- Consumers in circle/array/fillet/offset/rotate/scale/align/solid3d and
dimension.rs rotate_point/scale_point follow. `as f32` is kept only
where a value feeds a WireModel rubber-band/ghost (the preview path is
screen-space f32).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OFFSET rejected XLINE targets: on_entity_pick only accepted
Line/Circle/Arc/LwPolyline, compute_offset had no XLine arm, and
entity_wire_pts returned no points for it (so hover/preview/through-distance
failed too).
Add offset_xline (parallel infinite line: same direction, base point shifted
perpendicular toward the pick side), a compute_offset arm, and an
entity_wire_pts case (a long segment standing in for the infinite line during
hit-test and preview; the committed XLine renders true). Also widen the pick
filter to every type compute_offset supports — this additionally reaches
Ellipse and Spline, whose offset functions already existed but weren't
selectable.
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>
OFFSET derived the side from the first segment alone: which half-plane of
that edge's infinite line the pick fell on. For a closed loop that misreads
a pick placed beside the shape — outside the loop yet on the inner side of
the first edge's line — so a counter-clockwise rectangle (RECTANG's winding
when dragged bottom-left to top-right) offset outward by a side pick
collapsed inward instead.
Decide the side for a closed polyline with a point-in-polygon test mapped
through the loop winding (left normal points inward for a CCW loop): inside
→ inward, outside → outward, regardless of winding. Open paths keep the
first-segment heuristic. Adds a regression test covering both windings and
both directions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Whole command/snap/grip/cursor chain now builds on f64 DVec3 end to end,
so picked points, snaps and grip drags stay precise at UTM-scale
coordinates instead of quantizing to ~0.5 m through f32.
- All ~80 command files migrated: draw (line/circle/arc/ellipse/shapes
in full f64; others via point methods), modify, annotate, insert, view,
inquiry, layers, groups, clipboard, layout.
- Entity apply_grip impls updated for GripApply::{Absolute,Translate}(DVec3).
- Construction sites (EntityTransform/CmdResult/DynAnchor) emit DVec3;
wire-preview boundaries cast to f32 only at the GPU edge.
- Modify/annotate commands keep internal Vec3 geometry where precision is
non-critical, casting at the DVec3 boundary; key draw commands store f64.
Builds clean (no warnings). Visual verification of crosshair/snap/grip at
extreme UTM zoom pending.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Home tab was already titled "Draw"; rename the module to match:
folder modules/home → modules/draw, HomeModule → DrawModule, id "home"
→ "draw". The inner draw/ command folder is kept (modules::draw::draw).
Update build.rs PRIORITY ("home" → "draw") so the tab returns to the
front of the ribbon; registry.rs regenerates accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>