Commit Graph
954 Commits
Author SHA1 Message Date
gianlucafiore 9fb075f8ee fix: improve plot style editing and print all plot actions 2026-08-11 18:45:00 -03:00
Hakan Seven c4dfa7567d fix(model): correct primitive creation and grips 2026-08-12 00:03:27 +03:00
Hakan Seven 59ca0f693f feat(model): edit primitive history properties 2026-08-11 20:14:51 +03:00
Hakan Seven 4ac10187c2 fix(model): preserve solid history lifecycle 2026-08-11 20:10:23 +03:00
Hakan Seven d2993fd054 feat(model): add parametric solid grips 2026-08-11 19:59:24 +03:00
Hakan Seven e37b77e426 feat(model): persist solid creation history 2026-08-11 19:52:17 +03:00
Hakan Seven dc6dc2256d fix(model): sync solid render interaction 2026-08-11 19:39:01 +03:00
gianlucafiore e0ead249b6 Merge pull request #740 from gianlucafiore/fix/dimension-properties-editable
Fix dimension styles incorrectly treated as read-only
2026-08-11 08:45:12 -03:00
gianlucafiore 3a752029cd fix: allow editing dimension styles 2026-08-11 08:40:54 -03:00
Hakan Seven 6fdd090176 fix(ui): restore start actions and web copy
Route About info through browser clipboard on WASM.
Let Start-page links reach the existing command allowlist.

Refs #707
Refs #738
2026-08-11 13:15:09 +03:00
Hakan Seven 70f65270aa refactor(properties): remove detail sections 2026-08-11 12:49:28 +03:00
Karim Jerbi f3d8904fc7 fix(alias): only advance the migration marker after the merge persists
Advancing ocad.pgp.version (or the web counterpart) could succeed while the
merged alias table write failed, so a later launch skipped migration against a
stale file and permanently lost the new aliases. Gate the version write on the
alias-table write (native fs::write / web set_item) succeeding.
2026-08-10 23:49:43 +01:00
Karim Jerbi 7013aa8cbb fix(alias): migrate new default aliases into existing profiles
Existing installs only got newly-added default aliases when storage was
absent; load_aliases parsed an already-present ocad.pgp or web alias table
verbatim, so R/RA/RE/REA remained unknown to profiles seeded before version 2.

Introduce a versioned default merge: track the default-table version each
profile has seen (wordless ocad.pgp.version marker / localStorage key) and
fold in only the aliases introduced after that version, never overwriting
user-defined mappings or resurrecting deliberately removed aliases. On first
migration the merged table and the advanced marker are persisted so later
launches skip the already-delivered aliases.
2026-08-10 23:39:46 +01:00
Karim Jerbi 319710f36b test(commands): alias R routes like REDRAW 2026-08-10 23:26:27 +01:00
Karim Jerbi d2e18ead38 feat(aliases): R/RA/RE/REA for REDRAW/REGEN; register verbs 2026-08-10 23:26:26 +01:00
Karim Jerbi 692bb3babf feat(commands): REDRAW/REDRAWALL/REGEN/REGENALL 2026-08-10 23:26:26 +01:00
gianlucafiore e2e4bfdcd4 feat(plotstyle): improve CTB editor workflow 2026-08-10 19:09:40 -03:00
gianlucafiore 1cd1b89250 Merge pull request #729 from gianlucafiore/fix/705-grip-dynamic-input
Add dynamic input and precision snapping to 2D grip editing
2026-08-10 13:30:27 -03:00
gianlucafiore 86d7226df6 feat(grips): add dynamic input and precision snapping 2026-08-10 13:26:35 -03:00
Hakan Seven ac8183ec91 refactor(cad): use direct stack dependencies 2026-08-10 15:03:31 +03:00
gianlucafiore 4697e8cc33 feat(color): add CAD indexed color picker 2026-08-09 23:40:55 -03:00
Hakan SevenandClaude Opus 5 273f482aee refactor: retire the solid3d feature — the web build gets solids too
The feature existed for one reason: truck's mesh and boolean crates reach
`vtkio → xz2 → lzma-sys`, a C library that cannot cross-compile to wasm32. So
the web build dropped it, and with it the Model tab, solid tessellation and
ACIS import — all of which quietly did nothing in the browser.

