Commit Graph
459 Commits
Author SHA1 Message Date
ramox81 0426df79ca fix(section): add state grip menu 2026-09-10 11:25:33 +03:00
Marcel 10d1d9b7d9 Implement DELOBJ source deletion policies 2026-09-09 20:49:29 +02:00
Hakan Seven 57b9cf979e Merge PR #1116: add SECTIONPLANE workflow 2026-09-09 19:42:21 +03:00
ramox81 c9c768cd9f feat(model): add section plane workflow 2026-09-09 18:29:35 +03:00
Hakan Seven 9fe6033304 Merge PR #1110: expose selection settings
# Conflicts:
#	src/app/mod.rs
#	src/app/properties.rs
#	src/app/settings.rs
#	src/app/update/file.rs
#	src/app/view/modal.rs
#	src/ui/window/options.rs
2026-09-09 14:40:37 +03:00
PanosandClaude Opus 5 08d339d38f Translate the new Selection labels, and pin what SELECTIONPREVIEW's bits do
The ten strings the Options card gained were English in nineteen of the
twenty-one locales. They are now translated in all of them, using the words
each locale already uses for the surrounding labels — de-DE "Griffe",
fr-FR "Poignées", zh-CN "夹点", zh-TW "掣點" and so on for grips, and the
same for selection, preview and object. System-variable names stay in Latin
uppercase, as they already do in every locale's SETVAR listing.

Composing them from existing keys, the way el-GR composes the neighbouring
labels, only worked for one of the ten: there is no existing key for "limit",
"unlimited", "shift", "rectangle" or "lasso", and "Grips: Objects" as the
label of a numeric limit would read worse than English.

The rollover preview was reported as possibly not working. It works, and now
says so: `the_idle_rollover_follows_bit_one` drives a settled hover over a
line and checks what the scene highlighted, for all four values of the
bitmask. Bit 1 alone highlights, bit 2 alone does not, both do, neither does
not. That is the assertion the inverted config comment would have made
someone doubt — the pick simply needs the cursor still for the 500 ms of
`HOVER_DWELL_MS` before it runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 14:15:44 +03:00
PanosandClaude Opus 5 cabb20db45 Put the rest of the selection settings on the Options card
Options → Selection already carried the pick box, the selection-area effect
and the grip visuals. Four settings behind it were finished, persisted and
reachable only by typing a system variable, and one more was a constant.

**Four that only needed a widget.** `SELECTIONEFFECT`, `SELECTIONPREVIEW`,
`PICKADD` and `PICKDRAG` are all wired end to end already. `SELECTIONEFFECT`
was the odd one: the card's own "Restore Defaults" button resets it, so it
could be turned off by a button and only turned back on from the command
line.

`SELECTIONPREVIEW` is a bitmask and gets one checkbox per bit, which is why
`set_preview_bit` exists rather than two inline expressions — the bits are
the thing this change is most able to get backwards.

The `PICKADD` checkbox is worded as AutoCAD words it, "use Shift to add to
selection", which is the *inverse* of the stored flag. That inversion is
commented at both ends; it reads as a bug otherwise.

**`GRIPOBJLIMIT`.** The limit past which a selection gets no grips arrived
with the selection work as a hardcoded 100, matching AutoCAD's default. It is
now a setting, a slider and a system variable, following `PICKBOX`'s path
exactly. AutoCAD's 0 means "no limit", which the old `>` comparison could not
express, so the read now guards on it — with a test, because zero is the one
value that would otherwise suppress every grip instead of allowing all of
them.

The slider covers 0..=1000, where every useful value lives; the sysvar takes
AutoCAD's full 0..=32767, and a larger value set from the command line parks
the handle at the end while the read-out keeps showing the real number.

**`SelectionPrefs`** replaces the `pick_box` parameter rather than adding
three more. `view_window` already took seventeen positional arguments, and
`pick_add` / `pick_drag_rect` are two adjacent `bool`s that would swap
silently at the call site.

**Two fixes found on the way.** `OPTIONS` and `OP` were registered for
autocomplete but never dispatched, so typing either answered "Unknown
command" and the dialog could only be opened from the start page. And the
`SELECTIONPREVIEW` doc comment had its bits the wrong way round — the code
reads `& 1` for the idle rollover and `& 2` for the in-command one, as
AutoCAD does — which would have put the two new checkboxes' labels backwards.

