Commit Graph
2768 Commits
Author SHA1 Message Date
Sebastian 2dba0158ca ocs_doc_api: Phase 2b-c (linear Dimension) — Phase 2 complete
- CreateDimensionLinear(DimensionSpec) op + GetDimensionMeasurement query (append-only).
- backend add_dimension_linear (Dimension::Linear via DimensionLinear::new) +
  dimension_measurement (linear/radius/diameter distance).
- Dimension bounds from measured + definition points; Dimension handle + measurement().
- spec Dimension family; api_reference + binding_schema regenerated; mocks + baseline.
- Test phase2c_dimension_linear_create_measurement_bounds -> 607 host lib + 8 crate
  + 3 consistency green. Phase 2 (full 2D + annotations) is now complete.
2026-09-06 09:43:37 +02:00
Sebastian ba8e5ed0cd ocs_doc_api: Phase 2b-b (Hatch)
- CreateHatch(HatchSpec) op + GetHatchBoundary query (append-only).
- HatchSpec{boundary: Vec<[f64;2]>, solid}; executor validates >= 3 boundary points.
- backend add_hatch (Line-edge BoundaryPath, solid/pattern) + hatch_boundary
  (reconstruct loops from Line edges; non-line edges -> Unsupported).
- Hatch bounds from boundary edges; facade create_hatch + Entity handle.
- spec Hatch family; api_reference + binding_schema regenerated; mocks + baseline.
- Test phase2b_hatch_create_boundary_bounds_delete -> 606 host lib + 8 crate + 3 consistency green.
2026-09-06 09:24:11 +02:00
Sebastian fc0e2c2c9d ocs_doc_api: Phase 2b-a (Text/MText annotations)
- CreateText/CreateMText/SetTextContent ops + GetTextContent query (append-only).
- backend add_text/add_mtext/set_text_content/text_content (Text.value/MText.value).
- facade create_text/create_mtext + Text/MText handles with content()/set_content().
- Text/MText coarse bounds + transform (insertion point); ensure_transformable.
- spec Text/MText families; api_reference + binding_schema regenerated; mocks.
- can_modify backend hook (exists + not-locked) for SetTextContent validation.
- consistency baseline extended with the appended variants.
- Test phase2b_text_mtext_create_content_transform -> 605 host lib + 3 consistency green.
2026-09-06 09:07:41 +02:00
Sebastian 1925a83a25 ocs_doc_api: spec<->wire consistency + append-only baseline tests
Add tests/spec_wire_consistency.rs: (a) every op/query name in spec/entities.toml
maps to an existing Operation/Query variant; (b) both enums are append-only
relative to a recorded variant-order baseline (bincode discriminant stability).
Adds toml to dev-dependencies. 3 tests green.
2026-09-06 08:44:53 +02:00
Sebastian c74504f30c ocs_doc_api: fix review findings (scale/rotation/all-or-nothing/DoS/bounds/codegen)
CRITICAL:
- transform_entity_geometry: fix s^2 double-scale (axes already carry scale; drop
  the *s pre-multiply so 2D entities scale by s like solids), and handle rotation
  for direction/angle fields (apply_dir for Ray/XLine direction + Ellipse
  major_axis; offset Arc angles; compose Insert rotation).
- ensure_transformable: check layer-lock + resolve solid bodies (not just type),
  restoring TransformMany all-or-nothing; regression test with a locked layer.

WARNING:
- volume/centroid: memoize per (handle, geometry_epoch) + cap cold-cache
  tessellation budget (query-batch DoS).
- entity_to_profile_curves: fail Unsupported on polyline bulge (no silent
  chord-substituted sweep profile).
- Ellipse: validate minor_axis_ratio in (0,1] so bounds never under-bound.
- RasterImage bounds: bracket all four corners (rotated/flipped images).

SUGGESTION:
- binding_schema.json handles derived from spec families (includes ArcCurve/
  Ellipse/Spline/Ray/XLine).
- build.rs header: enums are hand-maintained (not 'GENERATED'); ops_gen/query_gen
  headers corrected to match.
- Dedupe divergence mesh_volume_centroid into the crate's geom module; host +
  both mocks delegate.

