# Design loading ## Cold geometry rebuilds after 0.3.1 Three alternating cold-process comparisons on the same Windows machine and optimized `ci` profile, with no concurrent build, compare the unchanged 0.3.1 source with the straight-line projection and exact surface-refinement reuse changes: | Saved design | Before, median evaluation | After | Reduction | |---|---:|---:|---:| | Public sensor enclosure, 21 features, 52 KB | 11.96 s | 8.85 s | 26.0% | | Local Pi case assembly, 25 features, 2,990 bodies | 14.75 s | 11.57 s | 21.6% | These are geometry rebuild times, not complete desktop opening times. The public fixture can be generated with `solvecraft-cli run examples/acceptance/sensor_enclosure.json --save sensor.solvecraft --quiet`, then measured with `solvecraft-cli bench-open sensor.solvecraft`. Each benchmark starts a new process; this does not measure a warm timeline-cache hit. The local assembly is private and is not part of the repository. Straight segments now use guarded algebraic closest-point projection instead of sampling 65 points and running Newton iterations. Other curve types retain their existing behavior. Curved-face refinement reuses derivative and midpoint evaluations with exact finite input keys and bounded, local caches. It keeps the original tolerance, seam and singularity checks, refinement passes, and triangle limits. Identity assembly placements retain the definition body's shared display-mesh cache. Changed placements continue to build separate meshes in their own coordinates. Face-name preparation remains eager during placement: deferring it merely shifted expensive work to thumbnails and the first face query, so that approach was excluded. Both saved fixtures retain exactly the same body names, face/edge/vertex/shell counts, bounds, volumes, and surface areas. Focused tests compare cached and uncached refinement coordinates and normals bit for bit on periodic, singular and transformed surfaces. For desktop phase profiling, set `SOLVECRAFT_PROFILE_OPEN=1` before launching the app. Interactive Open reports geometry, placement, tessellation, thumbnail and total worker times to stderr without logging the source path. Thumbnails were about 0.1 seconds of the slow assembly's open and were not the main bottleneck. Further cold Boolean and import costs remain; these changes do not make all documents open instantly. ### Prepared assembly geometry and selection Prepared world geometry is now keyed by the document, evaluated model and exact placement context instead of the session revision. Selecting a face or edge, changing visibility, or moving the view no longer discards already prepared assembly bodies and meshes. Geometry edits, pending occurrence moves, authoring context changes, undo/redo and timeline rollback still invalidate the cache. Scratch sessions share prepared geometry while their inputs match, and adopting a computed session retains its warm cache. On the private 2,990-body Pi assembly, a matched Linux desktop control probe reduced an edge-selection round trip from 9.24 s to 0.57 s. Initial opening stayed essentially unchanged (33.54 s to 33.32 s); this fixes a separate selection stall, not cold geometry evaluation. Three fresh Windows desktop runs with the corrected cache measured a median 15.87 s to ready: 13.04 s geometry, 2.20 s placement, 0.53 s meshing and 0.07 s thumbnails. These used the optimized `ci` profile and actual Vulkan rendering, with isolated settings and caches. Linux and Windows timings are separate measurements and must not be compared as a speedup. `solvecraft-cli bench-open design.solvecraft` reports JSON lines for file reading, JSON decoding and migrations (including input-name repair), timeline evaluation, individual features, and world placement/display meshing. `--parse-only` stops before geometry evaluation. The parse timer includes document validation and repair, not just the JSON parser. Windows x64, optimized `ci` profile, measured during other builds: | Design | Parse/repair | Evaluate | Display meshes | |---|---:|---:|---:| | 19-feature enclosure, 34 KB | 1.2 ms | 14.9 s | 6 ms | | 25-feature assembly, 15.2 MB, 2,990 bodies | 21 ms | 12.5 s | 2.1 s | | Generated 1,000 independent boxes, 185 KB | 5 ms | 0.82 s | 0.15 s | | Generated 5,000 boxes in five grids, 10 features | 0.45 ms | 17.6 s | 0.81 s | The enclosures spend most of evaluation in joins and cuts, not parsing. Designs save feature definitions; opening them rebuilds their geometry. Those times remain after the repair fix. A separate generated timeline with missing input names reproduced minutes in the reader: | Timeline (geometry rolled back) | Parse/repair before | After | |---|---:|---:| | 2,000 boxes, 6,000 unnamed inputs | 78.1 s | 10–20 ms | | 5,000 boxes, 15,000 unnamed inputs | interrupted after 131 s, still repairing | 26–46 ms | Previously, each missing input restarted the `d1`, `d2`, … search, scanning the document for every candidate. This approaches cubic work as unnamed inputs accumulate. Loading now reserves names once and assigns missing names with an advancing cursor. Existing names, names on later features, parameter collisions and reuse of surplus names retain the previous behavior. Regression tests compare the old and bulk repairs and load a 5,000-feature unnamed timeline. # Viewport performance Measured with the control channel (`ui.inspect` `frame_ms`: CPU time of one UI frame, which includes picking under the cursor) on the sample plate patterned 20 × 20 (400 bodies, about 600k triangles), 1600 × 1000 window under Xvfb, release build. Hover: 60 cursor moves across the model; orbit: a right-drag. | | hover median | hover p90 | orbit median | |---|---|---|---| | before | 18.7 ms | 37.5 ms | 21.0 ms | | after | 1.9 ms | 2.4 ms | 2.6 ms | | after, Browser hidden | 0.4 ms | — | 0.4 ms | What changed: - **Picking culls by bounding box.** Per-body boxes are cached until the model changes. The face raycast skips bodies whose box the ray misses or enters behind the nearest hit so far. Edge and vertex tests skip bodies whose box, projected on screen, is not under the cursor. - **Picks are reused** while the cursor, camera and model are unchanged, and **no picking runs while the view moves** (orbit, pan, zoom). - **The model extent is cached** (it sets the view's depth range and was recomputed from every mesh several times per frame). With 49 bodies the frame was already 2.8 ms (hover) before these changes. The remaining frame time with the Browser shown is mostly the Browser listing every body row. GPU ID-buffer picking was not needed at this size. ## Browser Same model and method (400 bodies, Bodies folder open, 60 hover moves over the viewport): | | median | p90 | |---|---|---| | Browser shown, before | 1.95 ms | 2.71 ms | | Browser shown, after | 0.72 ms | 1.19 ms | | Browser hidden | 0.30 ms | 0.72 ms | What changed: rows scrolled out of view only reserve their height. They register no widgets and paint nothing (no text layout, icons or eye/fold buttons), so the cost follows the rows on screen rather than the rows in the tree. Scrolling, Find in Browser and drag and drop still see every row's place. # Recompute performance `solvecraft-cli bench-edit