forked from 1b-agent/opencadstudio-upstream
fix(table): place a baked *T table block at the table's insertion point
A Table whose cells are baked into a *T block (block_record_handle) was rendered by tessellating the block's sub-entities directly. Those are laid out in block-local space (origin at the table's top-left corner) — the Table entity carries the world placement in insertion_point + horizontal_direction, exactly like an INSERT — so the whole table drew at the world origin instead of its location. Each baked sub-entity is now cloned and placed (rotated about the local origin by the horizontal_direction angle, then translated by insertion_point) before tessellation, so tessellate_entity produces correctly located, relative-to-eye geometry. Verified on budweiser handle 9378: its baked lines move from (0,0) to the insertion point (-1200018.7, -800071.4). Also bumps acadrust to pull the OLE2FRAME corner-decode fix, which places OLE2FRAME entities (e.g. handle 3088) at their real corners instead of the (1,1)/(0,0) defaults. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
db896ec632
commit
5e127ffa75
2 files changed
+108
-39
No files matched your search
Generated
+1
-1
@@ -73,7 +73,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#5ce1e312d8c9e7e8bd9911a5f68f13f3cc83992b"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#59398a8010bb2227a94a9625eaf74ae5fc55fa2f"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
||||
+107
-38
@@ -24,7 +24,6 @@ fn fade_if_locked(
|
||||
// requiring `Scene` (which contains `Rc<RefCell<...>>` and is `!Send`) to
|
||||
// cross thread boundaries.
|
||||
|
||||
|
||||
/// Tessellate a synthesised dimension-text entity through `tessellate_entity`
|
||||
/// so it picks up the standard text LOD ladder (baseline / greek / full),
|
||||
/// then re-color the returned wires with the dimension's resolved text colour
|
||||
@@ -41,8 +40,15 @@ pub(crate) fn tessellate_entity_dim_text(
|
||||
text_color: [f32; 4],
|
||||
) -> Vec<WireModel> {
|
||||
let mut wires = tessellate_entity(
|
||||
document, selected, active_viewport, bg_color,
|
||||
anno_scale, e, None, view_aabb, world_per_pixel,
|
||||
document,
|
||||
selected,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
e,
|
||||
None,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
);
|
||||
for w in &mut wires {
|
||||
// Synth dim text carries no real entity colour — paint everything
|
||||
@@ -266,8 +272,7 @@ pub(crate) fn tessellate_entity(
|
||||
.find(|br| br.name.eq_ignore_ascii_case(block_name))
|
||||
{
|
||||
if !br.entity_handles.is_empty() {
|
||||
let mut wires: Vec<WireModel> =
|
||||
Vec::with_capacity(br.entity_handles.len());
|
||||
let mut wires: Vec<WireModel> = Vec::with_capacity(br.entity_handles.len());
|
||||
// The Dimension's own layer style — layer-0 inheritance
|
||||
// target for baked sub-entities on layer "0" (#221).
|
||||
let dim_l0_color = view::render::adapt_to_bg(
|
||||
@@ -283,7 +288,9 @@ pub(crate) fn tessellate_entity(
|
||||
})
|
||||
.unwrap_or(0);
|
||||
for &eh in &br.entity_handles {
|
||||
let Some(sub) = document.get_entity(eh) else { continue };
|
||||
let Some(sub) = document.get_entity(eh) else {
|
||||
continue;
|
||||
};
|
||||
// Sub-entities inside *D### / DIMBLOCK## blocks
|
||||
// typically use ByBlock color/linetype/lineweight —
|
||||
// they should inherit from the Dimension entity.
|
||||
@@ -292,10 +299,17 @@ pub(crate) fn tessellate_entity(
|
||||
let sub_is_l0_bylayer = sub.common().layer == "0"
|
||||
&& sub.common().color == acadrust::types::Color::ByLayer;
|
||||
let sub_wires = tessellate_entity(
|
||||
document, selected, active_viewport, bg_color,
|
||||
document,
|
||||
selected,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
// Block contents are baked at the final WCS size —
|
||||
// don't let downstream paths re-apply anno_scale.
|
||||
1.0, sub, block_cache, view_aabb, world_per_pixel,
|
||||
1.0,
|
||||
sub,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
);
|
||||
for mut w in sub_wires {
|
||||
w.name = h.value().to_string();
|
||||
@@ -305,7 +319,11 @@ pub(crate) fn tessellate_entity(
|
||||
// fallback that render_style_for produces. A layer-0
|
||||
// sub inherits the dim's layer colour instead.
|
||||
if sub_color_is_byblock {
|
||||
w.color = if sel { WireModel::SELECTED } else { entity_color };
|
||||
w.color = if sel {
|
||||
WireModel::SELECTED
|
||||
} else {
|
||||
entity_color
|
||||
};
|
||||
w.aci = aci;
|
||||
} else if sub_is_l0_bylayer && !sel {
|
||||
w.color = dim_l0_color;
|
||||
@@ -397,14 +415,9 @@ pub(crate) fn tessellate_entity(
|
||||
// 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 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());
|
||||
let mut wires: Vec<WireModel> = Vec::with_capacity(br.entity_handles.len());
|
||||
// The Table's own layer style — layer-0 inheritance target
|
||||
// for baked sub-entities on layer "0" (#221).
|
||||
let tab_l0_color = view::render::adapt_to_bg(
|
||||
@@ -419,20 +432,54 @@ pub(crate) fn tessellate_entity(
|
||||
_ => 0,
|
||||
})
|
||||
.unwrap_or(0);
|
||||
// The *T block is laid out in block-local space (origin at
|
||||
// the table's top-left corner); the Table entity carries the
|
||||
// world placement in `insertion_point` + `horizontal_direction`
|
||||
// (like an INSERT). Place each baked sub-entity there before
|
||||
// tessellating — without it the whole table renders at the
|
||||
// origin. Rotating about the local origin then translating
|
||||
// gives world = insertion + R·local; tessellate_entity then
|
||||
// handles the UTM-scale relative-to-eye split as usual.
|
||||
let ins = tab.insertion_point;
|
||||
let angle = tab.horizontal_direction.y.atan2(tab.horizontal_direction.x);
|
||||
for &eh in &br.entity_handles {
|
||||
let Some(sub) = document.get_entity(eh) else { continue };
|
||||
let Some(sub) = document.get_entity(eh) else {
|
||||
continue;
|
||||
};
|
||||
let sub_color_is_byblock =
|
||||
sub.common().color == acadrust::types::Color::ByBlock;
|
||||
let sub_is_l0_bylayer = sub.common().layer == "0"
|
||||
&& sub.common().color == acadrust::types::Color::ByLayer;
|
||||
let mut placed = sub.clone();
|
||||
{
|
||||
let ent = placed.as_entity_mut();
|
||||
if angle.abs() > 1e-9 {
|
||||
ent.apply_rotation(
|
||||
acadrust::types::Vector3::new(0.0, 0.0, 1.0),
|
||||
angle,
|
||||
);
|
||||
}
|
||||
ent.translate(ins);
|
||||
}
|
||||
let sub_wires = tessellate_entity(
|
||||
document, selected, active_viewport, bg_color,
|
||||
anno_scale, sub, block_cache, view_aabb, world_per_pixel,
|
||||
document,
|
||||
selected,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
&placed,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
);
|
||||
for mut w in sub_wires {
|
||||
w.name = h.value().to_string();
|
||||
if sub_color_is_byblock {
|
||||
w.color = if sel { WireModel::SELECTED } else { entity_color };
|
||||
w.color = if sel {
|
||||
WireModel::SELECTED
|
||||
} else {
|
||||
entity_color
|
||||
};
|
||||
w.aci = aci;
|
||||
} else if sub_is_l0_bylayer && !sel {
|
||||
w.color = tab_l0_color;
|
||||
@@ -468,7 +515,12 @@ pub(crate) fn tessellate_entity(
|
||||
1.0
|
||||
};
|
||||
let mut wires = crate::entities::table::tessellate_table(
|
||||
tab, document, sel, entity_color, line_weight_px, table_anno,
|
||||
tab,
|
||||
document,
|
||||
sel,
|
||||
entity_color,
|
||||
line_weight_px,
|
||||
table_anno,
|
||||
);
|
||||
if !wires.is_empty() {
|
||||
let aabb = entity_aabb(e);
|
||||
@@ -487,7 +539,8 @@ pub(crate) fn tessellate_entity(
|
||||
|
||||
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, _) = view::render::render_style_for(document, e);
|
||||
let (ins_color, ins_pat_len, ins_pat, ins_lw_px, _) =
|
||||
view::render::render_style_for(document, e);
|
||||
let ins_color = view::render::adapt_to_bg(ins_color, bg_color);
|
||||
// Resolve the INSERT's *layer* style — the layer-0 inheritance target
|
||||
// for sub-entities on layer "0" with ByLayer properties (#221).
|
||||
@@ -778,9 +831,7 @@ pub(crate) fn tessellate_entity(
|
||||
};
|
||||
let mut rails = Vec::new();
|
||||
for sgn in [1.0_f64, -1.0] {
|
||||
if let Some(off) =
|
||||
convert::dgn_linestyle::offset_host_entity(e, sgn * radius)
|
||||
{
|
||||
if let Some(off) = convert::dgn_linestyle::offset_host_entity(e, sgn * radius) {
|
||||
let mut w = convert::tessellate::tessellate(
|
||||
document,
|
||||
h,
|
||||
@@ -885,9 +936,9 @@ fn lod_stub_wire(
|
||||
WireModel {
|
||||
pick_tris: Vec::new(),
|
||||
pick_tris_low: Vec::new(),
|
||||
dash_from_start: false,
|
||||
dash_align_end: None,
|
||||
text_verts: Vec::new(),
|
||||
dash_from_start: false,
|
||||
dash_align_end: None,
|
||||
text_verts: Vec::new(),
|
||||
name,
|
||||
// Diagonal of the entity's 3D AABB so depth tests against
|
||||
// shaded / hidden-line geometry are correct — the stub doesn't
|
||||
@@ -929,16 +980,35 @@ fn lod_stub_wire_3d(
|
||||
z_max: f32,
|
||||
) -> WireModel {
|
||||
let [x0, y0, x1, y1] = aabb;
|
||||
let (z0, z1) = if z_min <= z_max { (z_min, z_max) } else { (z_max, z_min) };
|
||||
let (z0, z1) = if z_min <= z_max {
|
||||
(z_min, z_max)
|
||||
} else {
|
||||
(z_max, z_min)
|
||||
};
|
||||
let p = [
|
||||
[x0, y0, z0], [x1, y0, z0], [x1, y1, z0], [x0, y1, z0],
|
||||
[x0, y0, z1], [x1, y0, z1], [x1, y1, z1], [x0, y1, z1],
|
||||
[x0, y0, z0],
|
||||
[x1, y0, z0],
|
||||
[x1, y1, z0],
|
||||
[x0, y1, z0],
|
||||
[x0, y0, z1],
|
||||
[x1, y0, z1],
|
||||
[x1, y1, z1],
|
||||
[x0, y1, z1],
|
||||
];
|
||||
// 12 edges = 4 bottom-face + 4 top-face + 4 vertical connectors.
|
||||
const EDGES: [(usize, usize); 12] = [
|
||||
(0, 1), (1, 2), (2, 3), (3, 0),
|
||||
(4, 5), (5, 6), (6, 7), (7, 4),
|
||||
(0, 4), (1, 5), (2, 6), (3, 7),
|
||||
(0, 1),
|
||||
(1, 2),
|
||||
(2, 3),
|
||||
(3, 0),
|
||||
(4, 5),
|
||||
(5, 6),
|
||||
(6, 7),
|
||||
(7, 4),
|
||||
(0, 4),
|
||||
(1, 5),
|
||||
(2, 6),
|
||||
(3, 7),
|
||||
];
|
||||
let mut points: Vec<[f32; 3]> = Vec::with_capacity(EDGES.len() * 3);
|
||||
for (a, b) in EDGES {
|
||||
@@ -952,9 +1022,9 @@ fn lod_stub_wire_3d(
|
||||
WireModel {
|
||||
pick_tris: Vec::new(),
|
||||
pick_tris_low: Vec::new(),
|
||||
dash_from_start: false,
|
||||
dash_align_end: None,
|
||||
text_verts: Vec::new(),
|
||||
dash_from_start: false,
|
||||
dash_align_end: None,
|
||||
text_verts: Vec::new(),
|
||||
name,
|
||||
points,
|
||||
points_low: Vec::new(),
|
||||
@@ -1086,4 +1156,3 @@ pub(crate) fn is_unindexable_entity(e: &acadrust::EntityType) -> bool {
|
||||
E::Insert(_) | E::Viewport(_) | E::Block(_) | E::BlockEnd(_)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user