Tests: +3 regression tests (scale-once, rotation, locked-layer all-or-nothing)
-> 604 host lib green; 8 crate unit tests green.
2026-09-06 08:36:06 +02:00
Sebastian fabdeae8bf ocs_doc_api: make mock backends exhaustive over new Curve2Spec variants
Add Arc/Ellipse/Spline/Ray/XLine arms to the test + example mock add_curve
kind matching and the example curve_bounds, so the crate's unit tests and
examples compile against the Phase 2 curve families.
2026-09-06 01:29:51 +02:00
Sebastian 0e18867915 IMPLEMENTATION.md: roadmap status — all 5 phases done (with noted remaining sub-items) 2026-09-06 01:23:25 +02:00
Sebastian f24c0b2d75 ocs_doc_api: Phase 5 (media & misc, read-mostly)
- entity_kind_name names the media/misc families (RasterImage, Table, Leader,
  MultiLeader, MLine, Mesh, Helix, Region, Body, Surface, Face3D, Dimension,
  Hatch) so GetEntity reports a real kind instead of 'Other'.
- RasterImage coarse bounds (insertion + u*width + v*height).
- Generic delete already works on these families; no typed create in v1
  (read-only EntityView per plan).
- spec RasterImage/Table read-mostly families; IMPLEMENTATION.md updated.
- Test phase5_media_entities_read_kind_and_bounds -> 601 host lib green.
2026-09-06 01:22:28 +02:00
Sebastian 7a481c6ce2 ocs_doc_api: Phase 4 (paper-space viewports)
- Operation::CreateViewport(ViewportSpec) appended to the wire enum (append-only).
- backend add_viewport (center/width/height/view_target/view_height).
- Viewport bounds (center +- w/2,h/2), transform (moves center), ensure_transformable.
- facade EntityCollection::create_viewport; spec Viewport family; api_reference +
  binding_schema regenerated; mocks updated.
- Test phase4_create_viewport_bounds_transform -> 600 host lib green.
- IMPLEMENTATION.md: Phase 4 done (set_view/view-query remaining).
2026-09-06 01:16:31 +02:00
Sebastian b03f68737e ocs_doc_api: Phase 3 (block references)
- Operation::CreateInsert(InsertSpec) appended to the wire enum (append-only).
- backend add_insert validates the BlockRecord exists (unknown block -> Validation,
  no mutation); host inserts acadrust::entities::Insert with scale+rotation.
- Insert transform moves insert_point (transform_entity_geometry) +
  ensure_transformable includes Insert.
- facade EntityCollection::create_insert; spec Insert family; api_reference +
  binding_schema regenerated; mocks updated.
- Test phase3_create_insert_and_transform -> 599 host lib green.
- IMPLEMENTATION.md: Phase 3 done (attributes/traversal remaining).
2026-09-06 01:05:51 +02:00
Sebastian d46351cdb3 ocs_doc_api: Phase 0 + Phase 2a (sweep profiles, non-solid transform, full 2D curves)
Phase 0 (outstanding supported-family methods):
- add_vertex on LwPolyline (insert vertex, out-of-range validation).
- extrude / revolve from profiles (entity_to_profile_curves: Line/Circle/Arc/
  LwPolyline straight-segment conversion -> geom2d::Curve -> brep::extrude/revolve).
- non-solid transform + transform_many via acadrust geometry
  (transform_entity_geometry for Line/Circle/Arc/Point/LwPolyline).
- ensure_transformable broadened accordingly (all-or-nothing pre-validation).

Phase 2a (full 2D curve families): ArcCurve/Ellipse/Spline/Ray/XLine.
- Curve2Spec extended (Arc/Ellipse/Spline/Ray/XLine) + create_arc/create_ellipse/
  create_spline/create_ray/create_xline facade constructors + handle types.
- entity_kind_name, entity_bounds (Arc coarse, Ellipse major-axis, Spline bbox;
  Ray/XLine unbounded), transform_entity_geometry, ensure_transformable extended.
- spec/entities.toml families; api_reference.md + binding_schema.json regenerated.

Tests: +6 doc_api integration tests (phase0_*, phase2_*) -> 598 host lib green.
IMPLEMENTATION.md updated (Phase 0 done, Phase 2a done / 2b remaining).
2026-09-06 00:51:39 +02:00
Sebastian e41086b058 Add ocs_doc_api: object-oriented CAD entity API (v2)
New crate crates/ocs_doc_api: object-centric facade (DocApi -> Document ->
collections -> typed handles), transport-agnostic wire layer (DocApiEnvelope =
single op | read-only query batch), per-op autocommit (no write transactions),
bulk ops with upfront-validation all-or-nothing, structured ApiError, and
build-time codegen (spec/entities.toml -> ops/query enums, api_reference.md,
binding_schema.json).

ocs_plugin_api: append-only PluginRequest::DocApiRequest / PluginResponse::
DocApiResponse (serde_bytes payload) + defaulted HostApi::doc_api_dispatch.