**Not done, deliberately.** `PICKFIRST` and `PICKAUTO` exist but are always
on, with no variable. `PICKSTYLE` is stored in the header and read by nobody
— group expansion decides from the group's own `selectable` flag — so an
"object grouping" checkbox would control nothing. Associative-hatch selection
does not exist as a selection behaviour. Each needs a behaviour change rather
than an exposure.

Of the ten new strings, en-US and el-GR are translated; the other nineteen
locales carry the English text. Composing them from existing keys, the way
el-GR composes the neighbouring labels, only works for one of the ten —
there is no existing key for "limit", "unlimited", "shift", "rectangle" or
"lasso", and "Grips: Objects" as the label of a numeric limit would be worse
than an untranslated string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 13:19:06 +03:00
Hakan Seven 973af87d58 fix: keep exact snapping, source drawing data and adaptive options layout
Resolve curve intersections through cadkernel, retain empty exact results, gather analytic candidates independently of tessellation, and bound material uploads. Preserve large MINSERT data while limiting rendering work; remove obsolete review narratives.
2026-09-09 13:17:30 +03:00
Hakan Seven f41d79221b Merge PR #1097: configure block double-click editing 2026-09-09 13:10:21 +03:00
Hakan Seven 6d5763bb70 Merge PR #1102: measure selection phases 2026-09-09 13:09:18 +03:00
Hakan Seven 8262ba41c1 Merge PR #1099: reduce area selection candidate queries 2026-09-09 13:09:18 +03:00
Hakan Seven bba46f1c9c Merge PR #1098: scale bulk selections 2026-09-09 13:09:18 +03:00
Hakan Seven b2ec9ef863 Merge PR #1096: align history controls and fix browser history copy 2026-09-09 13:09:18 +03:00
Panos f9cacaec2b Say where a selection's hit-testing time goes, and stop hashing to skip wires
`hit` was one number covering five things: resolving candidate handles, the
wire box test, two hatch tests and two mesh tests. `select-commit` now
reports them apart, which immediately reframed the problem — at 186 468
entities the wire box test is 55 ms of 206, the hatch tests are 103, and
handle resolution is 47.

Two changes alongside it, both in the crossing scan.

Skipping a wire already in the selection was keyed by hashing its name, once
per triangle and once per glyph inside the box. A bitmap over wire indices
answers the same question with an array index. The name set stays behind it,
because several wires can share one entity's name and the output dedups by
name; a wire whose name is already in that set now gets tested once more
before being dropped, which costs one projection and changes no output.

The degenerate-wire loop that ends both the box and the lasso scans tested
`seen.contains(wire.name)` before the structural tests beside it. `||`
short-circuits, so with the name lookup last a wire with two or more points —
very nearly all of them — is dropped without hashing anything. That loop
walks every candidate, so it was one string hash per wire in the box.
2026-09-09 10:39:37 +03:00
Panos d5bf919414 Cut the cost of finding what a selection box contains
A window over a whole drawing spent 407 ms gathering candidates and 259 ms
testing them, of 825 ms total, at 186 468 entities.

**Gathering** ran eight spatial queries because that is what hover and
snapping need between them. An area selection reads five: the wires and their
segments, plus the fill triangles, pick triangles and glyphs that
`indexed_box_crossing_hits` uses to catch hatches and text inside the box.
The three that exist only for snapping are skipped.

That was worth 148 ms. The rest came from the queries themselves.
`SpatialGrid::query` sorts its result twice — once over the entry indices, to
remove the repeats an entry collects for spanning several cells, and once
over the values. Over a box covering the drawing that is two O(n log n)
passes across 4.4 M elements to remove a handful of duplicates.
`query_unordered` returns the same set in cell order with the duplicates left
in, and reserves the space it needs instead of growing a 17 MB vector in
steps. Gathering went from 515 ms to 69 ms on a synthetic set at that size,
and from 407 ms to 27-40 ms in the application.

Everything an area selection does with the result already dedups and does not
read the order. Click picking keeps the sorted `query`, where scan order
decides which of two overlapping entities a click lands on.

