Commit Graph
1308 Commits
Author SHA1 Message Date
Hakan Seven cba2680be0 fix: preserve web builds and leader selection order 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 55908fefce Merge PR #1101: reduce selection property work 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 1943f8e3d1 Merge PR #1090: preserve reviewed clipboard correction 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 273821658a Stop the properties panel walking the whole selection
Rebuilding the properties panel for a 186 468-entity selection took ~112 ms,
and four things in it scaled with the selection for no reason.

The panel filtered the selection down to the active group with
`group.handles.contains(handle)`, a linear scan of a `Vec`, once per selected
entity. The default group is "All", which holds every selected handle, so
both factors are the whole selection: 186 468 squared. Hashing the group once
makes it linear. The measured cost never showed this — 55 ms at 1 993
entities against 112 ms at 186 468 is not a quadratic curve — but the scan is
still wrong, and it is the shape of thing that turns a large selection into
an unresponsive window.

`entity_in_working_plane` clones the entity before it checks whether the
plane is the identity, so with no UCS in play the panel deep-cloned every
selected entity to get back what it was handed. It now borrows, and clones
only where a transform is applied.

`aggregate_sections` built every entity's property list, collected them all,
then folded them together and read each exactly once. A property list is
sections of labels and formatted values, every one an allocation, so a
whole-drawing selection held 186 468 of them in memory at the same time. It
now builds one, folds it, and drops it, and stops once nothing is left in
common. The fold order is unchanged, so the result is too.

The ribbon sync cloned each entity's linetype into a `String` only to compare
it, and kept walking after every field had been found mixed. Once all four
are mixed nothing further can change, so it stops there.

`aggregate_sections` and `merge_sections` had no tests, so the fold that
drives the whole multi-selection panel was resting on the compiler. Two now
cover it: a field the entities agree on keeps its value, one they differ on
reads as varying, and a single entity folds to its own rows.

`properties-detail` and `properties-panel` report the phases, which is how
the panel arm was ruled out as the remaining cost — it does not reach its
5 ms floor at 186 468 entities.
2026-09-09 10:38:12 +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
JonathanandCopilot f98719cfd3 Fix: Enable clipboard copy for web/browser via system clipboard API
- Modify COPYCLIP command to write entities as JSON to system clipboard
- Modify CUTCLIP command to write cut entities as JSON to system clipboard
- Modify COPYBASE_AT command to write copied entities as JSON to system clipboard
- Entities are serialized as JSON with base point coordinates for cross-platform support
- Clipboard data format: OCS_CLIPBOARD_JSON header + base point + entity data
- Works in web (WASM) build via Clipboard API and native builds via iced clipboard

This fixes the issue where copy command didn't persist to system clipboard in web browser,
allowing proper paste operations and clipboard sharing between application instances.

Co-authored-by: Copilot <[email protected]>
2026-09-09 08:55:34 +02:00
JonathanandCopilot 3700f5abfb Fix: Enable clipboard copy for web/browser via system clipboard API
- Modify COPYCLIP command to write entities as JSON to system clipboard
- Modify CUTCLIP command to write cut entities as JSON to system clipboard
- Modify COPYBASE_AT command to write copied entities as JSON to system clipboard
- Entities are serialized as JSON with base point coordinates for cross-platform support
- Clipboard data format: OCS_CLIPBOARD_JSON header + base point + entity data
- Works in web (WASM) build via Clipboard API and native builds via iced clipboard

This fixes the issue where copy command didn't persist to system clipboard in web browser,
allowing proper paste operations and clipboard sharing between application instances.

Co-authored-by: Copilot <[email protected]>
2026-09-09 08:54:16 +02:00
JonathanandCopilot b3e3555f4f Adjust plus button spacing next to Start tab
- Changed button padding from [5, 10] to [4, 8] to match layout tabs
- Changed container left padding from 6.0 to 2.0 for consistent spacing
- Plus button now has same distance from Start tab as from Layout tabs

