fix(scene): render baked block geometry for Dimension and Table
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 <noreply@anthropic.com>
This commit is contained in:
parent
62876b1081
commit
eab72062f9
2 changed files with 95 additions and 0 deletions
|
|
@ -4814,6 +4814,48 @@ fn tessellate_entity(
|
||||||
return wires;
|
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<WireModel> =
|
||||||
|
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 {
|
if let EntityType::Insert(ins) = e {
|
||||||
// Resolve the INSERT's own style so ByBlock sub-entities can inherit it.
|
// 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);
|
let (ins_color, ins_pat_len, ins_pat, ins_lw_px, _) = render::render_style_for(document, e);
|
||||||
|
|
|
||||||
|
|
@ -464,6 +464,59 @@ pub fn tessellate_dimension(
|
||||||
anno_scale: f32,
|
anno_scale: f32,
|
||||||
) -> Vec<WireModel> {
|
) -> Vec<WireModel> {
|
||||||
let name = handle.value().to_string();
|
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<n>`, 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<WireModel> =
|
||||||
|
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_name = &dim.base().style_name;
|
||||||
let style = document.dim_styles.iter().find(|s| {
|
let style = document.dim_styles.iter().find(|s| {
|
||||||
s.name.eq_ignore_ascii_case(style_name)
|
s.name.eq_ignore_ascii_case(style_name)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue