From eab72062f95d7bfb4f4479ab6d7c5a8284e51213 Mon Sep 17 00:00:00 2001 From: Hakan Date: Thu, 21 May 2026 12:54:49 +0300 Subject: [PATCH] fix(scene): render baked block geometry for Dimension and Table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AutoCAD bakes a per-instance block with the final, displayed geometry into Dimension and Table entities (typically *D### / *T###, but custom names also occur — sample-file-H uses DIMBLOCK*-4NP). The block's text already encodes DIMTXT * DIMSCALE (or the table's scale factor), so synthesising the entity from the dim/table style on top of that double-applies the scale. When the referenced block exists with content, render it directly: - Dimension: dim.base.block_name -> block_record - Table: block_record_handle -> block_record The synthesis fallback stays in place for files where the block is missing or empty. Fixes sample-file-H.dwg dimensions rendering at 60000 instead of 200 (DIMTXT=199.196 * DIMSCALE=304.8). Co-Authored-By: Claude Opus 4.7 --- src/scene/mod.rs | 42 ++++++++++++++++++++++++++++++++ src/scene/tessellate.rs | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index e4e957c59..e92f345f5 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -4814,6 +4814,48 @@ fn tessellate_entity( return wires; } + // ── Table baked-block fast path ───────────────────────────────────────── + // + // AutoCAD bakes a Table's final rendered geometry (cell text, gridlines, + // fill) into a per-instance block (usually `*T###`) referenced through + // `table.block_record_handle`. The block's text uses the *displayed* + // height; synthesising cells from `self.rows + TableStyle` instead would + // re-apply the table's scale factor on top of already-baked geometry. + // When the block exists we render it directly. Same pattern as + // Dimension's `block_name`. + if let EntityType::Table(tab) = e { + if let Some(br_h) = tab.block_record_handle { + if let Some(br) = document + .block_records + .iter() + .find(|br| br.handle == br_h) + { + if !br.entity_handles.is_empty() { + let mut wires: Vec = + Vec::with_capacity(br.entity_handles.len()); + for &eh in &br.entity_handles { + let Some(sub) = document.get_entity(eh) else { continue }; + let sub_wires = tessellate_entity( + document, selected, active_viewport, world_offset, bg_color, + anno_scale, sub, block_cache, view_aabb, world_per_pixel, + ); + for mut w in sub_wires { + w.name = h.value().to_string(); + wires.push(w); + } + } + if !wires.is_empty() { + let aabb = entity_aabb(e, world_offset); + for w in &mut wires { + w.aabb = aabb; + } + return wires; + } + } + } + } + } + if let EntityType::Insert(ins) = e { // Resolve the INSERT's own style so ByBlock sub-entities can inherit it. let (ins_color, ins_pat_len, ins_pat, ins_lw_px, _) = render::render_style_for(document, e); diff --git a/src/scene/tessellate.rs b/src/scene/tessellate.rs index 8c6f7f692..25fd593af 100644 --- a/src/scene/tessellate.rs +++ b/src/scene/tessellate.rs @@ -464,6 +464,59 @@ pub fn tessellate_dimension( anno_scale: f32, ) -> Vec { let name = handle.value().to_string(); + + // ── Anonymous-block fast path ─────────────────────────────────────────── + // + // AutoCAD bakes each dimension's final geometry (extension lines, dim + // line, arrows, text MText) into a per-instance block — usually named + // `*D`, but some authoring tools use a custom name like + // `DIMBLOCK###-4NP`. When that block exists we render its contents + // directly: the text height inside the block is the FINAL displayed + // height (DIMTXT × DIMSCALE already applied at creation), so + // synthesising it from the dim style would double-apply DIMSCALE. + let dim_block_name = &dim.base().block_name; + if !dim_block_name.trim().is_empty() { + if let Some(br) = document + .block_records + .iter() + .find(|br| br.name.eq_ignore_ascii_case(dim_block_name)) + { + if !br.entity_handles.is_empty() { + let mut wires: Vec = + Vec::with_capacity(br.entity_handles.len()); + for &eh in &br.entity_handles { + let Some(sub) = document.get_entity(eh) else { continue }; + // Inherit dim's color + line weight when the sub-entity is + // ByBlock; ByLayer stays on its own layer. + let sub_color = if selected { + WireModel::SELECTED + } else { + entity_color + }; + let mut wire = tessellate( + document, + handle, + sub, + selected, + sub_color, + 0.0, + [0.0; 8], + line_weight_px, + world_offset, + // Sub-entities are already baked at the final WCS + // size; don't re-apply anno_scale. + 1.0, + ); + wire.name = name.clone(); + wires.push(wire); + } + if !wires.is_empty() { + return wires; + } + } + } + } + let style_name = &dim.base().style_name; let style = document.dim_styles.iter().find(|s| { s.name.eq_ignore_ascii_case(style_name)