forked from 1b-agent/opencadstudio-upstream
fix(blocks): unify block-backed content
Route pictures, table cells, leader content, symbols, and arrows through one dependency and transform graph.\n\nRefs #864
This commit is contained in:
1 parent
3a5591e036
commit
19629d1246
12 files changed
+902
-557
No files matched your search
@@ -1,6 +1,6 @@
|
||||
//! DGN line-style rendering (first pass).
|
||||
//!
|
||||
//! Linetypes converted from MicroStation DGN store their real pattern as DGN
|
||||
//! Imported DGN linetypes store their real pattern as DGN
|
||||
//! line-style objects (`AcDbLS*`), not standard `LTYPE` dashes — the standard
|
||||
//! table entry is empty, so acadrust exposes the structure in
|
||||
//! [`CadDocument::dgn_ls_definitions`] / `dgn_ls_components` instead. See
|
||||
@@ -12,7 +12,7 @@
|
||||
//! lengths are carried through to the pipe walls.
|
||||
|
||||
use acadrust::objects::DgnLsComponentType;
|
||||
use acadrust::types::{Handle, Transform, Vector3};
|
||||
use acadrust::types::{Handle, Vector3};
|
||||
use acadrust::{CadDocument, EntityType};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -133,29 +133,52 @@ pub fn wall_dashes(doc: &CadDocument, lt_name: &str) -> Vec<f64> {
|
||||
/// radius, which is also the offset of the two pipe walls (they sit tangent to
|
||||
/// the end circles) — so it doubles as the rail offset for the double line.
|
||||
pub fn symbol_radius(doc: &CadDocument, block: Handle, scale: f64) -> f64 {
|
||||
let Some(br) = doc.block_records.iter().find(|b| b.handle == block) else {
|
||||
let mut r = 0.0_f64;
|
||||
let depths = rustc_hash::FxHashMap::default();
|
||||
let graph = crate::scene::render_graph::RenderSceneGraph::new(doc, None, None, true, &depths);
|
||||
let Some(block_use) = crate::scene::render_graph::block_use_from_handle(
|
||||
doc,
|
||||
block,
|
||||
crate::scene::render_graph::BlockRole::LineStyleSymbol,
|
||||
Vector3::ZERO,
|
||||
) else {
|
||||
return 0.0;
|
||||
};
|
||||
let bx = br.base_point.x;
|
||||
let by = br.base_point.y;
|
||||
let d = |x: f64, y: f64| ((x - bx).powi(2) + (y - by).powi(2)).sqrt();
|
||||
let mut r = 0.0_f64;
|
||||
for eh in &br.entity_handles {
|
||||
let Some(e) = doc.get_entity(*eh) else {
|
||||
continue;
|
||||
};
|
||||
let ext = match e {
|
||||
EntityType::Ellipse(el) => {
|
||||
let a = (el.major_axis.x.powi(2) + el.major_axis.y.powi(2)).sqrt();
|
||||
d(el.center.x, el.center.y) + a
|
||||
}
|
||||
EntityType::Circle(c) => d(c.center.x, c.center.y) + c.radius,
|
||||
EntityType::Arc(a) => d(a.center.x, a.center.y) + a.radius,
|
||||
EntityType::Line(l) => d(l.start.x, l.start.y).max(d(l.end.x, l.end.y)),
|
||||
_ => 0.0,
|
||||
};
|
||||
r = r.max(ext);
|
||||
}
|
||||
graph.walk_insert(
|
||||
&block_use.insert,
|
||||
block,
|
||||
|_, _| true,
|
||||
|entity, context| {
|
||||
let mut placed = entity.clone();
|
||||
placed.apply_transform(&context.transform);
|
||||
let distance = |x: f64, y: f64| x.hypot(y);
|
||||
let extent = match &placed {
|
||||
EntityType::Ellipse(ellipse) => {
|
||||
distance(ellipse.center.x, ellipse.center.y)
|
||||
+ distance(ellipse.major_axis.x, ellipse.major_axis.y)
|
||||
}
|
||||
EntityType::Circle(circle) => {
|
||||
distance(circle.center.x, circle.center.y) + circle.radius
|
||||
}
|
||||
EntityType::Arc(arc) => distance(arc.center.x, arc.center.y) + arc.radius,
|
||||
EntityType::Line(line) => distance(line.start.x, line.start.y)
|
||||
.max(distance(line.end.x, line.end.y)),
|
||||
_ => crate::scene::convert::tess::entity_world_aabb_f64(&placed)
|
||||
.map(|[min_x, min_y, max_x, max_y]| {
|
||||
[
|
||||
distance(min_x, min_y),
|
||||
distance(max_x, min_y),
|
||||
distance(max_x, max_y),
|
||||
distance(min_x, max_y),
|
||||
]
|
||||
.into_iter()
|
||||
.fold(0.0_f64, f64::max)
|
||||
})
|
||||
.unwrap_or(0.0),
|
||||
};
|
||||
r = r.max(extent);
|
||||
},
|
||||
);
|
||||
let s = if scale.abs() > 1e-9 { scale } else { 1.0 };
|
||||
r / s
|
||||
}
|
||||
@@ -286,46 +309,50 @@ pub fn place_block_wires(
|
||||
world_per_pixel: Option<f32>,
|
||||
bg_color: [f32; 4],
|
||||
) -> Vec<WireModel> {
|
||||
let Some(br) = doc.block_records.iter().find(|b| b.handle == block) else {
|
||||
return Vec::new();
|
||||
};
|
||||
// The symbol block's native geometry is drawn at 1 / scale_divisor (the
|
||||
// divisor is read from the symbol component's leaf data). Scale about the
|
||||
// origin, then translate the (scaled) base point to the placement point.
|
||||
let s = if scale_divisor.abs() > 1e-9 {
|
||||
1.0 / scale_divisor
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let scale = Transform::from_scale(s);
|
||||
let offset = Vector3::new(
|
||||
at[0] - br.base_point.x * s,
|
||||
at[1] - br.base_point.y * s,
|
||||
at[2] - br.base_point.z * s,
|
||||
);
|
||||
let Some(mut block_use) = crate::scene::render_graph::block_use_from_handle(
|
||||
doc,
|
||||
block,
|
||||
crate::scene::render_graph::BlockRole::LineStyleSymbol,
|
||||
Vector3::new(at[0], at[1], at[2]),
|
||||
) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let insert = &mut block_use.insert;
|
||||
insert.set_x_scale(s);
|
||||
insert.set_y_scale(s);
|
||||
insert.set_z_scale(s);
|
||||
let depths = rustc_hash::FxHashMap::default();
|
||||
let graph = crate::scene::render_graph::RenderSceneGraph::new(doc, None, None, true, &depths)
|
||||
.with_annotation_scale(anno_scale);
|
||||
let mut out = Vec::new();
|
||||
for eh in &br.entity_handles {
|
||||
let Some(ent) = doc.get_entity(*eh) else {
|
||||
continue;
|
||||
};
|
||||
let mut clone = ent.clone();
|
||||
clone.as_entity_mut().apply_transform(&scale);
|
||||
clone.as_entity_mut().translate(offset);
|
||||
out.extend(super::tessellate::tessellate(
|
||||
doc,
|
||||
*eh,
|
||||
&clone,
|
||||
false,
|
||||
color,
|
||||
0.0,
|
||||
[0.0; 8],
|
||||
line_weight_px,
|
||||
anno_scale,
|
||||
None,
|
||||
world_per_pixel,
|
||||
bg_color,
|
||||
false,
|
||||
));
|
||||
}
|
||||
graph.walk_insert(
|
||||
insert,
|
||||
block,
|
||||
|_, _| true,
|
||||
|entity, context| {
|
||||
let mut placed = entity.clone();
|
||||
placed.apply_transform(&context.transform);
|
||||
out.extend(super::tessellate::tessellate(
|
||||
doc,
|
||||
entity.common().handle,
|
||||
&placed,
|
||||
false,
|
||||
color,
|
||||
0.0,
|
||||
[0.0; 8],
|
||||
line_weight_px,
|
||||
anno_scale,
|
||||
None,
|
||||
world_per_pixel,
|
||||
bg_color,
|
||||
false,
|
||||
));
|
||||
},
|
||||
);
|
||||
out
|
||||
}
|
||||
+36
-11
@@ -122,6 +122,7 @@ pub(crate) fn expand_block_object(
|
||||
.map(|record| record.flags.is_xref || record.flags.is_xref_overlay)
|
||||
.unwrap_or(false);
|
||||
let mut wires = cache::block_cache::expand_insert(
|
||||
document,
|
||||
cache,
|
||||
insert,
|
||||
owner,
|
||||
@@ -146,7 +147,11 @@ pub(crate) fn expand_block_object(
|
||||
|
||||
if options.apply_xclip {
|
||||
if let Some(filter) = pick::xclip::insert_spatial_filter(document, insert) {
|
||||
let transform = crate::scene::render_graph::insert_transform(document, insert);
|
||||
let transform = crate::scene::render_graph::insert_transform_at_scale(
|
||||
document,
|
||||
insert,
|
||||
anno_scale,
|
||||
);
|
||||
let polygon = pick::xclip::world_clip_polygon_for_transform(filter, &transform);
|
||||
pick::xclip::clip_wires(&mut wires, &polygon);
|
||||
for wire in &mut wires {
|
||||
@@ -952,12 +957,17 @@ fn tessellate_entity_inner(
|
||||
// Render non-annotative dimensions from their stored picture block.
|
||||
// Rebuild geometry only when no usable block exists.
|
||||
if matches!(e, EntityType::Dimension(_)) {
|
||||
if let Some(owned) = crate::scene::render_graph::owned_block_insert(document, e)
|
||||
.filter(|owned| owned.render_picture)
|
||||
if let Some(block_use) = crate::scene::render_graph::entity_render_block_uses(
|
||||
document,
|
||||
e,
|
||||
anno_scale,
|
||||
)
|
||||
.into_iter()
|
||||
.find(|block_use| block_use.active)
|
||||
{
|
||||
let mut wires = expand_block_object(
|
||||
document,
|
||||
&owned.insert,
|
||||
&block_use.insert,
|
||||
h,
|
||||
sel,
|
||||
active_viewport,
|
||||
@@ -969,7 +979,7 @@ fn tessellate_entity_inner(
|
||||
world_per_pixel,
|
||||
pslt_factor,
|
||||
BlockObjectOptions {
|
||||
suppress_root_points: owned.suppress_root_points,
|
||||
suppress_root_points: block_use.suppress_root_points,
|
||||
..BlockObjectOptions::default()
|
||||
},
|
||||
)
|
||||
@@ -1054,10 +1064,19 @@ fn tessellate_entity_inner(
|
||||
// When the block exists we render it directly. Same pattern as
|
||||
// Dimension's `block_name`.
|
||||
if let EntityType::Table(table) = e {
|
||||
if let Some(owned) = crate::scene::render_graph::owned_block_insert(document, e) {
|
||||
if let Some(block_use) = crate::scene::render_graph::entity_render_block_uses(
|
||||
document,
|
||||
e,
|
||||
anno_scale,
|
||||
)
|
||||
.into_iter()
|
||||
.find(|block_use| {
|
||||
block_use.active
|
||||
&& block_use.role == crate::scene::render_graph::BlockRole::TablePicture
|
||||
}) {
|
||||
let mut wires = expand_block_object(
|
||||
document,
|
||||
&owned.insert,
|
||||
&block_use.insert,
|
||||
h,
|
||||
sel,
|
||||
active_viewport,
|
||||
@@ -1091,9 +1110,15 @@ fn tessellate_entity_inner(
|
||||
line_weight_px,
|
||||
table_anno,
|
||||
);
|
||||
for insert in
|
||||
crate::entities::table::block_cell_inserts(table, document, table_anno)
|
||||
{
|
||||
for block_use in crate::scene::render_graph::entity_render_block_uses(
|
||||
document,
|
||||
e,
|
||||
table_anno,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|block_use| {
|
||||
block_use.role == crate::scene::render_graph::BlockRole::TableCell
|
||||
}) {
|
||||
wires.extend(tessellate_entity(
|
||||
document,
|
||||
selected,
|
||||
@@ -1101,7 +1126,7 @@ fn tessellate_entity_inner(
|
||||
bg_color,
|
||||
1.0,
|
||||
None,
|
||||
&EntityType::Insert(insert),
|
||||
&EntityType::Insert(block_use.insert),
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
|
||||
@@ -2091,15 +2091,17 @@ fn custom_arrow_from_block(
|
||||
true,
|
||||
&depths,
|
||||
);
|
||||
let insert = acadrust::entities::Insert::new(
|
||||
record.name.clone(),
|
||||
let block_use = crate::scene::render_graph::block_use_from_handle(
|
||||
doc,
|
||||
record.handle,
|
||||
crate::scene::render_graph::BlockRole::ArrowHead,
|
||||
acadrust::types::Vector3::ZERO,
|
||||
);
|
||||
)?;
|
||||
let mut lines = Vec::new();
|
||||
let mut fill = Vec::new();
|
||||
let mut deferred_hatch = false;
|
||||
graph.walk_insert(
|
||||
&insert,
|
||||
&block_use.insert,
|
||||
record.handle,
|
||||
|_, _| true,
|
||||
|entity, context| {
|
||||
|
||||
Reference in new issue
Block a user