cadkernel is pure Rust and has no C dependency, so none of that holds any
more. The feature gated exactly two things by the end, `acis_export` and the
save-path sync that calls it, and both are as portable as everything around
them. Removing it means the web build makes primitives, runs booleans, draws
ACIS solids read from a file, and writes them back out as exact geometry — the
same as the desktop build.

Verified against all three: native, `--no-default-features`, and an actual
`wasm32-unknown-unknown` check rather than an assumption about one.

The docs claimed otherwise in four places and are corrected. `tessellation.md`
in particular was organised around whether an entity "goes through truck",
which is no longer a question anything can be asked — the three paths are now
a kernel B-rep mesh, a curve sampled through `entities::curve`, or geometry
emitted directly, and the per-entity table is updated to match.

`index.html` no longer passes `data-cargo-no-default-features`; there are no
features left to turn off.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-10 01:28:25 +03:00
Hakan SevenandClaude Opus 5 8c7ebdc9f1 refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.

NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.

The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.

SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.

Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.

The two `automation` test failures are unchanged from before this and are not
caused by it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-10 01:08:50 +03:00
Hakan SevenandClaude Opus 5 1f642bb40a feat(model): build and read solids with the geometry kernel
Every 3-D solid OCS makes or opens now goes through cadkernel, which holds
analytic surfaces — a cylinder is a cylinder record, not a spline that looks
like one. That is what lets a solid built here be written back out as exact
ACIS instead of a triangulation, and a facetted export is a one-way door: the
next application to open the file would have no way back to the circle.

The Model tab's seven primitives, the Design-group booleans, 3DROTATE,
3DMIRROR, 3DALIGN, SLICE, SECTION, POLYSOLID and PYRAMID all come from the
kernel now, and `scene.solid_models` holds a `Body`. A mirrored solid needed
its own care before; the kernel reverses the loops on the way through, so it
comes back the right way out rather than lighting black.

EXTRUDE and REVOLVE sweep the profile `entities::curve` already defines, so a
circle, an arc-bulged polyline and a closed spline arrive as the same thing.
A circle extrudes into four cylinder patches rather than a run of chords, and
a profile turned about an axis becomes a cone, a sphere or a torus. A spline
profile has no analytic side wall and is refused rather than approximated.

ACIS goes both ways through the codec's own bridge: `lift` to read and
`append` to write. That drops the 679-line truck conversion and the planar-
only exporter, and the tessellator keeps its bespoke sampler as a fallback for
whatever the kernel cannot yet express — reported rather than passed off as
whole, since a mesh missing a wall looks finished.

truck-shapeops is no longer used and is gone from the manifest. truck-modeling
stays for now: fourteen files use it as a 3-D NURBS evaluator, which the
kernel has no answer for yet, and SWEEP and LOFT have no kernel operation
behind them either.

