`viewport_content_wires` cloned model-space tessellations and overwrote
`points` with paper-space projections (× scale) but left `pattern_length`
and `pattern` at their model-coord values (pre-multiplied by PSLTSCALE's
1/vp_scale). The GPU then compared a paper-coord distance against a
model-coord pattern, so dashed linetypes in viewport content collapsed to
solid (a typical 9 mm paper line fell inside the first 180 mm dash).
Multiply the projected wire's pattern by the same vp scale used for the
points. Works for both PSLTSCALE on (paper-uniform dashes) and off
(dashes shrink with viewport scale).
Adds two diagnostic examples used to track this down:
- inspect_lt: which layers/entities carry dashed linetypes
- inspect_block: enumerate entities + linetypes inside a named block
Symptom: open a non-UTM drawing, most lines/polylines/hatches/etc.
vanish — only ~5% of indexed entities reachable from a huge query
(walking the nodes flat showed all 2975 items present, but recursing
through `node.children` from root reached only 161).
Root cause: when straddler items piled up at an INTERNAL node and the
count exceeded LEAF_CAPACITY, `insert()` re-invoked `split()` on that
node. `split()` allocates four fresh children and assigns them to
`node.children = Some([...])` — overwriting the existing children
pointer and orphaning the entire previous subtree (the descendant
nodes still hold their items; nothing reaches them).
Splitting an internal node is futile anyway: straddlers don't fit
smaller children either. Fix: gate the split on
`children.is_none()` so only leaves split. Internal nodes accumulate
straddlers as a linear-scan list — slower per-query for big strad-
dler counts, but correct.
Adds a regression test (`many_straddlers_dont_orphan_subtree`) that
floods an internal node with straddlers and verifies a huge query
still returns every inserted handle.
Bonus: `examples/dump_index_stats.rs` — standalone tool that loads a
DWG/DXF and dumps per-type counts (indexed / unbounded / top-level /
block-internal) plus a sample of bboxes. Useful for future bug
triage; built and ran cleanly against the file that exposed this.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three changes to make paper-space viewports render correctly on
UTM-scale drawings whose viewports were saved with default
view_target=(0, 0, 0) and a view_center pointing at empty WCS:
1. **Auto-fit fallback.** When the saved view's WCS rect doesn't
overlap the IQR-bounded content cluster (`world_offset ±
local_extent_max`), override `view_center` with `world_offset` and
recompute `view_height` to fit the cluster into the paper viewport.
Matches AutoCAD's silent auto-fit-on-open for stale / uninitialized
viewports; without it those viewports rendered blank.
2. **f64 projection inner loop.** The previous f32 path computed
`(wire_offset_rel - target_offset_rel).dot(view_right) -
view_center` by subtracting values at ~5e6 magnitude (f32 ULP
~0.5 m) to land on a small paper offset. The cancellation produced
centimetre-scale jitter on paper output even when the model was
clean. Reconstruct the wire WCS coord, subtract the display center,
and dot-project in f64; cast to f32 only at the final paper
position where magnitudes are bounded by the viewport rect.
3. **examples/inspect_viewports.rs** — diagnostic that dumps every
viewport's saved view fields (view_target, view_center, view_height
vs paper height, custom_scale, status flags) plus drawing-level
insertion_units and model-space extents. Used to verify that
acadrust's DWG reader reads view_height correctly; the
`view_height == vp.height` patterns observed on real files turn out
to be genuine "default 1:1" file content rather than a reader bug
(a round-trip test through DwgWriter / DwgReader survives all
per-viewport values).
Also: switch the `acadrust` patch from `HakanSeven12/acadrust@feat/
expose-blockrecord-is-loaded` (a stale branch with the abandoned
is_loaded experiment) to `hakanaktt/acadrust@main`. Upstream main
now carries the layout paper-dimensions and mirrored-arc fixes that
this branch used to add — plus PR #20's DXF reading fixes that the
old branch was missing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Block defns and top-level entities now know their world-space XY
AABB, and `tessellate_entity` / `expand_insert` early-out for anything
that doesn't intersect the camera's view rect. Tested commit-only on
time_open (correct subset emitted with default camera); GUI run
pending user verification.
Cache plumbing:
- `LocalWire` / `BlockDefn` carry `aabb_local` (post-build recursive
union over nested defns).
- `Scene::view_world_aabb()` derives a 25%-margined world rect from
the active camera + last-seen render aspect.
- `wire_cache` / `paper_sheet_cache` keys widen to
`(geometry_epoch, camera_generation)` so pan/zoom invalidates the
cached wire list — and pipeline `cached_epoch` widens to match
so the GPU re-uploads on view change.
- `XrefStatus::Unloaded` ack already shipped — orthogonal to this.
Also adds `examples/xref_states.rs` to dump per-block_record xref
metadata for diagnosing the is_loaded / unloaded distinction.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Examples can't import from a bin-only crate, so add a thin lib.rs that
re-exports the existing modules. Used by the new examples that helped
diagnose the freeze fixed in the previous commit.
- examples/time_open: times parse / purge / cache / first-wires phases
- examples/inspect_insert: lists Insert blocks with sub-entity counts
- examples/find_raster: dumps RasterImage path + dimensions
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
acadrust's DWG reader occasionally desynchronises on certain files
(observed on render.dwg: 1519 of 1763 LwPolylines arrived with non-unit
normals in the 1e200+ range and 100k phantom vertices). Tessellating
those entities allocates gigabytes and produces NaN-laden wires,
freezing the UI.
Add a pre-tessellation guard that scans freshly loaded documents and
drops any entity failing a cheap sanity check: non-unit normal,
non-finite or absurd coordinate, or vertex count above 100k. Count of
dropped entities is threaded through DerivedCaches and surfaced as a
warning in the command line so the user knows the file was patched up
on the way in.
Same file in DXF form parses cleanly (0 corrupt), so this is strictly
a DWG-path workaround until the upstream parser bug is fixed.
The new examples/inspect_polyline.rs is a one-off diagnostic that dumps
LwPolyline/Polyline2D/Polyline3D fields plus a corrupt-entity count;
useful for narrowing down the acadrust bug.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>