Commit Graph
441 Commits
Author SHA1 Message Date
Hakan Seven 9e90e50662 feat(io): add user-approved recovery 2026-08-03 12:47:48 +03:00
Hakan Seven e102887000 fix(move): hide source during preview
Refs #521
2026-08-02 16:32:47 +03:00
Hakan Seven baf9b240a4 feat(annotation): show all scales on highlight 2026-08-02 14:37:12 +03:00
Hakan Seven e03e091b3c feat(annotation): integrate object contexts 2026-08-02 12:00:15 +03:00
Hakan Seven f39481ac49 fix(scene): isolate layout render sources 2026-08-02 03:02:14 +03:00
Hakan Seven c4c673336c fix(hatch): follow block grip movement 2026-08-01 21:52:25 +03:00
Hakan Seven 7b50d6a5a3 fix(solid): normalize quadrilateral edges
Accept perimeter and DXF Z point orders while keeping screen fill and interaction on the WCS-aware geometry.

Refs #617
2026-08-01 20:31:28 +03:00
Hakan Seven 9382a412df fix(viewport): adapt layout crosshair contrast
Choose the sheet or desk surface under the cursor instead of the active input space so MSPACE stays visible on light paper.\n\nRefs #611
2026-07-31 23:47:41 +03:00
Hakan Seven f0c1be0db1 fix(plot): correct layout hatch output
Keep Layout output on the selected physical sheet and preserve hatch lineweights and plot styles through paper, viewport, and nested block paths.\n\nRefs #607
2026-07-31 23:08:41 +03:00
Hakan Seven 325cd2474c feat(plot): complete plot configuration
- unify Model and Paper Space plot area behavior
- add CTB discovery, read/write, and bundled defaults
- apply scale, render mode, and output options consistently
2026-07-31 13:41:34 +03:00
Hakan Seven bd1b5b4293 feat(plot): complete plot workflow 2026-07-31 12:06:12 +03:00
Hakan Seven 2c64888048 fix(blocks): preserve UCS transformations
Bake BEDIT frames into block-local geometry while keeping model and viewport UCS isolated. Normalize legacy nonzero block origins during load.
2026-07-30 19:50:33 +03:00
Hakan Seven 2688ffbd6b fix(ui): filter scales by drawing units
Refs #578
2026-07-30 11:13:04 +03:00
Hakan Seven cd1378e6b2 fix(viewport): preserve tiled pane controls 2026-07-29 17:21:57 +03:00
Hakan Seven 93b9a840ff refactor(renderer): select paths by GPU limits
Keep storage fast paths on capable adapters and use packed or texture fallbacks only when device limits require them.\n\nCompact mesh-edge varyings to avoid WebGL shader rejection.
2026-07-29 12:49:41 +03:00
Hakan Seven 25166b9508 fix(hatch): preview pattern grip drag 2026-07-28 20:07:10 +03:00
Hakan Seven 8050fc79fd fix(scene): clear selection after erase 2026-07-28 17:35:21 +03:00
Hakan Seven 05b044f367 perf(scene): make geometry edits incremental
Avoid full document, cache, and GPU rebuilds for entity deltas so add, delete, history, selection, and live polyline edits stay responsive on large drawings.
2026-07-28 17:16:27 +03:00
Hakan Seven d5be78a187 fix(selection): separate transient visibility
Keep object isolation out of persisted entity visibility and clear stale grips across block-editor transitions.

Refs #512

Refs #517
2026-07-27 23:36:02 +03:00
Hakan Seven b629dc32de feat(wipeout): add polygon creation modes
Closes #528
2026-07-27 21:50:50 +03:00
Hakan Seven b8889ccd20 feat(mview): add viewport creation options
Support polygonal and object viewports through native clip-boundary handles, including clipped rendering and selection.