Co-authored-by: Copilot <[email protected]>
2026-09-09 07:48:26 +02:00
Hakan Seven b0c0cbdf4a Merge PR #1086: add collapsible property sections 2026-09-09 06:38:47 +03:00
Hakan Seven 4ddd19f6e8 feat(mcp): describe complete CAD record schemas 2026-09-09 01:01:44 +03:00
Hakan Seven 8f574c8388 feat(mcp): expose complete drawing records 2026-09-08 23:59:13 +03:00
Marcel d22f4fdbe7 Remove accidental file 2026-09-08 20:53:20 +02:00
MarcelP102 c9bb898b92 Merge branch 'HakanSeven12:main' into codex/collapsible-properties-sections 2026-09-08 20:47:42 +02:00
Marcel 7a87e08a02 Add collapsible Properties-panel sections 2026-09-08 20:33:24 +02:00
Hakan Seven c5503e4758 Merge PR #1083: implement solid shell workflow 2026-09-08 21:02:01 +03:00
ramox81 7913f8e2da Merge remote-tracking branch 'origin/main' into codex/shell-parity
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	crates/ocs_doc_api/Cargo.toml
#	src/modules/model/mod.rs
2026-09-08 19:49:35 +03:00
ramox81 be06a756da Implement solid shell workflow and properties 2026-09-08 19:37:35 +03: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 001605baa1 Merge pull request #1065 from ramox81/codex/slice-command-parity 2026-09-08 18:28:01 +03:00
Hakan Seven 4972bbed70 Fix issues found while integrating performance PRs 2026-09-08 17:33:02 +03:00
ramox81 261ab66afd Restore primitive properties after edge operations 2026-09-08 17:11:23 +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 <[email protected]>
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 <[email protected]>
2026-09-08 16:42:20 +03:00
ramox81 8822371e18 Complete CHAMFEREDGE workflow and history 2026-09-08 15:45:10 +03:00
Karim Jerbi a754fac6d4 Attach analytical arc to polyline Arc mode rubber-band preview 2026-09-08 13:16:49 +01: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
ramox81 21a357f55a Complete SLICE command behavior 2026-09-08 10:27:23 +03:00
Hakan Seven e51c4d0932 Merge updated origin/main 2026-09-07 19:01:54 +03:00
Hakan Seven d5047b49d9 Merge PR #1053: complete INTERSECT behavior 2026-09-07 18:43:49 +03:00
Hakan Seven 7c2a4d63ff Merge PR #1051: complete SUBTRACT behavior 2026-09-07 18:36:04 +03:00
Hakan Seven 050fbb0a6b Merge PR #1045: complete UNION behavior 2026-09-07 18:36:04 +03:00
Hakan Seven e889864855 Merge PR #1036: add paper sizes 2026-09-07 18:35:44 +03:00
ramox81 1e118b2e90 Complete exact INTERSECT behavior 2026-09-07 16:32:01 +03:00
Karim Jerbi 0425f979f9 feat(render): GPU analytical rendering for circles, arcs, and ellipses
- Render planar circles, circular arcs, ellipses, and elliptical arcs analytically via GPU-instanced screen-space quads and WGSL shaders.
- Implement exact arc sweep clamping with round end caps matching the CAD wire shader.
- Implement robust Newton-Raphson closest-point solver for ellipses with isotropic screen-space derivative anti-aliasing.
- Integrate into wire pipeline and PersistentWireArena upload paths.
- Remove unused mesh_volume_centroid function in doc_api.
2026-09-07 13:50:12 +01:00
ramox81 04a9a1b0a0 Complete SUBTRACT behavior 2026-09-07 13:58:31 +03:00
ramox81 5857ffa2bd Align UNION solid properties palette 2026-09-07 12:02:59 +03:00
ramox81 a59ea47990 Complete UNION command behavior 2026-09-07 11:27:03 +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 3385160493 Merge PR #1028: add document API with atomic edits
Add the typed document facade and plugin IPC adapter. Prepare bulk edits
before mutation, preserve undo and dirty state, keep profile elevations,
validate numeric inputs and session bindings, and generate Python constructors
with typed results.

