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)