Closes #525
2026-07-27 19:28:51 +03:00
Hakan Seven 8018ab1523 feat(view): implement LIMITS command
Closes #478
2026-07-27 16:15:28 +03:00
Hakan Seven 4887a53d66 perf(render): batch and cull mesh instances
Share repeated block geometry across GPU instances.\n\nPatch changed mesh chunks and cull large batches with indirect draws.
2026-07-27 01:11:21 +03:00
Hakan Seven 6fb68d1c7c feat(scene): integrate DWG object data 2026-07-26 23:38:38 +03:00
Hakan Seven eb23715605 feat(scene): integrate standard DWG data 2026-07-26 22:30:14 +03:00
Hakan Seven bca65600cd fix: stabilize web loading and paper rendering 2026-07-25 21:34:29 +03:00
Hakan Seven deec48ad99 perf: keep heavy work off UI thread 2026-07-25 18:49:18 +03:00
Hakan Seven 12bf126646 perf: remove large drawing stalls
Keep resident geometry and GPU resources stable across edits, MSPACE zoom, and Model/Paper switches. Add unified PERF output for tracing remaining costs.
2026-07-25 13:34:48 +03:00
Hakan Seven 2bc4c1c8f3 perf: eliminate initial hover freeze 2026-07-25 10:09:23 +03:00
Hakan Seven 1c7e4bb850 perf: reuse interaction index after edits
Remap immutable base candidates and cache only changed-wire indexes so block-heavy drawings avoid rebuilding millions of primitives on pointer movement. Build 3D BVHs lazily for plan views.
2026-07-25 01:34:49 +03:00
Hakan Seven da34c6817a perf: eliminate dense edit stalls
Keep resident wires, draw depths, GPU slabs, and edit history incremental across entity changes. Defer rollover work to avoid cursor jumps on dense drawings.
2026-07-25 01:06:00 +03:00
Hakan Seven ea93e169ea perf: make edit history incremental
Replace full-document snapshots with Arc first-touch deltas and batch cache invalidation. Reuse interaction and GPU category caches across unrelated edits.
2026-07-24 23:25:33 +03:00
Hakan Seven 90714d34b2 perf: expand spatial interaction indexing
Share subprimitive broad-phase queries across snap, hover, selection, hatch, and mesh paths while preserving exact hit tests.\n\nRefs #433
2026-07-24 22:00:40 +03:00
Hakan Seven 4db763ef27 perf: unify spatial interaction queries
Dense drawings previously rescanned full geometry for snapping, tracking, and selection.\n\nRefs #433
2026-07-24 20:49:05 +03:00
Hakan Seven 7a1c0ead0d fix(xref): preserve layer-zero colors
Treat namespaced XREF layer |0 as layer zero during block inheritance so ByLayer geometry keeps its source color.\n\nCloses #437
2026-07-24 18:43:04 +03:00
Hakan SevenandClaude Opus 4.8 d54fb9e2aa fix(render): annotative scale representations + model-space linetype scale
Two rendering defects surfaced on the budweiser benchmark drawings, both
tied to a drawing's annotation scale.

Annotative scale representations. A drawing may materialise each annotation
scale as its own object (here a block insert per scale, on a "0 @ <scale>"
layer, each carrying a per-object AcDbContextData bound to one scale).
Nothing filtered them, so every scale drew at once — a 1× copy stacked
under a 10× one. Add annotative::annotative_offscale and apply it in the
three paths that expand block content: the wire block-defn cache, the
resident/top-level visibility gate (model space only — paper viewports use
their per-viewport frozen scale layers), and the exploded-insert hatch/fill
walk (filtered before normalize strips the handle). An object is shown when
a representation matches the current scale; the off-scale ones are hidden.
When the current scale is unsupported by any representation, fall back to
the base "1:1" copy instead of hiding everything (the object must not
vanish) or showing everything (the copies must not stack).

Model-space linetype scale. PSLTSCALE (a paper-space flag) was applied to
model-space linetypes via the annotation scale, so a drawing saved at e.g.
CANNOSCALE 10:1 drew its linetypes 10× off. Thread a `paper_space` flag
through tessellate_entity and gate the PSLTSCALE factor on it, so it only
scales linetypes for content shown inside a paper-space viewport; model
space keeps LTSCALE × CELTSCALE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 21:24:56 +03:00
Hakan SevenandClaude Fable 5 3689f0e504 fix(dimension): render baked *D fills inside block dimensions
A dimension bakes its geometry into a per-instance *D block, offset into
the owning block by the dimension insertion_point. When that dimension
sits inside a block (INSERT → block → Dimension → *D), only its top-level
form drew; nested it rendered nothing, and once the lines/text/arrow
outlines were routed through the block cache the arrowhead FILLS were
still missing:

- 2D SOLID arrowheads: block_cache's Dimension arm now expands the *D
  block, translating each sub by insertion_point (WCS → block-local) and
  routing Insert subs through build_nested_ref, others through
  tessellate_sub_local (which emits the solid fill_tris).
- Insert(hatch) arrowheads (custom arrow blocks = LwPolyline + Hatch):
  the hatch renders via the separate GPU explosion path, which only
  descends INSERT→INSERT→Hatch trees and so never reached a hatch behind
  a Dimension. block_has_hatch now recurses through a Dimension into its
  *D block, and the fill explosion's new explode_including_dims lifts
  each dimension child's *D content into the owning block's frame
  (translate insertion_point, then apply the insert transform) so its
  arrow inserts / hatches ride the normal walk.

Table *T baked blocks get the mirror treatment in block_cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 11:39:14 +03:00
Hakan Seven 1803f10264 Merge PR #442: cut mesh-import wire-set memory ~35% (#358)
entity_wires() returns the memoized Arc instead of a per-call deep copy;
MESH entities over 50k vertices ship no per-vertex snap/key tables
(matching PolyfaceMesh/PolygonMesh); the Face3D/other render split
caches a None marker and uses the base set directly when there is no
Face3D wire; test fix for the depth-map BlockCache::build signature.

Reviewed: correctness sound — wire AABB comes from entity_aabb (not
key_vertices) so large meshes stay selectable; snap consumers are
empty-guarded; the Arc/strong_count reasoning for try_resident_patch
holds. One consistent pre-existing quirk noted (auto_fit_viewport
centroid excludes tableless meshes, as Poly*Mesh already do).
2026-07-23 10:25:27 +03:00
Hakan SevenandClaude Fable 5 1e913e9e78 feat(underlay): render PDF underlays end-to-end
PDF underlays only drew their clip outline / insertion cross; the page
itself never appeared. The chain now runs: the definition's file path
resolves with the raster-image fallbacks (as stored, relative to the
drawing, basename next to the drawing — attach-time relative paths go
stale), the page rasterises through the system pdftoppm (poppler) at
150 dpi — no new crate; missing tool degrades to the outline — cached
per (path, page) including negative results, and the pixels feed the
existing image pipeline as an ImageModel: 1 underlay unit = 1 PDF inch
(px/dpi × entity scale), rotated about the insertion, fade as opacity,
the clip polygon (non-inverted) as the textured region. Draw-order,
layer visibility and per-viewport freezes ride the generic image path;
in-place edits rebuild through the shared image seed.

When the page renders, the wire path draws its frame with an interior
pick surface instead of the lone cross, so selection covers the visible
page. wasm keeps the outline placeholder (no external process).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 03:41:41 +03:00
Hakan SevenandClaude Fable 5 94609fc54a fix(text): bake draw-order depth into glyphs so fills can't cover text
Fill triangles (DIMTFILL backgrounds, MLEADER masks, hatches) carry the
entity's draw-order rank, but SDF glyph quads were pushed at neutral 0.0
— any entity in the front half of the draw order had its own background
fill painted over its text. gather_text_verts now adds the host wire's
wire_draw_depth to each glyph: a text background lands at the same
biased depth (glyphs draw after fills and win the LessEqual test), and
text respects draw order against other entities' fills too.