The two `automation` test failures are unchanged from before this and are not
caused by it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-10 00:32:34 +03:00
Hakan Seven 8016b79ddf fix(snap): align extension tracking
Require OTRACK crossings to include the active Extension ray and resolve Shift constraints consistently across preview, grips, and clicks.\n\nRefs #716
2026-08-09 23:55:06 +03:00
gianlucafiore af7613f1d8 Merge remote-tracking branch 'upstream/main' into fix/716-perpendicular-tracking 2026-08-09 11:43:08 -03:00
gianlucafiore 080a82306e Fix perpendicular and OTRACK snap interactions 2026-08-09 11:37:21 -03:00
Hakan Seven f57c258ada fix(snap): correct active intersection snapping 2026-08-09 13:27:51 +03:00
Hakan Seven 6a39b82a68 Merge pull request #712 from gianlucafiore/fix/704-active-intersection-snap
Fix active intersection snapping
2026-08-09 13:17:31 +03:00
Hakan Seven d80ee709ac fix(otrack): clear and localize tracking hint 2026-08-09 13:04:55 +03:00
Hakan Seven ae24f985f0 Merge pull request #714 from gianlucafiore/feature/tracking-reference-tooltip
Show active tracking reference tooltip
2026-08-09 12:57:50 +03:00
Hakan Seven 2f60dd0f59 refactor(i18n): group Fluent attributes 2026-08-09 11:12:47 +03:00
gianlucafiore a8c9e3dd0c Merge branch 'feature/tracking-reference-tooltip' 2026-08-08 23:23:06 -03:00
gianlucafiore a06e8a7a69 Show active tracking reference tooltip 2026-08-08 23:09:06 -03:00
gianlucafiore 17b4eafa3b Merge upstream/main into fix/704-active-intersection-snap 2026-08-08 22:13:54 -03:00
gianlucafiore 15727bbffe Fix active intersection snapping 2026-08-08 21:20:40 -03:00
Hakan Seven 8847724127 fix(cursor): restore default geometry
Keep CURSORSIZE 5 and PICKBOX 3 mapped to the pre-option 60 px arms, 15 px box, and 8 px selection aperture.\n\nRefs #413
2026-08-08 21:53:26 +03:00
Hakan Seven 1738f9a79b feat(cursor): add crosshair options
Add persistent cursor sizing, pick aperture, color, pointer mode, isometric drafting planes, and SNAPANG-aligned grid behavior.\n\nRefs #413
2026-08-08 21:32:44 +03:00
Hakan Seven adb8ced8b5 feat(view): complete ZOOM options
Add command-line navigation modes and persistent wheel controls.\n\nRefs #412
2026-08-08 20:37:53 +03:00
Hakan Seven 598de3e47a Merge pull request #703 from gianlucafiore/feature/699-hatch-pick-boundary
Add internal-point hatch boundary detection
2026-08-08 18:36:46 +03:00
gianlucafiore 7d5818d5e0 Add internal-point hatch boundary detection 2026-08-08 09:09:43 -03:00
Hakan SevenandClaude Opus 5 2feac73eec fix(locale): put TCOUNT's new prompts back in the catalog
The placement options arrived with their prompts and messages written as
plain Rust strings, so they were the only command left speaking English
whatever the interface was set to.

They go through t! / tf! now like every other prompt, and the strings are
registered so the call actually reaches a translation: a source string
only resolves if the generated lookup knows it, and t! on an unknown
string just hands the source back. Keys follow what the table already
uses — the slug of the source, cut to forty-six characters, and the first
ten hex digits of its SHA-1 — which reproduces 2978 of the 3161 entries
exactly; the rest are hand-written ones predating the generator.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 03:19:54 +03:00
Hakan Seven 7ef5b431e1 Merge pull request #698 from gianlucafiore/fix/697
Fix TCOUNT numbering and add placement options
2026-08-08 03:07:09 +03:00
Hakan SevenandClaude Opus 5 2ea3bca093 feat(properties): stop offering a height the style has fixed
A text style with a non-zero height fixes the size of everything drawn in
it — the CAD that writes the file skips the height prompt for such a
style, and shows the height without letting it be changed. OCS offered
the field either way, so a drawing could be edited into disagreeing with
its own style.

The height row is now read-only when the style fixes it, for TEXT, MTEXT
and both attribute kinds. `num_prop` already draws a numeric row either
way, so the value still reads the same, just grey.

The panel builders are handed an entity, not the document it came from,
and widening that trait would touch every entity type. The lookup rides
beside the unit context instead, seeded from the same place, since it
answers the same kind of question: something the drawing settles that a
row needs in order to render itself.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 02:45:46 +03:00
Hakan SevenandClaude Opus 5 188d1a9ccb fix(dim): draw a dimension from the picture it carries
A DIMENSION references the block "that contains the entities that make up
the dimension picture", and that block is the picture: AutoCAD requires
it and draws it, BricsCAD draws it when present and only renders from the
dimension variables when it is missing. OCS re-derived the picture from
DIMVARS every time and ignored the block entirely.

That works while a style agrees with what the drawing actually drew. A
Revit-exported schedule here does not: its style stores DIMTXT already in
drawing units beside a DIMSCALE meant to scale it, so the text came out
three hundred times too tall, and each dimension carries a DIMFXL
override that is likewise already in drawing units, so the extension
lines ran nearly a million units past the points they measure.