**Testing** projected both endpoints of every segment in the box, then threw
the result away for any wire already selected. A circle or a polyline
contributes one segment per tessellated span, so at 24 spans per wire that is
23 projection pairs out of 24 doing nothing. Skipping a wire once it is in
took a 4.4 M-segment scan from roughly a second to 42 ms.

The overlay path — active after any edit, so most of a working session —
needed the same treatment, including its own merge: `extend_indexed` sorts
and dedups every category, which would have put back the sorts this removes.
`extend_indexed_area` keeps the dedup on `wire_indices`, and must: the window
branch of `box_hit` walks it directly, so a wire left in twice would be
selected twice.

Tests cover the equivalence in both directions: the area query matches the
full one as a set in every category an area selection reads, and the merge
dedups `wire_indices` and nothing else.
2026-09-09 10:35:17 +03:00
Panos d02ce08349 Make a large selection scale with the drawing
Selecting a whole drawing was unusable on a 186 468-entity file. Four costs
scaled with the selection where they did not have to.

Every selected handle triggered a walk of the entire document to find LEADERs
pointing at it. On the file this came from, selecting 471 583 entities in a
585 786-entity drawing did not finish inside eight minutes. A reverse index
built once per geometry epoch answers the same question; a test asserts it
returns exactly what the walk returned, including the case of two leaders on
one annotation.

Group expansion asked the selection about every group: for each selected
handle it scanned each group's own member list. Asking each group once
whether it holds anything selected turns that around.

Selecting and deselecting went one entity at a time, and `deselect_entity`
rebuilt the ordered list with a `retain` per handle, so removing N of M
walked the order list N times. Both now take a slice.

Grips were built for every selected entity. AutoCAD stops at GRIPOBJLIMIT,
and the DWG header does not carry the variable, so its default of 100 stands
in for it.

The remaining two commits are measurement: `select-commit` reports the phases
of a selection separately, which is what showed the bulk paths had been added
to the press-drag branch while a click-move-click window goes through a
different one entirely.
2026-09-09 10:29:24 +03:00
Marcel 6185369508 Add configurable block double-click editing modes 2026-09-09 09:06:34 +02:00
Marcel d22f4fdbe7 Remove accidental file 2026-09-08 20:53:20 +02:00
Marcel 7a87e08a02 Add collapsible Properties-panel sections 2026-09-08 20:33:24 +02:00
Hakan Seven 7ec07f3222 Merge pull request #1070 from ramox81/codex/filletedge-parity
# Conflicts:
#	Cargo.lock
2026-09-08 18:30:34 +03:00
Hakan Seven 4972bbed70 Fix issues found while integrating performance PRs 2026-09-08 17:33:02 +03:00
PanosandClaude Opus 5 d267083a29 perf: say which message caused an edit's latency
The first measurement showed four consecutive edits at ~192 ms each with
a handler cost of 0.55 ms. That is below the `update` line's 5 ms
threshold, so the log reports the latency but not what asked for it —
and whether a live command preview is bumping the geometry epoch on every
mouse move is exactly the question the numbers raise.

`nav-prepare` now carries the message label alongside the op.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 16:42:21 +03:00
PanosandClaude Opus 5 b4ae4cad4e perf: measure an edit from the input to the frame that answers it
Drawing a point in a large drawing feels slow, and nothing currently
measures the thing the user actually waits for. `update` times the message
handler, `wires-build` and friends time the rebuild, but the span between
them — press to the frame that shows the new geometry — has no number.

Navigation already has exactly that plumbing: `record_nav_perf` stamps an
instant when the input arrives, `prepare` and `encode` pick it up, and
`nav-prepare` / `nav-render` report `input`, `build`, `prepare` and
`elapsed` from the original event.

`NavPerfOp::Edit` reuses it. `update` snapshots `geometry_epoch` before
dispatch and stamps the sample when it moved, so any message that changed
geometry — a drawn point, a grip commit, an undo — reports its full
latency rather than only the handler's share.

Diagnostic only: `record_nav_perf` sets a pending sample and nothing else,
so no LOD or settle behaviour changes.

