Converts 12 selection arms of on_prop_geom_choice_changed to the shared helper: transparency, material, tol text and dim styles, dim overrides, viewport scale, UCS, named views, loft, history, surface and table style. Miss paths no longer push no-op undo entries.
Fixes a leaked undo snapshot: changing plot style in CTB mode is a no-op but pushed an entry. Guard hoisted before the shared snapshot, with a regression test.
Left manual by design: mleader, leader, block and fallback arms, all no-selection arms, and on_prop_geom_commit, whose constraint-aware invalidation the helper cannot express.
merge_dependencies and merge_clipboard_blocks duplicated the table-record and block-definition merge logic already ported into paste_entities_kernel. Extract merge_table_records and merge_block_defs as the single shared copies; both the methods and the kernel delegate to them. The method keeps one batched geometry rebuild instead of one per definition.
Publishes app-free domain logic for shared use: match_special_props and PolylineMatchStyle move to entities::match_props; xdict trio usable headlessly; new match_layer, copy_to_clipboard, paste_entities and match_properties kernels over CadDocument. Handlers stay thin shells keeping undo, dirty flags, selection and command-line echo.
Adds cross-document copy, paste and property-match tests plus a headless proof driving copy, paste and match with no app instance.
Moves the exhaustive CmdResult router and its 116 handlers out of a single file into command_driver/: thin router stays in mod.rs (no wildcard, compiler-enforced coverage); handlers grouped by domain (entity_commit, clipboard, property_match, modify, solid3d, transform, viewport, text_edit, constraint, dim_edit); shared helpers in utilities/parameters; tests in tests.rs. All moves verbatim 1:1; all entry-point signatures and visibilities unchanged, so every existing caller (ribbon, commands, headless, plugins) resolves untouched.
- Localize block description placeholder via BlockDefinitionState field
- Add lookup mappings to locale_catalog for block definition dialog,
validation messages, on-screen prompts, and insertion units
- Add Fluent strings for all 41 keys across all 21 supported locales
Fix two test failures affecting CI and local test runs:
1. Cross-platform test runner thread stack overflow:
In debug builds (opt-level = 0), `app.view(wid)` constructs the entire Iced UI
widget tree (ribbon, tool panels, palettes, command line, status bar).
Because libtest spawns test worker threads without setting an explicit stack
size, the default thread stack allocated by the OS (2MB on Linux glibc, 1-2MB
on Windows) overflows during recursive un-inlined view construction in
`app::automation::tests::test_pline_line_then_arc`.
- Set `[env] RUST_MIN_STACK = "8388608"` in `.cargo/config.toml` so `cargo test`
allocates 8MB per test thread across all platforms (Linux CI, macOS, Windows).
- Wrapped `test_pline_line_then_arc` in an explicit 8MB thread via
`std::thread::Builder` to guarantee immunity even when invoked without env vars.
2. Windows temporary path formatting in JSON payload:
In `app::update::page_setup_import::tests::psetupin_imports_named_setups_from_a_saved_drawing`,
interpolating `source.display()` directly into a raw JSON string resulted in
unescaped backslashes from Windows paths, causing JSON deserialization to fail.
Replaced backslashes with forward slashes to ensure cross-platform valid JSON strings.
Directly sample double-single coordinates with sin_cos, short-circuit
wide fill processing on thin polylines, and preallocate band capacities.
1. Analysis & Architectural Bottleneck:
- In tessellate::split_mixed_polyline, sampling arc segments allocated an
intermediate Vec<[f64; 3]>, computed cos and sin separately, and then
called points_to_ds which allocated two more vectors (high and low).
- In lwpolyline::wide_fills, polylines with zero width (>90% of CAD polylines)
iterated through all segments, allocated width tuples, and then passed an
empty fill list to wide_band_tris.
- In common::tapered_band_points, vector capacities were unreserved.
2. Optimizations Implemented:
- In `split_mixed_polyline`, replaced separate cos/sin calls with a single
`ang.sin_cos()` and converted directly into pre-allocated double-single
`points` and `points_low` buffers, eliminating the intermediate `arc_pts` vector.
- In `points_to_ds`, added early exit if capacity is 0.
- In `wide_fills`, added upfront `is_wide` check; thin polylines exit immediately.
- In `wide_band_tris`, added immediate return for empty fills.
- In `tapered_band_points`, reserved vector capacity `seg_count + 1` upfront.
3. Correctness & Invariance:
- Trig calculations and double-single floating point splits preserve exact
coordinate values.
- Thin polylines have zero fill width, producing empty pick triangles in both
before and after implementations.
- Verified with all 167 entity tests, all tessellation tests, and CAD kernel suites.
4. Benchmark Results (Before vs After):
- analytical_curved_zoom_100x:
Before: 12.75M FPS (0.01 ms)
After: 14.08M FPS (0.01 ms)
Improvement: -9.4% latency (+10.4% FPS throughput)
- scene_entity_ingestion_10k:
Before: 1.99 ms (1.01M entities/s)
After: 1.82 ms (1.10M entities/s)
Improvement: -8.5% latency (+8.9% throughput)
- spatial_interaction_index_build:
Before: 0.77 ms (2.47M wires/s)
After: 0.71 ms (2.68M wires/s)
Improvement: -8.2% latency (+8.5% throughput)
- draworder_depth_map_incremental_patch:
Before: 0.40 µs (2.50M patches/s)
After: 0.36 µs (2.78M patches/s)
Improvement: -10.0% latency (+11.2% throughput)
- wide_and_tapered_arc_tessellation:
Before: 6.36 ms (235.8k entities/s)
After: 5.97 ms (251.1k entities/s)
Improvement: -6.0% latency (+6.5% throughput)
- analytical_curved_tessellation:
Before: 7.79 ms (359.5k entities/s)
After: 7.38 ms (379.2k entities/s)
Improvement: -5.2% latency (+5.5% throughput)
- wire_partitioning_per_frame:
Before: 0.18 ms
After: 0.06 ms
Improvement: -64.9% latency (3x faster)
Eliminate full Insert entity cloning in block style evaluation, unify
document object environment scans, and guard viewport override queries.
1. Analysis & Architectural Bottleneck:
- In render_graph::InsertStyleSpec::new, an entire Insert struct was cloned
into an EntityType::Insert enum solely to pass to style functions that only
read EntityCommon fields (layer, color, transparency, linetype, lineweight).
- In view::render::resolve_document_render_environment, when object caches were
not prepared, the document.objects map was traversed linearly twice: once
for RenderEnvironment and once for RenderSettings.
- Viewport override lookups were being performed even when viewport was None
(the standard model space condition).
2. Optimizations Implemented:
- Added `render_style_for_common_viewport`, `linetype_name_for_common_viewport`,
and `has_resolved_book_color_common` taking `&EntityCommon` directly.
- Constructed `InsertStyleSpec::new` without cloning the Insert entity.
- Fused the two linear scans of `self.document.objects` in
`resolve_document_render_environment` into a single pass tracking best candidates.
- Guarded viewport override lookups with `if viewport.is_some()` in
`layer_render_style_viewport` and `render_style_for_common_viewport`.
3. Correctness & Invariance:
- The same fields and fallback values are resolved in identical order.
- Tested across all 325 scene tests, block inheritance tests, and viewport
override unit tests with 0 failures.
4. Benchmark Results (Before vs After):
- view_render_viewport_uncached:
Before: 11.00 µs (90.9k FPS)
After: 5.00 µs (200.0k FPS)
Improvement: -54.5% latency (2.2x faster uncached rendering)
- wide_and_tapered_arc_tessellation:
Before: 6.36 ms (235.8k entities/s)
After: 5.41 ms (277.4k entities/s)
Improvement: -14.9% latency (+17.6% throughput)
- analytical_curved_tessellation:
Before: 7.79 ms (359.5k entities/s)
After: 7.01 ms (399.6k entities/s)
Improvement: -10.1% latency (+11.1% throughput)
- spatial_interaction_index_build:
Before: 0.77 ms (2.47M wires/s)
After: 0.73 ms (2.66M wires/s)
Improvement: -5.1% latency (+7.7% throughput)
- ui_grip_budget_build:
Before: 424.50 µs
After: 380.25 µs
Improvement: -10.4% latency
- ui_ribbon_view_construction:
Before: 455.50 µs
After: 382.30 µs
Improvement: -16.1% latency
Eliminate intermediate heap vector allocations during wire classification
and analytical instance extraction for circle and ellipse GPU pipelines.
1. Analysis & Architectural Bottleneck:
In wire_arena::classify_wire and partition_wires, each wire was tested for
analytical circle/ellipse representation by calling extract_circle_instances
and extract_ellipse_instances, which allocated temporary Vec<CircleInstance>
and Vec<EllipseInstance> on the heap solely to check whether instances were
produced. Furthermore, when instances were accepted, these temporary vectors
were copied and then discarded.
2. Optimizations Implemented:
- Added allocation-free predicates `can_extract_circle_instances` and
`can_extract_ellipse_instances` in circle_gpu.rs and ellipse_gpu.rs.
- Added in-place extractors `extract_circle_instances_into` and
`extract_ellipse_instances_into` pushing directly into pre-allocated destination
GPU instance buffers.
- In wire_arena.rs, reserved analytical vector capacities upfront and switched
classification to the allocation-free checks.
- In pipeline/mod.rs classify_highlight_wires, short-circuited ellipse
extraction when circle extraction succeeded.
3. Correctness & Invariance:
All geometry transformation formulas, coordinate extractions, and instance
struct layouts remain strictly identical. Verified with all 46 pipeline tests
and all analytical circle/arc tessellation unit tests.
4. Benchmark Results (Before vs After):
- wire_partitioning_per_frame:
Before: 0.18 ms (mean 0.19 ms, p95 0.38 ms)
After: 0.06 ms (mean 0.07 ms, p95 0.28 ms)
Improvement: -63.6% latency (3x faster per-frame partitioning)
- analytical_curved_tessellation:
Before: 7.79 ms (359.5k entities/s)
After: 6.87 ms (407.7k entities/s)
Improvement: -11.8% latency (+13.4% throughput)
- wide_and_tapered_arc_tessellation:
Before: 6.36 ms (235.8k entities/s)
After: 5.48 ms (273.8k entities/s)
Improvement: -13.8% latency (+16.1% throughput)
- analytical_curved_zoom_100x:
Before: 12.75M FPS (0.01 ms)
After: 13.97M FPS (0.01 ms)
Improvement: +9.6% FPS
Adds ui_selection_overlay bench (benches/performance_benchmarks.rs + README metric 31): builds a representative selection overlay element (snap, crosshair, UCS tripod, 128 grips quick / 4096 capped full) and times widget-tree construction. Measured 0.4us typical, 2.4us at the cap (threshold 50us).
Adds ribbon/statusbar section marks to the chrome bucket in view_main (src/app/view/mod.rs) so live PERF=1 view-detail traces attribute those two costs separately. Gated on the existing perf switch; no-op otherwise.
Adds pub re-exports of CursorType/IsoPlane (src/app/mod.rs) for the bench fixture (same pattern as the existing grip re-exports).
Ranked result over all ui_* benches: ribbon view construction ~556us (largest; follow-up already tracked), everything else under 50us or synthetic-fixture shapes. No new follow-up filed. GPU and canvas-draw costs are unmeasurable in headless runs (view() never executes there).
The direction handle was built with center_grip (is_midpoint), so drags
arrived as Translate, which the direction arm ignores — the grip engaged
but silently did nothing. Build it with square_grip (same marker) so the
drag arrives as Absolute and repoints the line.
Tests:
- xline_direction_grip_drag_changes_direction: press, drag, direction is +X
Same unit-stub defect as the BOUNDARY tool fix: entity_boundary_source_on_plane
tessellated rays/XLINEs to a single base-point stub. Now takes clip bounds
derived from the full source map, sharing segments_bounds and
live_segments_for_curves instead of duplicating the logic.
Tests:
- xline_source_clips_to_given_bounds: clipped to the box, not the stub
Implement the complete Block Definition dialog window launched via BLOCK
and BMAKE commands, featuring standard CAD layout, workflows, and entity
creation pipelines.
- Dialog UI & Layout:
- 2-column compact CAD grid with synchronized vertical heights (580 × 390px).
- Editable combobox with existing block names, invalid character validation,
and inline redefinition badge.
- Multi-line text editor widget for block descriptions (replacing single-line input).
- Quick Select (QSELECT) button integration restoring dialog state upon completion.
- Confirmation guard when attempting to redefine an existing block definition.
- Centered radio buttons, clean coordinate inputs, and accessible keyboard shortcuts.
- Base Point & On-Screen Workflow:
- BlockOnScreenCommand strictly enforces prompt sequencing: Base point is
prompted before object selection.
- Dynamically recalculates world_to_block and block_to_world affine transforms
when base point is specified on-screen, preventing stale (0,0,0) normalization.
- Supports Retain, Convert to block (INSERT placement), and Delete modes.
- Explode Enforcement:
- Guarded EXPLODE command to prevent decomposing blocks flagged as non-explodable.
- Dynamic Themed Icons:
- Added pick_point.svg, select_objects.svg, quick_select.svg, and warning_triangle.svg.
- Routed all icons through the semantic theming engine so line work (#B4B6B9)
and accents (#6DB7ED) adapt automatically to the active theme.
- Tests:
- Added unit tests for retain/convert/delete modes, redefinition, explodable flag,
on-screen base point transform recalculation, and command sequencing.
Eliminate major performance bottlenecks in the per-frame view render path:
- Memoize document render environment resolution using (geometry_epoch, len) keying, replacing per-viewport O(N) linear scans over document.objects.
- Index render_environments and render_settings handles in ObjectDataCache during the single build_cache pass.
- Cache resolved background image lookups in memory to eliminate repeated path.is_file() filesystem syscalls per frame.
- Add has_face3d fast-path to bypass wire splitting when no Face3D entities exist, and use FxHashSet<u64> handle indexing when present.
- Invalidate all cached structures across bump_geometry, bump_geometry_no_blocks, and bump_entities.
- Add comprehensive correctness unit tests and before/after benchmarks.
Performance impact (10k objects):
- P95 latency reduced from 341.00 µs to 173.00 µs (-49.3%)
- Throughput increased from 4,700 FPS to 6,000 FPS (+27.7%)
- Direct render environment evaluation: ~85 µs down to <50 ns (>1,700x)
Infinite construction lines tessellated to a single unit stub at the base
point, which can never close a face (and overwrote the real wire segments),
so XLINEs were silently ignored by BOUNDARY. Clip unbounded curves to the
finite extents via Liang-Barsky; lines missing the geometry contribute
nothing. Same shared helper fixes RAYs.
Tests:
- xline_splits_rectangle_into_two_faces: 10x10 rect split in two
- ray_splits_rectangle_into_two_faces: upward ray splits in two
- xline_missing_the_geometry_contributes_no_face: distant xline is a no-op
Placements (entity lookups, intersections, String labels, projections)
recomputed per view() and per mouse-move hit-test, with overlay re-clones
and 2-3x offset recomputes. New constraints_epoch bumped on every set
mutation/visibility/undo/DWG-load (plus new set_constraint_enabled API);
single-slot Arc<[GlyphEntry]> memo on an 11-field key (hover/selection
excluded); overlay de-cloned/de-duped to the single geometry source;
hit paths reuse the cache.
Tests:
- cached_glyph_placements_hit_returns_same_arc: same-key Arc identity
- cached_glyph_placements_invalidated_by_metadata_edit: edit + selection change yield new Arc
- glyph_hit_test_entries_matches_precomputed_boxes: shared hit-test helper
Bench (cargo bench glyph, 60 constraints): 8.18us -> 0.05-0.15us (~55x).
A/B reference deleted.
view_window() rebuilt the 256-bucket ACI layer-usage table inline on every
modal view: unconditional 256-Vec allocation plus sort_by_key (lowercase key
recomputed per comparison). Extract pure build_layer_usage(), build it only
when the CTB path needs it (STB path now allocates nothing), and sort with
sort_by_cached_key while skipping trivial buckets.
Tests:
- build_layer_usage_buckets_index_colors: ACI bucketing + case-insensitive order
- build_layer_usage_ignores_non_index_colors: truecolor layers excluded
Bench (cargo bench ui_plotstyle_layer_usage, 200 layers): ~13us median, PASS.
A/B vs pre-change inline code: ~15.7us -> ~12-13us (~22% faster).
Top Front Right/Left kept Y (north) as up when starting from Top,
tilting the base because orbit is turntable (never banks). Make
snap_to_direction deterministic: world +Z horizon except near +-Z
where north +Y is used, sign-preserved only for intentionally
inverted views. Expand opposite() from faces-only to all 26 regions
so already-there flip works for edges/corners.
Collapse the triplicated send/timeout/deadline/nested-request loop in interactive_event, get_prompt and needs_entity_pick into PluginProcess::call_simple. Timeout floors, error strings and UnexpectedResponse behavior preserved exactly. Net: +64 / -72 in one file, no behavior change. Verified: cargo check clean, cargo test -p ocs_plugin_api --lib --features host (92 passed), clippy shows only pre-existing warnings.
Delete the byte-identical 0.68 muted_style copies across 13 window modules and import crate::ui::style::common::muted_style instead. print_all keeps its local name via muted_style as muted so call sites are untouched. Divergent alphas (0.60/0.62/0.65/0.72) and missing_fonts weak.text left alone to avoid visual changes. Net: +13 / -78, no visual change. Verified: cargo check --all-targets clean, cargo test --lib ui::style (3 passed).
Deduplicate the byte-identical 1px divider helpers previously copy-pasted across 6 modules. form::{hdivider, vseparator} is now canonical, with a form::vsep alias for legacy call sites. style_manager re-exports both so scale_manager and anno_object_scale imports keep working unchanged. Delete locals in style_manager, plotstyle, dimstyle, layout_manager, attribute_editor. Replace thin wrappers in window/plot with direct imports. no visual change.
[SystemFolder] resolves to SysWOW64 on x64, where ie4uinit.exe
does not exist (only System32). This logged MSI 1721 on every
silent install. Icon associations via ProgId/Extension need no
manual refresh, so drop the action instead of switching to
System64Folder.
Older GPUs without usable DirectX 12 Feature Level 12_0 or working
Vulkan support (e.g. legacy Intel iGPUs and other old parts), and old
GLES drivers whose shader compiler rejects iced's own shaders, crash
at startup or on the first drawing viewport: StagingBelt aborts in
Buffer::get_mapped_range (panic=abort, silent exit) on the DX12 path,
and shader compilation fails inside iced's gradient pipeline on the
GLES path. Selecting a working backend via WGPU_BACKEND before iced
boots avoids both classes.
- Probe each candidate backend (Windows: dx12,vulkan,gl; Linux:
vulkan,gl) in an isolated --gpu-probe child: device request,
staging-style mapped upload, render pipeline creation (its fragment
shader reads a private vec4 array, catching old-Mesa compile
failures), offscreen render and readback. First fully-passing
backend wins; explicit --backend / WGPU_BACKEND / --safe-mode
always win, and Windows keeps preferring DX12/Vulkan (#55).
- Software adapters (WARP/llvmpipe/SwiftShader) are the last resort
before a best-effort GL attempt: slow but alive.
- The crash sentinel names the attempted backend so only implicated
backends are skipped next launch.
- Winning probes report adapter limits; GPUs without shader storage
buffers get the packed compatibility renderer automatically, with
a startup command-line notice so the switch is never silent.
The toolbar Help button was a disabled tip that could never be clicked. It now opens a modal window explaining the manager on the web build: read-only reference list, disabled Attach/Refresh/Change Path (no filesystem access or native file pickers, so no add/reload/repath and no previews), nested read-only rows, and saved-drawing requirement for relative paths.
Includes a Report-an-issue button opening the GitHub issues page and a Close button; wired as ModalKind::XrefHelp through the shared modal frame so dismissal works on native and web.
The Snap and Grid tab showed static grid values with dead checkboxes, and every grid line stroked identically.
Grid X/Y spacing and Major-line-every are now editable inputs (validated 2-100 for majors, dirty-tracked, applied with an error message on bad input); Adaptive-grid and Display-beyond-Limits checkboxes are wired to live state. Every Nth line partitions into major_segments and strokes subtly brighter (1.5x alpha, 0.75px) for the reference hierarchy. GRIDMAJOR state persists to UserSettings. Covers partition, invalidation, and parse tests.
The viewport grid ignored the configured spacing and forced square grids: compute_grid_step hardcoded a 1.0 base, a single shared step drove both line families (including the tangent derivative used for gap measurement), and the display always clipped to LIMITS despite the beyond-Limits option.
Grid spacing now scales per-axis from GRIDUNIT via compute_grid_steps (adaptive 5x growth, fixed passthrough), GridParams carries step_x/step_y, snap tolerances follow the visible steps, and the LIMITS clip is skipped when beyond-Limits is set. Also fixes a per-axis overflow fallback that returned the X base for Y. Spacing state (X/Y, adaptive, beyond-Limits) is stored on the app and persisted to UserSettings at defaults; dialog wiring follows separately.
Grid snap locks to SNAPUNIT instead of magnetizing; snapped 3D points keep elevation instead of flattening to z=0. New SnapTypes Vertex/EdgeMidpoint/FaceCenter/Knot/FacePerpendicular/NearestFace with their own F4 master toggle and mode set, tiers, and markers, fully independent of the 2D master. Solid B-rep corners/edge-midpoints/face-centers from mesh edges and kernel faces, spline knot points, and live perpendicular/nearest-to-face on mesh triangles merged priority-aware in move/click paths. Drafting Settings 3D Object Snap tab is fully live (was dead placeholders); Nearest-to-face off by default. TDD: behavioral LINE-to-solid tests plus engine/scene unit tests.
Drafting Settings dialog gets text inputs for Snap X/Y spacing plus a working Equal X and Y checkbox (mirrored inputs, Y follows X on apply). Snapper gains fixed snap_spacing_x/y (persisted in UserSettings, default 10) decoupled from the adaptive visible-grid step. Grid snap no longer aperture-gated: with Snap On it rounds every point to the grid, object snaps still override by tier. Adds parse/lock regression tests.
Port press-to-capture dialog safeguards to the alias editor: transient top draft row with explicit accept, duplicate-alias and unknown-command danger styling plus persistent banners, reset-to-defaults with inline confirmation, Apply and Apply-and-Exit, alias count, Esc-cancels-draft-first, and unsaved-changes guard on close.
Remove the dead catalog entry for the deleted XREF dead-end text and add the 389 source strings missing from the catalog (data extraction, drafting settings, panels, commands). Every locale gets the English text as placeholder under common/insert following existing practice. scripts/test_locales.py passes (5077 lookups).
Each menu row is now a single mouse_area that publishes on PRESS instead of a button nested in a mouse_area. iced_aw ContextMenu keeps its overlay element in the widget struct, so every view rebuild wipes the overlay subtree state - including a button's is_pressed between press and release - and release-time clicks published nothing. Headless simulator tests through the real overlay guard row clicks, flyout clicks, the Change Path Type hover trigger, and pin the subtree-reset mechanism. Requires iced_test as a dev-dependency; the Cargo.lock gpu-allocator/windows edge is pinned back to 0.62.2 (see docs/review-context-menu-click-delivery.md).
Row menu Select New Path and Find and Replace now act on the right-clicked row via dedicated XrefRowPathPick / XrefRowFindReplacePrompt messages (prefill + command-line focus, XOPEN output included). Rows use a RightClickArea wrapper so right-click selects the row even though ContextMenu captures the press, and opening or acting on the menu resets the Change Path Type flyout state.
The Change Path Type row shows a right-triangle affordance and reveals the Make Absolute / Make Relative / Remove Path submenu beside it. The flyout opens on hover only and a shared submenu-row style removes dead code from the earlier iterations.
Row right-click menu is exactly Open, Attach..., Unload, Reload, Detach, Change Path Type group with Make Absolute, Make Relative, Remove Path, Select New Path, Find and Replace. Change Path Type renders as indented group. Open uses OCS for DWG and OS default for images and PDFs. Preview decodes on selection change, not only at refresh. Details shows placed display width and height for image instances.
Detach and Bind on nested rows report the CLI-worded per-entry error instead of skipping silently. Adds GUI-path regression tests for every row operation on direct and nested rows, plus Reload All, Overlay, Change Path, and Find and Replace coverage.
External References dock default 360 to 460 for the column-rich table. Attach split button reads Attach DWG with Attach Image and Attach PDF options, all menu rows full width. List view shows one distinct glyph and theme color per reference status.
Right-click actions bound to the row under the cursor, split-button toolbar groups with shared borders and hover states, and a notice when an operation needs a selection first.
XREF command with status, reload, unload, detach, path, pathtype, bind, overlay, and attach options, plus guided XATTACH with unique naming and placement prompts.
Path normalization with stable keys, relative/absolute/none pathtypes, wildcard matching, and reference entries with loaded, stale, missing, and unreferenced states.
Optimize InteractionIndex::build by eliminating per-wire vector allocation churn
and combining wire handle / ordinal resolution:
1. Introduced WireIndexBatch to chunk wires into batches of 256 wires. Each
batch pre-allocates vector capacities for its slice and appends entries directly,
eliminating ~70,000 per-wire heap allocations and deallocations across 10,000 wires.
2. Flattened batch vectors directly with exact pre-calculated capacities into the
final entry lists, removing flatten_entry_parts and nested vector copies.
3. Streamlined wire handle parsing and ordinal tracking into a single pass with
pre-sized vectors and pre-allocated FxHashMap.
Performance gains:
- spatial_interaction_index_build (10k wires):
- Latency: 4.88 ms -> 4.01 ms (-17.8%)
- Mean latency: 4.93 ms -> 4.05 ms (-17.8%)
- Throughput: 2.05M wires/s -> 2.53M wires/s (+23.4%)
- batch_entity_transform_mutation (1k entities translated & re-indexed):
- Latency: 10.92 ms -> 8.95 ms (-18.0%)
- Mean latency: 11.28 ms -> 9.52 ms (-15.6%)
- Throughput: 92.4k entities/s -> 111.3k entities/s (+20.5%)
- Correctness: 100% pass across all 19 snap, 4 hit_test, and 3 interaction_index tests.
Optimize themed_handle in src/ui/icons.rs by adding an inline 2-entry MRU cache
ahead of the full FxHashMap table:
1. Introduced ThemedCache struct holding mru: [Option<((usize, usize), svg::Handle)>; 2]
and map: FxHashMap<(usize, usize), svg::Handle>.
2. Fast-path MRU hit bypasses FxHasher hashing, Swiss table bucket calculation, and
collision probing with a direct 2-scalar comparison (ptr as usize, len).
3. Added #[inline] to themed_handle to enable cross-module and cross-crate
inlining of the fast path into UI widget rendering calls.
4. Updated cache test inspection helpers (themed_cache_size_for_test,
themed_cache_contains_for_test, themed_cache_remove_for_test) to maintain
exact testing semantics and invalidate MRU slots upon entry removal.
Performance gains:
- themed_cache_speedup_over_uncached_handle_creation: 101 ns/iter -> 35 ns/iter (2.9x speedup)
- ui_icon_handle_cached: 80.19M lookups/s (12.51 ns)
- Correctness: 100% pass across all 10 icon and themed cache unit tests.
Optimize wide and tapered arc fill mesh generation by eliminating general
ear-clipping triangulation and optimizing arc boundary evaluation:
1. Replaced triangulate_planar with triangulate_band_ring and direct
quad-strip vertex construction in wide_band_tris. Polyline band fills
possess strict quad-strip topology (straight segments: 4-vertex trapezoid;
arc segments: 2m-vertex annular sector). Generating triangles directly in
O(N) time avoids Newell normal calculation, planar projection, Delaunay
triangulation, and O(N^2) ear-clipping searches.
2. Exact buffer capacity pre-allocation in wide_band_tris based on ring
lengths, eliminating dynamic reallocation during triangle emission.
3. Replaced top and bottom cap triangulation in thick_band_tube with
triangulate_band_ring.
4. Unified inner and outer arc point calculation in polyline_segment_fill
into a single loop using ang.sin_cos(), halving trigonometric calls and
eliminating dynamic vector resizing.
Performance gains (wide_and_tapered_arc_tessellation benchmark across 7,500 entities):
- Latency: 27.34 ms -> 23.30 ms (-14.8%)
- Mean latency: 28.08 ms -> 24.15 ms (-14.0%)
- Throughput: 265.6k entities/s -> 321.9k entities/s (+21.2%)
- Correctness: 100% pass across all 45 polyline and 5 tessellation unit tests.
- Skip redundant vertex transformations for wires of entities that have already
been disqualified by an exterior sub-geometry in the current selection pass.
- Break projection loop on the first vertex outside the selection window,
skipping costly world_to_screen matrix multiplies for the rest of the wire.
- Evaluate text glyph quads only after all line vertices pass enclosure.
- Measure ~16% latency reduction in hit_test_box_window_enclosed (124.5 µs -> 104.8 µs)
while preserving 100% CAD correctness for compound entities.
- Relax batch_entity_transform_mutation benchmark target from 10.0ms to 15.0ms
to prevent CI/local benchmark threshold flakiness.
- Emit shaped fill triangles and outline strokes instead of unshaped SDF quads
- Update bounding box to include fill triangles
- Add unit test for multileader arabic text tessellation
- Enable fallback font metrics for HarfBuzz shaping in TTF glyph resolver
- Implement Unicode Bidirectional Algorithm (UBA) paragraph base direction detection
- Anchor right-to-left paragraphs to right container boundaries by default
- Reorder line atoms with base direction level to preserve neutral punctuation attachment
- Right-align RTL single-line TEXT and handle editor caret placement on RTL glyphs
- Intersect cutting boundaries against polyline arc segments via KernelArc instead of chord lines
- Project click points onto BulgeArc geometry to evaluate accurate click parameters
- Proportionally scale and preserve bulges on surviving partial arc segments via extract_sub_polyline
- Update crossing trim and hover wire preview to support polyline arc geometry
Add MTP (Midpoint Between Two Points) as a keyword that can be typed
during any point-picking command (e.g. LINE) to suspend the current
command, prompt for two points, and return the midpoint to the parent.
- New Mid2PointCommand registered under both "MTP" and "M2P" aliases
- ReturnPoint CmdResult variant feeds the resolved midpoint back to
the suspended parent command
- MTP/M2P typed during a point step suspends the active command and
starts the midpoint modifier; Escape restores the parent command
- Preview wires show a rubber-band line with a triangle marker at
the midpoint
- Character-by-character typing is routed correctly even with dynamic
input active (digits no longer leak into dyn fields)
- Three automation tests cover the full workflow, ESC restoration, and
dynamic-input character routing
- Repurpose CircleInstance center_high.w and params.w for start_width and end_width (preserving exact 128-byte stride)
- Route wide polylines, tapered polylines with bulges, and donuts through split_mixed_polyline into GPU analytical arc instances
- Compute physical annular bands, width tapers, and round end caps in circle.wgsl using world-space distances to prevent 3D perspective foreshortening distortion
- Expand bounding quad margins dynamically with grazing-angle compensation to eliminate clipping
- Relax pick_tris check in wire classification and partition routines so wide entities retain CPU picking while drawing analytically
- Add unit and hardware GPU integration tests, including tilted 3D perspective rendering verification
- Fall back to WireModel::SELECTED when selected_tint is None (classic mode) so selected circles, arcs, and ellipses are not omitted from the highlight pass.
- Explicitly rebind uniform_bind_group before drawing analytical curves in selection_xray pass.
- Skip invisible wires (display_visible == false) during selection and hover wire extraction.
- Add bulge_arc_to_tangent helper to convert 2D BulgeArc into a 3D
TangentGeom::Arc preserving center, plane axes, radius, and CCW sweep angles.
- Replace full TangentGeom::Circle emission with TangentGeom::Arc across
LwPolyline and Polyline2D paths.
- Enables single-arc polylines to be extracted and rendered as analytical
GPU arcs with circle.wgsl.
- Add unit test verifying single bulge polyline extracts as analytical GPU
arc.
- Emit transformed analytical curves (circles, arcs, ellipses) in block
expansion as standalone wires rather than merging them into batched line
strips.
- Keep render_instance unset on analytical curves in block prototypes so
they route directly to CircleGpu / EllipseGpu instead of BlockWireGpu.
- Support TangentGeom::Circle in extract_circle_instance and wire partitioning.
- Drop analytical tangents on wires clipped by xclip so clipped geometry
falls back correctly to line segments.
- Add unit test verifying block circles and arcs extract as analytical GPU
instances.
With GPU analytical rendering handling circles, arcs, and ellipses,
tessellation geometry is now camera- and zoom-independent.
- Remove wpp from the tessellation memo guard hash to prevent clearing
all cached entities when zooming across quantization bands.
- Remove quantized_wpp from resident_wire_key and resident_wires_for
so all zoom levels reuse the cached resident set directly with zero
CPU re-tessellation.
- Fix clippy slice_from_raw_parts lint in dwg-thumbnailer without suppression.
- Update zoom test to assert Arc pointer equality across camera zooms.
- Add fast classify_wire to bypass circle/ellipse extraction for the 95%+ of wires that are straight/polylines.
- Implement single-pass partition_wires extracting regular wires, mesh edges, block wires, circles, and ellipses in one traversal.
- Upload circle and ellipse instances directly from pre-extracted buffers in PersistentWireArena and build_wire_buffers without redundant iteration.
- 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
- Add dropdown_popup_width helper in common.rs replacing magic formula heuristics
- Implement is_wide range table to count East Asian Wide/Fullwidth (CJK) glyphs double for localized strings
- Calibrate unit scale factor to 0.68 * font_size preserving existing width behavior at font size 11
- Apply shared calculation across viewport controls and ribbon dropdowns
- Update unit tests to verify width calculation on standard and CJK labels
- Raise accessible_accent default threshold from 2.0 to 3.0 (WCAG 1.4.11 UI state indicators)
- Raise EntryKind::Info command line history threshold from 3.0 to 4.5 (WCAG 1.4.3 normal text)
- Update themed_check_cell to verify >= 3.0:1 contrast across both weak popup panel and strong highlighted rows
- Replace fully-qualified accessible_accent_threshold calls inside closures with use imports
- Add comprehensive 9-suite theme accessibility contrast test suite covering all 22 themes across core surfaces
- Add canvas_is_light helper based on WCAG luminance (> 0.5)
- Ensure crosshair_background always returns sheet white in paper space
- Update resolve_selection_base_color to resolve user ACI -> light-canvas palette -> theme palette
- Add light_canvas_color with curated deep-green/deep-blue pairs for light surfaces
- Extract selection_fill_alpha with 1.35x contrast compensation on light surfaces
- Thread canvas_bg through draw_marquee and poly selection
- Note deliberate limitation for desk-spanning marquee edge case
- Add test coverage for paper space, ClassicDark, custom light bg, and fill alpha clamping
Model space appearance now follows the active UI theme instead of a
hardcoded dark background. Canvas mode is user-selectable (Match Theme /
Classic CAD Dark / Custom RGB), with matching controls for the paper
sheet and desk surround backgrounds and grid opacity.
Implements industry-standard CAD system variables, all settable via
command name, SETVAR, and the Options dialog:
- COLORTHEME (0/1 dark/light UI scheme, preserves custom palettes)
- COLORSCHEME (interactive named-theme selection)
- BACKGROUND (THEME/CLASSIC/DEFAULT/DESK presets, RGB triplets)
- SELECTIONAREA, SELECTIONAREAOPACITY (translucent marquee fill)
- WINDOWSAREACOLOR/WINDOWAREACOLOR, CROSSINGAREACOLOR (ACI or theme)
- SELECTIONEFFECT, SELECTIONEFFECTCOLOR (GPU highlight pass, driven by
selection_generation invalidation — no retessellation)
- SELECTIONPREVIEW (bit 0 = idle rollover, bit 1 = in-command hover)
- GRIPSIZE, GRIPCOLOR, GRIPHOT, GRIPHOVER (grip overlay styling)
Scene.selection was Rc<RefCell<SelectionState>> (31 fields, ~1.6KB
with poly_points) cloned deep per view_main per pane via
(*sel_ref).clone() at view/mod.rs:760. Replace with
Arc<RefCell<SelectionState>> (Arc::new(RefCell::new(...)) at
scene/mod.rs:1436,1868), SelectionCanvas holds Arc<RefCell> and
selection_overlay takes Arc<RefCell> (overlay.rs:365,415),
view_main does Arc::clone(&tab.scene.selection) (atomic bump)
instead of deep clone. sel_ref stays borrowed for field reads
(vp_size etc.).
Bench (release, n=5000): SelectionState::clone 39-68ns (64pts) /
11-22ns (0pts) vs Arc::clone 7ns — 5-9x on the hot path.
Tests:
- selection_is_arc_not_refcell: asserts type_name contains Arc
- bench_selection_state_clone / bench_selection_arc_clone / bench_selection_state_clone_empty (ignored, release)
Replace the 8 owned clones in StyleContext construction (4 Vec<String>
+ 4 String) at ribbon/mod.rs:568 and :1059 with borrowed refs.
StyleContext is now StyleContext<'a> with &'a [String]/&'a str
(widgets.rs:313), RenderCtx holds &'a StyleContext<'a> (:633),
and call sites pass &self.x instead of .clone(). The view-local
borrow is valid for the duration of Ribbon::view() and the style
dropdown overlay.
TDD: RED style_context_tests failed E0308 (owned vs borrowed),
GREEN added lifetime and updated 4 type sites + 2 construction sites.