Host: DocApiBackend impl for HostSession (src/app/doc_api.rs) mapping to scene
primitives (solid_models lift-on-miss, register_prepared_solid_model, meshes
metrics), entity conversions (doc_api_convert.rs), HostSession accessors +
doc_api_dispatch override.

Tests: 592 host lib (incl. 7 DocApi integration tests: create/boolean/query,
unknown-id, transform-many all-or-nothing, query-batch cap, 2D/geometric/DWG
roundtrips), 8 crate unit tests. Examples + README/ARCHITECTURE/IMPLEMENTATION docs.
2026-09-06 00:15:27 +02:00
Hakan Seven 9129c85f97 Add Greek README translation and language links 2026-09-05 14:52:45 +03:00
Hakan Seven 055f1cdb1a Redesign modelling ribbon icons 2026-09-05 14:46:06 +03:00
Hakan Seven 9756f0598a Remove control GitHub Actions workflow 2026-09-05 13:32:48 +03:00
Hakan Seven de3fe2fdf1 Remove native and web compiler warnings
Reuse the application library from the binary entry point instead of compiling the module tree twice. Keep desktop-only transports and caches out of web builds, remove unused test code, and check executable targets in CI.
2026-09-05 13:21:02 +03:00
Hakan Seven 3253965194 Run wire arena regressions in CI and shorten cache documentation 2026-09-05 12:33:20 +03:00
Hakan Seven ff3262e39d Merge commit '7bf095e02be49764fdda1fa32ef13a1675592246' 2026-09-05 12:30:43 +03:00
Hakan Seven c39fd8c4c0 Merge commit '69bd3c40a9ed3c0a2e412cc5716ef52b5e300033' 2026-09-05 12:30:43 +03:00
Hakan Seven d91e54e501 Merge commit 'b9c252474853d47c91bd6c03cd6bf8752378061f' 2026-09-05 12:30:43 +03:00
Hakan Seven af0cc68c5a Merge pull request #1006 with block rendering fixes
Invalidate cached world-space block vertices when the base translation changes. Share the regular/mesh partition between full uploads and patches so instanced blocks are uploaded only once. Cover both paths with CPU and GPU regression checks.
2026-09-05 12:30:43 +03:00
Hakan Seven 66067860f9 Merge commit '19490a548bad05356d69bd47c6e3827a6d3b0a77' 2026-09-05 12:25:19 +03:00
PanosandClaude Opus 5 19490a548b fix: give the wire arena its own buffer budget
`PersistentWireArena` keeps one instance buffer for the whole resident
set and patches it in place on an edit. Sizing that against the shared
per-chunk budget caps it at 32 MiB, which is 524 288 `WireInstance`s.

A drawing with more than that fails `WireArena::build`, falls back to the
batched path, and re-uploads everything on every edit. On the 198 k-wire
reproducer -- 821 316 instances -- that shows up as:

    wire  200-370ms  arena-hybrid

instead of the ~12 ms `arena-patch` the same drawing reached before the
limits moved onto the shared budget.

The 32 MiB cap is right for transient chunks, where many are allocated
per frame and any one of them failing loses the frame. The arena's buffer
is a single long-lived allocation that exists precisely to avoid those
re-uploads, and it can afford more: 821 k instances is 53 MB, ~79 MB with
the arena's headroom. 128 MiB leaves it four times smaller than the
505 MiB allocation that crashed a 2 GB card, and it is allocated through
`gpu_upload`, so exhausting VRAM still degrades the frame rather than
aborting.

`OCS_GPU_CHUNK_MIB` overrides both caps, so a test can still force small
buffers everywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:50:17 +03:00
PanosandClaude Opus 5 fb64e76f04 fix: let the wire arena serve drawings that contain blocks
Clicking to end a segment froze the UI until the whole drawing had been
re-uploaded. With block geometry cached the remaining cost is the regular
half, and on two drawings it dominates:

    113 k wires   regular=96ms  block=13ms  index=12ms   total 122ms
    198 k wires   regular=85ms  block= 7ms  index=25ms   total 118ms

It is rebuilt from scratch because `PersistentWireArena` -- the only
thing that patches wire buffers in place -- was disabled outright by:

    && !vp_wires.iter().any(|wire| wire.render_instance.is_some())

One block reference was enough; the reproducer has 11 363. The scene
already produces an incremental patch in 0.9 ms and the GPU side could
not use it.