Reuse existing entity transforms and profile conversion. Compute mass
properties and closed-surface bounds in cadkernel; pin its main revision
6a5e6fc2822f6af6b2611cd605b44437b35158f0. Keep plugin API metadata at version 6.

Validation: 646 native application tests, 597 kernel tests, 12 document API
tests, 91 plugin API tests, 3 Python tests, default/IPC feature builds and
wasm32 check passed.
2026-09-06 21:43:02 +03:00
Sebastian f79c8c59bb ocs_doc_api: dissolve src/app/doc_api_convert.rs shim into direct crate usage
Delete the thin re-export file; src/app/doc_api.rs now uses ocs_doc_api::convert
directly (use ocs_doc_api::convert + convert::* call sites). Remove the
mod doc_api_convert registration from src/app/mod.rs.

The conversion logic already lives in the crate (src/convert.rs) from the prior
move; the shim was redundant. 623 host lib + 38 doc_api tests green.
2026-09-06 17:14:48 +02:00
Sebastian d746d635fb ocs_doc_api: move doc_api_convert.rs into the crate (src/convert.rs)
Move the 503-line host-independent entity-conversion module (acadrust EntityType
<-> DTO conversions, transform_entity_geometry, entity_to_profile_curves,
bulge_arc_segment, entity_bounds, entity_kind_name, points_aabb) from
src/app/doc_api_convert.rs into crates/ocs_doc_api/src/convert.rs (host feature).

The host file is now a thin re-export (pub use ocs_doc_api::convert::*) so all
existing crate::app::doc_api_convert::* paths keep working unchanged. Zero
coupling risk (the module only touches acadrust + crate DTOs).

623 host lib + 8 crate + 3 consistency green.
2026-09-06 17:00:04 +02:00
Sebastian 919128e087 ocs_doc_api: fix branch-review findings (tab auth, bulge center, budget, caps, dead code)
CRITICAL:
- execute_doc_api: reject tab_id != bound tab (confused deputy); test
  doc_api_tab_mismatch_is_rejected.
- bulge_arc_segment: fix negative/CW bulge center (apothem magnitude + sign picks
  side; was double-sign-mirrored); test CW-vs-CCW center offset.
- cold-tess budget: move mass_cache/cold_tess_used to per-tab Scene so the budget
  persists across DocApi dispatches (was reset per request); budget-exhausted ->
  approximate meshes.metrics fallback instead of error.

WARNING:
- Loft: add BULK_ITEM_CAP (was uncapped -> unbounded kernel call from one request).
- CreateMany: pre-validate EntitySpec::Curve (ellipse ratio) in prepare phase ->
  all-or-nothing restored; regression test.
- Partial Ellipse: offset start/end_parameter by rot_z on rotation; regression test.
- resolve_body: add with_body borrow accessor (host overrides to borrow cache in
  place, O(1)); bounds() uses it instead of deep-cloning the B-rep per query.
- add_raster_image: validate file path (non-empty, image extension, reject
  UNC/traversal); regression test.

SUGGESTION:
- deploy: bump API_VERSION 5 -> 6 so DocApi plugins are gated at handshake on old
  hosts (append-only wire + defaulted method keep old V2-V5 plugins loading).
- dead code: wire query_name into apply_queries error labels; host uses crate's
  ObjectId::from_handle/to_handle (drop local duplicates); delete is_null/backend_mut.
- mock drift: test mock now stores curve specs + computes curve bounds (aligns with
  example mock); Spline/Dimension bounds delegate to points_aabb.

623 host lib + 8 crate + 3 consistency + 34 plugin_api green.
2026-09-06 16:03:34 +02:00