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>
This commit is contained in:
Hakan SevenandClaude Fable 5 committed 2026-07-23 11:39:14 +03:00
1 parent 1803f10264
commit 3689f0e504
3 files changed
+160 -3

No files matched your search

+48 -3
View File
@@ -847,6 +847,52 @@ impl Scene {
// at world position, like a block-internal hatch. (#222)
let mut wide_block_memo: std::collections::HashMap<String, bool> =
std::collections::HashMap::new();
// `explode_from_document` returns a Dimension child verbatim (it carries
// no fill of its own), so a filled arrowhead that lives as a nested
// Insert(hatch) inside the dimension's baked `*D` block never reaches
// this fill explosion. Expand each dimension child's `*D` content into
// the owning block's frame — translate by the dimension insertion_point
// (WCS → block-local) then apply the insert transform (block-local →
// world) — so its arrow inserts / hatches ride the normal walk. Only
// fill-bearing children (Insert / Hatch) are lifted; the dimension's
// lines and text still render through the wire path.
fn explode_including_dims(ins: &DxfInsert, doc: &CadDocument) -> Vec<EntityType> {
let mut out = ins.explode_from_document(doc);
let Some(br) = doc.block_records.get(&ins.block_name) else {
return out;
};
let xform = ins.get_transform();
for &beh in &br.entity_handles {
let Some(EntityType::Dimension(dim)) = doc.get_entity(beh) else {
continue;
};
let bn = dim.base().block_name.clone();
if bn.trim().is_empty() {
continue;
}
let ins_pt = dim.base().insertion_point;
let Some(dblk) = doc
.block_records
.iter()
.find(|r| r.name.eq_ignore_ascii_case(&bn))
else {
continue;
};
for &deh in &dblk.entity_handles {
let Some(dsub) = doc.get_entity(deh) else {
continue;
};
if !matches!(dsub, EntityType::Insert(_) | EntityType::Hatch(_)) {
continue;
}
let mut placed = dsub.clone();
placed.as_entity_mut().translate(ins_pt);
placed.apply_transform(&xform);
out.push(placed);
}
}
out
}
for entity in self.document.entities() {
let EntityType::Insert(ins) = entity else {
continue;
@@ -902,8 +948,7 @@ impl Scene {
[f32; 4],
crate::scene::view::render::InheritStyle,
(f32, f32),
)> = ins
.explode_from_document(&self.document)
)> = explode_including_dims(ins, &self.document)
.into_iter()
.map(|e| (normalize(e), 0usize, ins_color, l0, (base_depth, half_gap)))
.collect();
@@ -947,7 +992,7 @@ impl Scene {
.map_or(0.0, |d| d[0])
* d_half;
let nd_half = d_half / (block_count(&nins.block_name) as f32 + 1.0);
for e in nins.explode_from_document(&self.document) {
for e in explode_including_dims(&nins, &self.document) {
stack.push((
normalize(e),
depth + 1,