Neither is guesswork about units now. The block holds the geometry the
drawing was authored with, so it is drawn: the contents are already in
world space and the block's base point is the origin, and the identity
instance that carries them exists only to reuse the scene graph's
ByBlock / layer-0 colour resolution. Every wire answers to the dimension,
so a pick still selects the dimension rather than one of its pieces.
Re-deriving stays as the fallback for a dimension with no block.

Annotative dimensions keep the old path: their several representations
are separate blocks and choosing between them is what the annotation
machinery already does, while the doctrine above assumes one picture.

A picture is only good while its inputs hold, so it is dropped when they
change: editing a dimension's properties, and saving the dimension style
it is drawn with. Entity edits already dropped it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 02:44:30 +03:00
gianlucafiore b704a43872 Fix TCOUNT numbering and placement options 2026-08-07 19:50:11 -03:00
Hakan SevenandClaude Opus 5 039f703d7f fix(input): stop drafting-aid keys from cancelling the command
F8 reached ORTHO as an ordinary command, and dispatching a command tears
down whatever is running first: the active command dropped, its preview
cleared, its base point forgotten. So pressing F8 partway through a LINE
abandoned the line, and during a MOVE it took the dragged ghost with it.

A function key gets pressed mid-command — that is what it is for.
Toggles that only flip a flag now skip the teardown: ORTHO, GRID, SNAP,
POLAR, and the drafting-settings popup. None of them starts a command,
opens a document or reads geometry, so there is nothing for the teardown
to protect.

F7, F9 and F10 came through the same path and were broken the same way;
they are fixed with F8. F3, F11 and F12 already had messages of their
own and were never affected.

Closes #677

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 00:48:25 +03:00
Hakan SevenandClaude Opus 5 eb513ac5f8 fix(stretch): let the selection stage behave like every other one
STRETCH ended itself when a crossing window caught nothing, so a missed
aim meant restarting the command to try again. A missed aim is not a
decision to stop: say what happened and ask for the corner afresh, the
way a selection that picks nothing leaves MOVE still asking.

Enter now ends the selection stage too. With objects already picked the
window is only there to narrow them, so there is nothing left to choose
— Enter takes a window containing all of them, which stretches every
vertex, which is the whole selection moving. That is what a
pick-selected stretch means. With nothing picked there is nothing to
end, so Enter still cancels. The prompt says so.

Not changed: with a prior selection, a window that misses every vertex
still goes on to ask for the base point. The command holds wires, not
entities, and the entity rules do not follow the wires — a small window
inside a circle catches its centre, and so the whole circle, while no
tessellated vertex is inside. Guarding on the wires would refuse that
stretch.

Closes #676

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 00:42:16 +03:00
Hakan SevenandClaude Opus 5 a4698c5b6b fix(units): leave the sheet viewport alone when converting
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 <[email protected]>
2026-08-08 00:11:57 +03:00
Hakan SevenandClaude Opus 5 62dfbfe2be fix(paper): give the sheet back its desk and its edge
A paper layout painted its whole canvas in the page colour, so the sheet
had no visible edge — zooming out never reached one, because the surround
was the same colour as the page. Floating viewports then painted
themselves black over it.

Both come from the same idea, that a viewport's canvas is a colour it can
be handed. What lies behind a viewport depends on what the viewport is:

- The full-canvas sheet shows the desk. `paper_bg_color` is the page, and
  the page is a bounded rectangle `paper_sheet_fill` already draws; using
  it for the canvas too erased the boundary between them. The desk is now
  its own colour, defined once in the scene and read by both the widget
  and the renderer, so the two cannot drift apart.
- A floating viewport sits on that sheet and must paint no canvas at all.
  Handing it a transparent one does not work: the background shader
  returns an opaque colour whatever alpha it is given, so alpha zero came
  out black. Skipping the pass is the only way to ask for see-through, so
  `skip_background` now suppresses both the clear and the pass. It is part
  of the frame signature too — without that, a cached frame would keep
  showing the canvas the flag was just turned off for.

Shading and fog still blend toward what is behind a floating viewport,
which is the page, so the uniform keeps that colour even though nothing
paints it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 00:11:47 +03:00