Instanced wires are not something the arena has to handle: they are a
different batch type with their own pipeline. Partition them out, let the
arena own and patch the rest, and keep building `BlockWireGpu` for them.

The handle index is unaffected -- it maps into `vp_wires`, not into arena
slabs, and the patch path already maintains it.

Measured on the 198 k-wire drawing, per drawn segment:

    before   wire 350ms  (shared-fullupload)
    after    wire  12ms  (arena-patch)

with the whole click, including `bump_entities`, going from ~411 ms to
~20 ms.

Verified visually on drawings with and without blocks, through repeated
edits and undo/redo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:18:50 +03:00
PanosandClaude Opus 5 071b4b6a87 perf: keep block geometry on the GPU across edits
Adding one entity to a drawing rebuilt every block group's geometry from
scratch. Measured on a 1.17 M-entity drawing with 11 380 block wires, the
per-edit upload split as:

    upload-split regular=80.4ms block=230.3ms

The block half is three quarters of it, and every byte of it was
identical to the previous frame: a new line is not a block reference.
`BlockWireGpu::from_wires` re-emitted each group's segments, re-expanded
them six-fold and re-uploaded the vertex buffers anyway.

That work depends only on the block definition being expanded, which
already has a stable identity: `render_instance.source_id`, minted once
when the block cache expands a definition and unchanged while that
expansion stays cached. Cache the vertex chunks and constants under
`(source_id, mesh_edge, colour)` -- colour is in the key because a
highlight tint changes the constants while leaving the vertices alone.

Only the per-instance placements are rebuilt, and they must be: each
carries its own draw depth and the depth map shifts on every add or
remove. They are also small, a few thousand placements against millions
of vertices.

Entries not seen in a build are dropped, so a block-cache rebuild that
mints fresh source ids does not strand old buffers on the GPU.

Measured: the block half falls from 230 ms to 7.4 ms, and the whole
per-edit upload from ~350 ms to ~118 ms.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:16:12 +03:00
PanosandClaude Opus 5 7bf095e02b perf: parse wire names across cores when building the handle index
With the block geometry cached, the per-edit upload is 135 ms of which
only 92 ms was accounted for by the regular and block halves. The rest is
the handle index: a `String::parse` per wire over the whole 198 k
resident set, rebuilt from scratch on every edit.

The arena has an incremental path for this (`patch_handle_index`), but
the shared path never reaches it -- the same gate that keeps the arena
away from this drawing. Short of that, the parses are independent, so run
them across cores and keep only the map insertion serial.

`par_iter().enumerate()` collects in index order, so the slot lists keep
the emission order they depend on.

Adds a `wire-handle-index` PERF line, since this was the unaccounted
third of the upload.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:10:49 +03:00
PanosandClaude Opus 5 69bd3c40a9 perf: stop scanning every entity for associative centers on each edit
The spans put it exactly: 63.5 ms of a 63.8 ms pointer-up is
`bump_entities`, and inside it two drawing-wide scans.

`refresh_associative_centerlines` and `refresh_associative_center_marks`
each walk all 1 166 357 entities, filter to Lines and parse every one's
extended data looking for an association that references a changed
handle. About 30 ms each, run on every edit -- to find, on this drawing,
none at all.

Track whether the document holds any such association. The question is
settled once, with a single pass, and after that each edit only examines
the handles it actually changed: an entity that carries an association
flips the answer to yes, and nothing else can. Deleting the last one
leaves the answer at yes, which costs a scan that finds nothing rather
than skipping one it should have run. A coarse `bump_geometry` re-opens
the question, since it can carry a new document.

Where an association really exists the scans run exactly as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:10:49 +03:00
PanosandClaude Opus 5 b9c2524748 perf: warm the entity quadtree alongside the handle index
Warming only the interaction handle index left the first hover at
385 ms, down from 992 but not gone:

    hover-detail  query=0.1 handles=384.6 ... candidates=186

The handles stage builds two indexes, not one. `interaction_candidate_handles`
also reaches for `entity_index()` — the entity quadtree, keyed on
geometry_epoch and just as cold at that point. Warm both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:10:49 +03:00
PanosandClaude Opus 5 909b7f3379 perf: hand the loader thread's block definitions to the scene
The first edit on a large drawing spent 1147 ms rebuilding the block
definition cache:

    wires-build 2723.3ms ... blk_cache=1147.2 ... memo_miss=186455

Not because the edit invalidated it -- `add_entity_internal` already
routes a non-block entity through `bump_entities`, so drawing a line
leaves `block_epoch` alone. The cache was empty because
`prepare_open_geometry` built it on a throwaway `Scene`, exactly as it
did the tessellation memo, and it went the same way.