Nothing is optimised here on purpose. Four call sites feed the snapper
along different branches, and picking one to instrument before knowing
which dominates is how the earlier rounds of this work were wasted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 16:42:20 +03:00
ramox81 d426012660 Execute command scripts through full input runner 2026-09-08 13:52:18 +03:00
ramox81 8bb6703c70 Complete FILLETEDGE workflow and history 2026-09-08 12:36:49 +03:00
Hakan Seven e889864855 Merge PR #1036: add paper sizes 2026-09-07 18:35:44 +03:00
juan 4eb656e946 add-paper-sizes 2026-09-06 19:57:24 -03:00
Hakan Seven d66e89ca19 Complete application localization across all supported languages 2026-09-07 01:05:02 +03:00
Hakan Seven 2ebad14586 fix(web): capture save previews from the browser canvas
Capture the rendered canvas during the clean frame instead of requesting synchronous GPU buffer readback. Preserve embedded previews and allow saving when canvas readback is denied. Add a browser save and reopen regression check.

Fixes #1021
2026-09-06 13:14:56 +03:00
Hakan Seven 2c83ae2736 Show donation request once per app version 2026-09-06 11:44:38 +03:00
Karim Jerbi 0dab3ec54a feat: add COMMANDLINEFADETIME sysvar for overlay history fade
Controls how long command-line history lines stay visible, in ms (0-60000, default 3000). Bare form stays active until a value or Esc.
2026-09-06 02:13:09 +01:00
gianlucafiore 410624e4b3 feat(insert): add initial PDF underlay attachment 2026-09-05 15:39:22 -03:00
Hakan Seven ac8a545669 Fix layout cache correctness and GPU resource recovery 2026-09-05 19:56:49 +03:00
Hakan Seven 1a4b389b0e Merge pull request #1014: perf: let one tessellation serve every background 2026-09-05 19:28:34 +03:00
PanosandClaude Opus 5 391a74a615 perf: let one tessellation serve every background
Switching to a Layout re-tessellated the whole model — `memo_miss=186455`,
2034 ms — to arrive at geometry identical to what was already in the memo.
The only thing that differed was colour.

`local_wires_for` already knows this: it stores the raw colour and drops
its background argument with `let _ = bg_color;`, noting that
`Batches::finalize` applies `adapt_to_bg` last, "so the cache no longer
has to rebuild on BACKGROUND / layout-switch". The tessellation memo's
guard had not been told, and still mixed the background in, so every
entry was discarded on a switch.

`finalize` now records what its colour resolution consumed — the raw
colour, the raw glyph colours, and the three flags it branches on — as
`WireModel::bg_adapt`, and `resolve_colors_for_bg` replays it for another
background. The guard drops the background, so a memo entry serves any
layout.

The resolution recomputes from the recorded raw colour rather than from
the resolved one, which makes it idempotent and therefore safe over a set
that mixes memo hits with freshly tessellated misses. Re-adapting the
resolved colour would not be safe: `adapt_to_bg` snaps a near-white colour
to pure black on a light background, and pure black comes back pure
*white* on a dark one, losing the tint. A test pins that asymmetry so the
raw colour is not optimised away later.

The contract is tested against the real `finalize` rather than a copy of
its logic: for every background pair, building under one and resolving for
the other lands exactly where building under the other would have.

`bg_adapt` is `None` for wires built anywhere else, and the resolution
skips them, so nothing outside the block cache changes colour. It is also
`None` when the resolution would be a no-op — a preserved colour with no
canvas fill looks the same under every background — which keeps the common
case at no cost.

Ordering is unchanged: the pass runs inside the builder, before
`append_scene_markers` and `apply_refedit_fade`, both of which have their
own separate background dependency.

`wires-build` gains `colors=` so the pass has a number of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 18:53:16 +03:00
PanosandClaude Opus 5 09838be9ec feat: draw a notice for one frame before rebuilding a layout
Switching to a Layout stops the main thread long enough that the
compositor decides the application has stopped responding and offers to
kill it. The switch itself is not the problem — `layout-switch` reports
about 10 ms — it is the frame after it, which rebuilds the whole wire set.

That frame now has a cheap one in front of it. `layout_settling` is set
when the switch takes, and while it holds, the viewport is replaced by a
centered "Preparing layout…" on the canvas background. No shader widget
means no scene primitive, so `prepare` has nothing to do and the frame
costs nothing. A `window::frames` subscription clears the flag, which is
the guarantee this needs: that callback runs once a frame has actually
been presented, so the notice is on screen and the compositor has been
answered before the expensive frame starts.