The #403 glyph-reuse fast path gains a rank-stability gate: adding or
removing an entity shifts every sibling's bias (the rank denominator
changes), so reuse is only safe across all-Modified edits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 01:13:33 +03:00
Kevin GriffinandClaude Fable 5 e9198a47e5 perf(render): don't duplicate the wire set when no Face3D wire exists
The per-epoch Face3D/other split cloned every wire into two owned lists
even when the face3d list came out empty — for pure 2-D drawings and
mesh imports (#358) the 'other' list was a wire-for-wire copy of the
entire base set, held resident in split_cache alongside the original.

Cache None as an 'everything is other' marker and hand the render the
base set itself for that frame. The base Arc is deliberately not stored
in the cache: try_resident_patch can only apply an incremental patch
while the resident Arc is uniquely held, so pinning it there would
silently degrade every edit to a full rebuild.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:03:01 -07:00
Kevin GriffinandClaude Fable 5 f347a20a4c perf(scene): return the memoized wire set by Arc, not by deep copy
entity_wires() cloned the entire wire set — every point, fill, and snap
buffer of every entity — on each call, although the set is already
memoized behind an Arc and none of the 18 call sites mutates it. On
large mesh imports (#358: 42M faces, ~210 MB of wire content per 1M
triangles) that is a multi-GB allocation + memcpy on every pick, snap,
selection, or plot pass.

Return the Arc instead. Call sites iterate as before; the two
window-plot paths that build a culled owned subset now clone only the
wires inside the plot window, and the print pipeline moves the Arc into
its async task.

Measured (tests/mem_probe_meshent.rs, 1M-triangle MESH, release):
resident after entity_wires() drops 758 -> 547 MB (866 -> 646 B/face).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 12:56:36 -07:00
Hakan SevenandClaude Fable 5 b5dbef5776 feat(props): editable block Name row + collapsible XYZ groups
- Block reference Name (Misc) is editable: the dropdown re-points the
  insert to another definition; typing a new name renames the definition
  everywhere (record re-key, Block marker, every INSERT reference).
  Anonymous (*) and xref blocks stay read-only, with the same guards
  added to Scene::rename_block.
- Custom single-control dropdown (text field + caret) built on the
  shared floating_below overlay so the list always opens downward,
  unlike iced's space-based menu placement.
- Consecutive "<Base> X/Y/Z" rows (Position, Scale, Start, Center, ...)
  collapse into one expandable summary row across all entities; the
  expand state persists across rebuilds and selection changes.
- The BEDIT block tab no longer offers the layout Rename/Delete
  context menu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 11:11:10 +03:00
Hakan SevenandClaude Fable 5 c0499a1d3c fix(render): SDF text reuse is per wire source (#403)
Paper layouts gather glyphs for the sheet and each content viewport in
one frame; the single-slot last_sdf_text handed the first source's
glyphs to the rest, hiding every model-space text entity inside
viewports (regression in aea1fd2). Key the reuse by render source, and
invalidate on Tolerance/ATTDEF edits and complex-linetype hosts too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 19:26:30 +03:00
Hakan SevenandClaude Fable 5 e177f92ff5 fix(xref): blocks respect their internal draw order
Xref merge now inserts entities in the source block's chain order and
carries SortEntitiesTable objects across (remapped to merged handles).
draw_depth_map values become [depth, half] so exploded block/xref fills
and wide-polyline band wires both compose child depth as
insert_depth + in-block rank x half, recursing into nested inserts with
the same divisor - bands and fills from one block interleave by the
block's internal order instead of stacking at the insert's single depth.
Band batches split per rank via WireModel.depth_override; thin wires
stay merged. The wire arena falls back to a full rebuild when a slab
carries mixed depths so refreshes don't flatten band overrides.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 15:53:28 +03:00
Hakan SevenandClaude Opus 4.8 b884e33f38 fix(viewport): apply per-viewport layer freeze to fills, images and meshes
A content viewport that froze layers still drew those layers' hatches,
2-D solids, images, OLE frames and 3-D solids — only its wires honoured
`Viewport.frozen_layers`. A viewport meant to be empty (all content
layers frozen) leaked the whole model instead.

viewport_data_for fed content viewports the full model hatch/wipeout/
image/mesh sets with no per-viewport frozen filter. Thread the viewport's
frozen-layer set through the builders (synced_hatch_models,
exploded_insert_hatch_models, wipeout_models, image_models, mesh_models,
instanced_block_meshes) via Scene::layer_frozen_in — the same
layer-name→handle→membership test the wire path uses — and expose
*_for_viewport accessors that cache the filtered set per order-independent
frozen-set signature (viewports sharing a frozen set share the build; an
empty set returns the shared unfiltered arc, so model tiles and
non-freezing viewports are byte-identical).

Each viewport renders in its own pipeline slot with the hatch upload
gated on geometry_epoch, so a per-viewport filtered set uploads once per
epoch rather than every frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:01:30 +03:00
Hakan SevenandClaude Opus 4.8 bda79c5967 fix(paper): derive paper-space unit from plot scale, not a limits heuristic
The previous limits/paper-size ratio heuristic only recognised millimetre
(1.0) and inch (1/25.4) paper spaces. Issue #341's layout uses an
arbitrary architectural scale (~11.84 mm per paper-space unit), which the
snap-to-inch-or-mm logic missed, so the sheet was drawn 11.8x too large
and the drawing shrank into the corner.

Derive the mm->paper-space factor from the plot scale instead, matching
how the DXF format defines the paper-space/paper relationship:
  page_unit_mm = 25.4 if plot_paper_units==inch else 1.0
  mm_per_unit  = (scale_numerator / scale_denominator) * page_unit_mm
  factor       = 1 / mm_per_unit   (fallback 1.0 when scale is degenerate)
The scale and page unit are a matched pair, so this is correct even when
the page unit alone looks wrong (an inch page at 1:25.4 is a millimetre
paper space). Uses scale_numerator/denominator (codes 142/143), not the
scale_factor field (code 147), whose value is unreliable for Scale-To-Fit.

Verified against budweiser2018 (inch + mm layouts) and the #341 AEC Bldg
Plan Sample (11.84 mm/unit): every sheet now matches its viewport bbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:32:30 +03:00
Hakan SevenandClaude Opus 4.8 7d68eb0f10 fix(paper): size inch paper-space sheets in their own units
Some layouts lay out paper space in inches (viewports and drawing limits
in inch units), but paper_width/height and plot margins are always
millimetres. paper_limits() drew the sheet at the mm size, 25.4x too
large, so the drawing shrank into the corner and the printable-area
dashes ran 25.4x too long.

Infer the mm->paper-space unit factor per layout from the limits/paper
ratio (plot_paper_units records the plot output unit and can disagree,
so it is not used). Scale the sheet, the printable-area margins and its
dash pattern by it. PDF export recovers physical mm by dividing back,
leaving millimetre layouts byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:13:01 +03:00
Hakan SevenandClaude Opus 4.8 626e8091f7 refactor(viewport): unify content-viewport clipping on the stencil path
Rectangular paper viewports now go through the same GPU stencil clip as
non-rectangular ones: viewport_clip_boundary_ndc returns the four rect
corners (a full-target mask) instead of nothing, so the render path has
no rect/non-rect special case — every content viewport carries a clip
boundary and draws with stencil reference 0xFF.

With a single clip path, the old per-wire / -image / -wipeout scissor
mechanism is fully dead and removed: compute_*_scissors, the
*_pixel_scissors buffers, the project_scissor helper, the per-pass
set_scissor_rect blocks, the vp_scissor field threaded through
WireModel / ImageModel / HatchModel and their GPU mirrors (WireGpu /
ImageGpu / WipeoutGpu / HatchWebGpu), from_run's scissor parameter, the
scissor grouping in build_wire_buffers, and the is_arena_eligible gate.
Net -245 lines across 54 files.

Rectangular viewports still clip exactly via their own render rectangle;
the stencil mask is a redundant full-target no-op there, kept only so
the clip code path stays uniform. XCLIP keeps its CPU wire clip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:06:28 +03:00
Hakan SevenandClaude Opus 4.8 b30f9b9741 feat(scene): draw nonprint light, camera and geo-location glyphs
Lights, camera views and the geographic-location marker had no on-screen
representation. Add display glyphs for all three:

- Lights render through a new EntityType::Light: a cone/flashlight for spot
  lights, a wire sphere for point lights, a sun for distant lights, sized in
  screen space (fixed-world fallback).
- Camera views (perspective VIEW records) and the GEODATA "daisy" marker are
  synthesized into the model-space resident wire set from the document, since
  they are not entities.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:15:40 +03:00