`PreparedOpenGeometry` now carries the definitions across, moved rather
than cloned, and `install_prepared_open_geometry` stamps them with the
receiving scene's `block_epoch`. That stamp is sound: the document is not
touched between the loader finishing and this call, so the definitions
describe it exactly. A key that does not match what the scene later asks
for simply misses, which is the old behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:10:49 +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
PanosandClaude Opus 5 d5d9ceec45 perf: hand the loader thread's tessellation memo to the scene
`prepare_open_geometry` builds the first Model wire set on a throwaway
`Scene`, filling its per-entity tessellation memo with an entry for every
one of the drawing's 186 455 entities. `install_prepared_open_geometry`
then transferred the wire set and the interaction index but not the memo,
so the temporary scene took it to the grave.

The first geometry edit therefore rebuilt from cold:

    wires-build 2723.3ms memo_hit=0 memo_miss=186455

against 760 ms once the memo is warm -- 1.3 s of work the loader thread
had already done, repeated on the user's first action.

`PreparedOpenGeometry` now carries the memo, moved rather than cloned
since the scene is dropped immediately afterwards. The guard hash travels
with it: the memo is only usable under the state it was built for, and a
mismatch on the receiving side clears it, which is what happened
unconditionally before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:10:48 +03:00
PanosandClaude Opus 5 2d2fdacdfd perf: parse each wire name once when applying draw order
The resident wire sort keys on the entity handle parsed out of each
wire's name `String`. `sort_by_key` evaluates that key on every
comparison, so a 198 k-wire resident set parsed every name roughly
eighteen times over.

Measured at 295 ms on every rebuild of the resident set -- including
rebuilds that re-tessellated nothing, where the memo hit on all 186 455
entities and the sort was most of what was left.

`sort_by_cached_key` evaluates the key once per element. It is stable in
the same way `sort_by_key` is, so draw order is unchanged.

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 28d3aa5be2 Harden native MCP compatibility 2026-09-05 01:36:23 +03:00
Hakan Seven cbb4015760 Remove MCP bundle distribution 2026-09-05 01:10:13 +03:00
Hakan Seven afbf826b0c Ship client-neutral MCP bundles 2026-09-04 23:58:39 +03:00
Hakan Seven 17e5cda3fb Tighten MCP command line control 2026-09-04 23:38:46 +03:00
Hakan Seven d7c3422dbe Space MCP control from command line edge 2026-09-04 23:30:26 +03:00
Hakan Seven 071e224d26 Add client-neutral native MCP control 2026-09-04 23:27:03 +03:00
Hakan Seven 28cc473062 Add persistent live OpenCADStudio control 2026-09-04 22:25:55 +03:00
Hakan Seven 9d991d7feb Bound GPU uploads and complete Greek and performance integration 2026-09-04 20:26:29 +03:00
Hakan Seven 89624f64fc Merge PR #1002: opt-in performance instrumentation 2026-09-04 20:11:02 +03:00
Hakan Seven 75f9205da4 Merge PR #1001: GPU allocation stability and Greek locale 2026-09-04 20:06:56 +03:00
Hakan Seven 92e1dfd685 Merge PR #1000: cache selection filter entity types 2026-09-04 20:06:56 +03:00
Hakan Seven b4e7007c66 Merge PR #996 already included by the PRESSPULL stack 2026-09-04 19:50:19 +03:00
Hakan Seven 290bfca030 Merge PR #993 while retaining the validated dependency pins 2026-09-04 19:50:07 +03:00
Hakan Seven 90761904a9 Merge reviewed modeling PRs with Unicode and PRESSPULL fixes 2026-09-04 19:49:55 +03:00
Hakan Seven 9026017cd0 Pin reviewed codec and kernel fixes 2026-09-04 19:34:38 +03:00
Hakan Seven d8790ef433 Use exact kernel curves for PRESSPULL area containment 2026-09-04 19:34:38 +03:00
Karim Jerbi a9fda66ac0 fix(ui): add non-color marker to CLI info entries and drop dead desk parameter
- Add 'i  ' prefix to EntryKind::Info and coordinate set_step_prompt deduplication to satisfy WCAG 1.4.1 across all 22 themes
- Drop unused _desk parameter from crosshair_background and clean up its call sites
- Document AutoCAD-standard context-based crosshair drafting surface architecture in crosshair_background
- Add doc comments documenting deliberate alpha omission in canvas_is_light and backlog scope in is_wide
2026-09-04 16:26:13 +01:00