The deferral is of the *frame*, not the switch. Two callers depend on the
switch having completed when it returns — one calls `fit_all` immediately
after, another compares `current_layout` — so deferring the switch itself
would have broken both.

This restarts the compositor's timer immediately before the slow frame and
replaces a stale window with something that says what is happening. It
does not make the slow frame fast: if the rebuild still exceeds the
compositor's timeout the dialog can return, and the durable fix is the
2 s `memo_miss` behind it, which is a separate change.

Only `en-US` is validated against at build time; Greek is filled in too
since it is in use here, and the rest fall back until translated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 18:53:02 +03:00
PanosandClaude Opus 5 38f576c296 perf: warm the interaction handle index during open
The first hover over the canvas after opening a large drawing froze the
UI for a second:

    hover-detail  query=0.2 handles=986.1 ... candidates=253
    hover-dwell   986.6ms

`interaction_handle_index` and the insert-hatch map it reads are built
lazily, and first use is that hover. Neither can move to the loader
thread: they read `hatches`, `meshes` and `block_meshes`, which are
installed on the real `Scene` only after the loader has finished.

Build them at the end of the open instead, where the user is already
waiting. This does not make the work cheaper -- open grows by roughly the
second the hover used to cost -- but a second inside an operation that is
already seconds long reads very differently from a second-long freeze on
a mouse move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:10:48 +03:00
Hakan Seven 13afc1479f Automate weekly releases and synchronize application versions 2026-09-05 02:13:20 +03:00
Hakan Seven 28cc473062 Add persistent live OpenCADStudio control 2026-09-04 22:25:55 +03:00
Hakan Seven 92e1dfd685 Merge PR #1000: cache selection filter entity types 2026-09-04 20:06:56 +03:00
PanosandClaude Opus 5 f1efc73cbe perf: stop rescanning every entity to fill the selection-filter menu
Pan and even plain cursor movement over the canvas ran at ~8 fps on a
large drawing. Nothing rendered was responsible: the GPU idled at 0-8%,
encoding no scene at all changed nothing, and vertices, pixels and draw
calls could each be cut without moving the number. `view` was the frame
-- 110 ms of it against 3-6 ms on a small drawing -- and inside `view`
it was the status bar.

The status bar builds the selection-filter menu, which asks for the
distinct entity type names in the layout. That call cloned the layout's
entire handle list (1 166 357 handles, ~9 MB), then did a map lookup, a
String allocation and a BTreeSet insert for each one. About 107 ms of a
110 ms frame, to produce roughly twenty names, for a menu that is only
visible when opened.

Cache the result against geometry_epoch and the layout block, since it
only changes when entities are added or removed, and make the scan
itself borrow the handle list rather than clone it and collect &str,
allocating only for the names it returns.

The immediate-mode UI rebuilds every widget on every mouse move, which is
fine as long as nothing in that path is O(document). This was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:20:30 +03:00
ramox81 9279384e9a Complete PRESSPULL selection, face editing and extrusion history 2026-09-04 15:31:30 +03:00
ramox81 479bf87988 Complete LOFT commands, surfaces, previews and editable history 2026-09-04 13:03:33 +03:00
ramox81 ad47b17603 Complete SWEEP command options and kernel integration 2026-09-04 11:15:44 +03:00
gianlucafiore 71d1d18f89 fix(grips): clear OTRACK after typed stretch commit 2026-09-03 17:23:11 -03:00
Hakan Seven 611ea0696b revert: restore thumbnail capture for native saves (#956) 2026-09-03 21:11:00 +03:00
Hakan Seven edd3faeeae fix(save): decouple native saves from screenshots (#956) 2026-09-03 21:06:19 +03:00
Hakan Seven 3088e641aa fix(view): stabilize 3D navigation and hidden lines
Refresh stale depth bounds after geometry edits, allow orbiting through camera poles, and preserve hidden-line occlusion for selected solids.\n\nFixes #957
2026-09-03 20:22:35 +03:00
Hakan Seven 1d8494099d Fix revolve cursor plane 2026-09-03 20:02:11 +03:00
Hakan Seven b5a926cc20 Merge remote-tracking branch 'origin/main' into review/pr-986
# Conflicts:
#	Cargo.lock
#	Cargo.toml
2026-09-03 19:52:20 +03:00