forked from 1b-agent/opencadstudio-upstream
fix(blocks): unify nested object transforms
Route block-backed entities through one local geometry and accumulated transform path. Refs #864
This commit is contained in:
1 parent
853e830126
commit
abb4f4bf56
4 files changed
+471
-587
No files matched your search
+80
-111
@@ -1700,6 +1700,59 @@ impl crate::entities::traits::Transformable for MultiLeader {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn block_content_insert(
|
||||
document: &acadrust::CadDocument,
|
||||
ml: &MultiLeader,
|
||||
) -> Option<acadrust::entities::Insert> {
|
||||
if ml.content_type != LeaderContentType::Block || !ml.context.has_block_contents {
|
||||
return None;
|
||||
}
|
||||
let record = ml.block_content_handle.and_then(|handle| {
|
||||
document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.handle == handle)
|
||||
})?;
|
||||
let mut insertion = ml.context.block_content_location;
|
||||
if ml.block_connection_type == BlockContentConnectionType::BlockExtents {
|
||||
let mut bounds = [f32::MAX, f32::MAX, f32::MIN, f32::MIN];
|
||||
for handle in &record.entity_handles {
|
||||
if let Some(entity) = document.get_entity(*handle) {
|
||||
let aabb = crate::scene::convert::tess::entity_aabb(entity);
|
||||
if aabb != crate::scene::model::wire_model::WireModel::UNBOUNDED_AABB {
|
||||
bounds[0] = bounds[0].min(aabb[0]);
|
||||
bounds[1] = bounds[1].min(aabb[1]);
|
||||
bounds[2] = bounds[2].max(aabb[2]);
|
||||
bounds[3] = bounds[3].max(aabb[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if bounds[0] <= bounds[2] && bounds[1] <= bounds[3] {
|
||||
let to_block = Transform::rotation(-ml.block_rotation);
|
||||
let from_left = ml.context.leader_roots.first().map_or(true, |root| {
|
||||
to_block.apply_vector([root.direction.x, root.direction.y])[0] >= 0.0
|
||||
});
|
||||
let anchor_x = if from_left { bounds[0] } else { bounds[2] } as f64;
|
||||
let anchor_y = ((bounds[1] + bounds[3]) * 0.5) as f64;
|
||||
let local_x = (anchor_x - record.base_point.x) * ml.block_scale.x;
|
||||
let local_y = (anchor_y - record.base_point.y) * ml.block_scale.y;
|
||||
let rotated =
|
||||
Transform::rotation(ml.block_rotation).apply_vector([local_x, local_y]);
|
||||
insertion.x -= rotated[0];
|
||||
insertion.y -= rotated[1];
|
||||
}
|
||||
}
|
||||
let mut insert = acadrust::entities::Insert::new(record.name.clone(), insertion);
|
||||
insert.set_x_scale(ml.block_scale.x);
|
||||
insert.set_y_scale(ml.block_scale.y);
|
||||
insert.set_z_scale(ml.block_scale.z);
|
||||
insert.rotation = ml.block_rotation;
|
||||
insert.normal = ml.context.block_content_normal;
|
||||
insert.common = ml.common.clone();
|
||||
insert.common.color = ml.block_content_color.clone();
|
||||
Some(insert)
|
||||
}
|
||||
|
||||
/// Per-entity tessellation entry for `MultiLeader`. Returns multiple
|
||||
/// `WireModel`s (leader/dogleg + arrow fill, optional block content,
|
||||
/// text strokes, frame, background fill) so each piece can carry its
|
||||
@@ -1713,6 +1766,10 @@ pub trait MultiLeaderTess {
|
||||
entity_color: [f32; 4],
|
||||
line_weight_px: f32,
|
||||
anno_scale: f32,
|
||||
active_viewport: Option<acadrust::Handle>,
|
||||
annotation_scale_handle: Option<acadrust::Handle>,
|
||||
block_cache: Option<&crate::scene::cache::block_cache::BlockCache>,
|
||||
view_aabb: Option<[f32; 4]>,
|
||||
world_per_pixel: Option<f32>,
|
||||
bg_color: [f32; 4],
|
||||
) -> Vec<crate::scene::model::wire_model::WireModel>;
|
||||
@@ -1727,11 +1784,15 @@ impl MultiLeaderTess for MultiLeader {
|
||||
entity_color: [f32; 4],
|
||||
line_weight_px: f32,
|
||||
anno_scale: f32,
|
||||
_world_per_pixel: Option<f32>,
|
||||
active_viewport: Option<acadrust::Handle>,
|
||||
annotation_scale_handle: Option<acadrust::Handle>,
|
||||
block_cache: Option<&crate::scene::cache::block_cache::BlockCache>,
|
||||
view_aabb: Option<[f32; 4]>,
|
||||
world_per_pixel: Option<f32>,
|
||||
bg_color: [f32; 4],
|
||||
) -> Vec<crate::scene::model::wire_model::WireModel> {
|
||||
use crate::scene::convert::tessellate::{
|
||||
append_arrow, arrow_from_block, color_or_inherit, tessellate, ArrowKind, DimGeom,
|
||||
append_arrow, arrow_from_block, color_or_inherit, ArrowKind, DimGeom,
|
||||
};
|
||||
use crate::scene::model::wire_model::{SnapHint, TangentGeom, WireModel};
|
||||
use glam::Vec3;
|
||||
@@ -2043,117 +2104,25 @@ impl MultiLeaderTess for MultiLeader {
|
||||
fill_tris_low: Vec::new(),
|
||||
});
|
||||
|
||||
// ── Block content ───────────────────────────────────────────────────────
|
||||
// When content_type == Block, the MultiLeader displays a block reference
|
||||
// at block_content_location with the recorded rotation/scale. A
|
||||
// synthetic Insert supplies that context to the shared scene graph;
|
||||
// every child then uses its normal tessellator.
|
||||
if ml.content_type == LeaderContentType::Block && ml.context.has_block_contents {
|
||||
let block_record = match ml.block_content_handle {
|
||||
Some(h) if !h.is_null() => document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|br| br.handle == h)
|
||||
.cloned(),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(block_record) = block_record {
|
||||
let block_name = block_record.name.clone();
|
||||
let block_color = if selected {
|
||||
line_color
|
||||
} else {
|
||||
color_or_inherit(&ml.block_content_color, entity_color)
|
||||
};
|
||||
let mut insertion = ml.context.block_content_location;
|
||||
if ml.block_connection_type == BlockContentConnectionType::BlockExtents {
|
||||
let mut bounds = [f32::MAX, f32::MAX, f32::MIN, f32::MIN];
|
||||
for entity_handle in &block_record.entity_handles {
|
||||
if let Some(entity) = document.get_entity(*entity_handle) {
|
||||
let aabb = crate::scene::convert::tess::entity_aabb(entity);
|
||||
if aabb != WireModel::UNBOUNDED_AABB {
|
||||
bounds[0] = bounds[0].min(aabb[0]);
|
||||
bounds[1] = bounds[1].min(aabb[1]);
|
||||
bounds[2] = bounds[2].max(aabb[2]);
|
||||
bounds[3] = bounds[3].max(aabb[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if bounds[0] <= bounds[2] && bounds[1] <= bounds[3] {
|
||||
let to_block = Transform::rotation(-ml.block_rotation);
|
||||
let from_left = ml
|
||||
.context
|
||||
.leader_roots
|
||||
.first()
|
||||
.map_or(true, |root| {
|
||||
to_block.apply_vector([
|
||||
root.direction.x,
|
||||
root.direction.y,
|
||||
])[0]
|
||||
>= 0.0
|
||||
});
|
||||
let anchor_x = if from_left { bounds[0] } else { bounds[2] } as f64;
|
||||
let anchor_y = ((bounds[1] + bounds[3]) * 0.5) as f64;
|
||||
let local_x =
|
||||
(anchor_x - block_record.base_point.x) * ml.block_scale.x;
|
||||
let local_y =
|
||||
(anchor_y - block_record.base_point.y) * ml.block_scale.y;
|
||||
let rotated = Transform::rotation(ml.block_rotation)
|
||||
.apply_vector([local_x, local_y]);
|
||||
insertion.x -= rotated[0];
|
||||
insertion.y -= rotated[1];
|
||||
}
|
||||
}
|
||||
let mut synth_ins = acadrust::entities::Insert::new(block_name, insertion);
|
||||
synth_ins.set_x_scale(ml.block_scale.x);
|
||||
synth_ins.set_y_scale(ml.block_scale.y);
|
||||
synth_ins.set_z_scale(ml.block_scale.z);
|
||||
synth_ins.rotation = ml.block_rotation;
|
||||
synth_ins.common.layer = ml.common.layer.clone();
|
||||
let depths = rustc_hash::FxHashMap::default();
|
||||
let graph = crate::scene::render_graph::RenderSceneGraph::new(
|
||||
if let Some(insert) = block_content_insert(document, ml) {
|
||||
wires.extend(
|
||||
crate::scene::convert::tess::expand_block_object(
|
||||
document,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
&depths,
|
||||
);
|
||||
graph.walk_insert(
|
||||
&synth_ins,
|
||||
&insert,
|
||||
handle,
|
||||
|_, _| true,
|
||||
|entity, context| {
|
||||
let mut placed = entity.clone();
|
||||
placed.apply_transform(&context.transform);
|
||||
let mut sub_wires = tessellate(
|
||||
document,
|
||||
handle,
|
||||
&placed,
|
||||
selected,
|
||||
block_color,
|
||||
leader_pat_len,
|
||||
leader_pat,
|
||||
leader_lw_px,
|
||||
1.0,
|
||||
None,
|
||||
None,
|
||||
bg_color,
|
||||
false,
|
||||
);
|
||||
for w in &mut sub_wires {
|
||||
w.name = name.clone();
|
||||
}
|
||||
wires.extend(sub_wires);
|
||||
},
|
||||
);
|
||||
// Block attributes attached to the multileader — render each as
|
||||
// its own attribute entity at WCS location like INSERT does.
|
||||
for ba in &ml.block_attributes {
|
||||
let _ = ba; // BlockAttribute carries only the value override
|
||||
// string; we'd need the AttributeDefinition handle
|
||||
// to materialise it as ATTRIB geometry. Skipped
|
||||
// until that wiring exists.
|
||||
}
|
||||
}
|
||||
selected,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
1.0,
|
||||
annotation_scale_handle,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
1.0,
|
||||
crate::scene::convert::tess::BlockObjectOptions::default(),
|
||||
)
|
||||
.wires,
|
||||
);
|
||||
}
|
||||
|
||||
// ── Text strokes / frame / background fill ──────────────────────────────
|
||||
|
||||
Vendored
+87
-96
@@ -42,6 +42,7 @@ pub struct LocalWire {
|
||||
/// Low-bit residual paired with `points` so block-instance wires keep
|
||||
/// sub-f32 precision once the renderer translates them to world space.
|
||||
pub points_low: Vec<[f32; 3]>,
|
||||
pub is_point: bool,
|
||||
pub point_marker: Option<PointMarker>,
|
||||
/// SDF glyph quads for block-internal text, in block-local coordinates.
|
||||
/// Non-empty only when SDF text is on and this sub is a TEXT/MTEXT. The
|
||||
@@ -126,6 +127,7 @@ pub struct NestedRef {
|
||||
/// The nested INSERT's own signed draw-order rank in (-1,1) within the
|
||||
/// parent block — narrows the depth sub-range its children may occupy.
|
||||
pub local_rank: f32,
|
||||
pub suppress_root_points: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -172,6 +174,7 @@ struct ExpansionPrototypeKey {
|
||||
insert_style: Vec<u32>,
|
||||
selected: bool,
|
||||
is_xref: bool,
|
||||
suppress_root_points: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -207,11 +210,8 @@ impl BlockCache {
|
||||
self.defns.get(block_name)
|
||||
}
|
||||
|
||||
/// Build (flat) defns only for block records actually referenced by
|
||||
/// Inserts in the document — transitively, so nested-insert targets are
|
||||
/// included too. The Model_Space / Paper_Space block_records are skipped
|
||||
/// because their entities are emitted as top-level wires, not via the
|
||||
/// cache.
|
||||
/// Build definitions referenced by block-backed entities. Space records stay
|
||||
/// roots so their direct entities preserve individual selection handles.
|
||||
pub fn build(
|
||||
doc: &CadDocument,
|
||||
anno_scale: f32,
|
||||
@@ -395,16 +395,24 @@ impl BlockCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Walk all entities + all block_record contents collecting every distinct
|
||||
/// `block_name` that appears in an Insert (transitively).
|
||||
fn block_object_name(doc: &CadDocument, entity: &EntityType) -> Option<String> {
|
||||
if let EntityType::Insert(insert) = entity {
|
||||
Some(insert.block_name.clone())
|
||||
} else {
|
||||
crate::scene::render_graph::owned_block_insert(doc, entity)
|
||||
.map(|owned| owned.insert.block_name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect every referenced block-backed object.
|
||||
fn collect_referenced_blocks(doc: &CadDocument) -> Vec<String> {
|
||||
let mut seen: HashSet<String> = HashSet::default();
|
||||
let mut queue: Vec<String> = Vec::new();
|
||||
|
||||
for entity in doc.entities() {
|
||||
if let EntityType::Insert(ins) = entity {
|
||||
if seen.insert(ins.block_name.clone()) {
|
||||
queue.push(ins.block_name.clone());
|
||||
if let Some(name) = block_object_name(doc, entity) {
|
||||
if seen.insert(name.clone()) {
|
||||
queue.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -416,9 +424,9 @@ fn collect_referenced_blocks(doc: &CadDocument) -> Vec<String> {
|
||||
let Some(entity) = doc.get_entity(eh) else {
|
||||
continue;
|
||||
};
|
||||
if let EntityType::Insert(ins) = entity {
|
||||
if seen.insert(ins.block_name.clone()) {
|
||||
queue.push(ins.block_name.clone());
|
||||
if let Some(name) = block_object_name(doc, entity) {
|
||||
if seen.insert(name.clone()) {
|
||||
queue.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,11 +443,13 @@ fn referenced_block_tree(doc: &CadDocument, root: &str) -> Vec<String> {
|
||||
continue;
|
||||
};
|
||||
for &handle in &record.entity_handles {
|
||||
let Some(EntityType::Insert(insert)) = doc.get_entity(handle) else {
|
||||
let Some(entity) = doc.get_entity(handle) else {
|
||||
continue;
|
||||
};
|
||||
if seen.insert(insert.block_name.clone()) {
|
||||
queue.push(insert.block_name.clone());
|
||||
if let Some(name) = block_object_name(doc, entity) {
|
||||
if seen.insert(name.clone()) {
|
||||
queue.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -510,6 +520,20 @@ fn build_defn(
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if let Some(owned) = crate::scene::render_graph::owned_block_insert(doc, entity) {
|
||||
if owned.render_picture {
|
||||
let mut nested = build_nested_ref(
|
||||
&owned.insert,
|
||||
doc,
|
||||
bg_color,
|
||||
viewport,
|
||||
depth_map,
|
||||
);
|
||||
nested.suppress_root_points = owned.suppress_root_points;
|
||||
subs.push(LocalSub::Nested(nested));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
match entity {
|
||||
EntityType::Block(_) | EntityType::BlockEnd(_) => continue,
|
||||
// A non-constant ATTDEF is only a template — the insert supplies an
|
||||
@@ -543,91 +567,30 @@ fn build_defn(
|
||||
subs.push(LocalSub::Wire(wire));
|
||||
}
|
||||
}
|
||||
// A nested table's stored graphics use the same scene-graph
|
||||
// traversal as top-level table content. The resulting leaf wires
|
||||
// remain local to this cached definition; the parent instance
|
||||
// transform is composed later by the cache expansion.
|
||||
EntityType::Table(table) => {
|
||||
let baked = table.block_record_handle.and_then(|handle| {
|
||||
doc.block_records
|
||||
.iter()
|
||||
.find(|record| record.handle == handle)
|
||||
});
|
||||
if let Some(record) = baked.filter(|record| {
|
||||
!record.entity_handles.is_empty()
|
||||
}) {
|
||||
let table_plot_l0 = crate::scene::view::render::is_effective_layer_zero(
|
||||
&table.common.layer,
|
||||
);
|
||||
let table_plot_visible = doc
|
||||
.layers
|
||||
.get(&table.common.layer)
|
||||
.map(|layer| layer.is_plottable)
|
||||
.unwrap_or(true);
|
||||
let mut insert = acadrust::entities::Insert::new(
|
||||
record.name.clone(),
|
||||
table.insertion_point,
|
||||
);
|
||||
insert.rotation = table
|
||||
.horizontal_direction
|
||||
.y
|
||||
.atan2(table.horizontal_direction.x);
|
||||
insert.common = table.common.clone();
|
||||
let graph = crate::scene::render_graph::RenderSceneGraph::new(
|
||||
doc,
|
||||
None,
|
||||
annotation_scale_handle,
|
||||
all_visible,
|
||||
depth_map,
|
||||
)
|
||||
.with_viewport(viewport);
|
||||
graph.walk_insert(
|
||||
for wire in tessellate_sub_local(
|
||||
doc,
|
||||
entity,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
bg_color,
|
||||
viewport,
|
||||
depth_map,
|
||||
) {
|
||||
subs.push(LocalSub::Wire(wire));
|
||||
}
|
||||
for insert in crate::entities::table::block_cell_inserts(
|
||||
table,
|
||||
doc,
|
||||
anno_scale,
|
||||
) {
|
||||
subs.push(LocalSub::Nested(build_nested_ref(
|
||||
&insert,
|
||||
table.common.handle,
|
||||
|_, _| true,
|
||||
|leaf, context| {
|
||||
let mut placed = leaf.clone();
|
||||
placed.apply_transform(&context.transform);
|
||||
for mut wire in tessellate_sub_local(
|
||||
doc,
|
||||
&placed,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
bg_color,
|
||||
viewport,
|
||||
depth_map,
|
||||
) {
|
||||
wire.plot_visible &= table_plot_l0 || table_plot_visible;
|
||||
wire.plot_l0 |= table_plot_l0;
|
||||
subs.push(LocalSub::Wire(wire));
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
for wire in tessellate_sub_local(
|
||||
doc,
|
||||
entity,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
bg_color,
|
||||
viewport,
|
||||
depth_map,
|
||||
) {
|
||||
subs.push(LocalSub::Wire(wire));
|
||||
}
|
||||
for insert in crate::entities::table::block_cell_inserts(
|
||||
table,
|
||||
doc,
|
||||
anno_scale,
|
||||
) {
|
||||
subs.push(LocalSub::Nested(build_nested_ref(
|
||||
&insert,
|
||||
doc,
|
||||
bg_color,
|
||||
viewport,
|
||||
depth_map,
|
||||
)));
|
||||
}
|
||||
)));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -695,6 +658,7 @@ fn build_nested_ref(
|
||||
local_rank: depth_map
|
||||
.get(&nested_ins.common.handle.value())
|
||||
.map_or(0.0, |d| d[0]),
|
||||
suppress_root_points: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,6 +814,7 @@ fn tessellate_sub_local(
|
||||
result.push(LocalWire {
|
||||
points: wire.points,
|
||||
points_low: wire.points_low,
|
||||
is_point: matches!(sub, EntityType::Point(_)),
|
||||
point_marker: wire.point_marker,
|
||||
text_verts: wire.text_verts,
|
||||
key_vertices: wire.key_vertices,
|
||||
@@ -1005,6 +970,9 @@ pub fn expand_insert(
|
||||
// Current annotation scale. An annotative block scales as one uniform unit
|
||||
// about its insertion point; a non-annotative block is unaffected.
|
||||
anno_scale: f32,
|
||||
// Dimension picture blocks store definition POINTs at their root. They are
|
||||
// metadata, not visible geometry; nested POINTs remain regular block data.
|
||||
suppress_root_points: bool,
|
||||
) -> Option<Vec<WireModel>> {
|
||||
let defn = cache.defn(&ins.block_name)?;
|
||||
let base = defn.base_point;
|
||||
@@ -1045,6 +1013,7 @@ pub fn expand_insert(
|
||||
is_xref,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
suppress_root_points,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
@@ -1148,6 +1117,7 @@ pub fn expand_insert(
|
||||
&mut first_batches,
|
||||
&mut visited,
|
||||
0,
|
||||
suppress_root_points,
|
||||
(0.0, 1.0),
|
||||
);
|
||||
let mut first = first_batches.finalize(&name, selected, bg_color);
|
||||
@@ -1185,7 +1155,16 @@ pub fn expand_insert(
|
||||
));
|
||||
translation.then(&xform)
|
||||
};
|
||||
expand_defn(defn, &base_xform, &ctx, &mut batches, &mut visited, 0, (0.0, 1.0));
|
||||
expand_defn(
|
||||
defn,
|
||||
&base_xform,
|
||||
&ctx,
|
||||
&mut batches,
|
||||
&mut visited,
|
||||
0,
|
||||
suppress_root_points,
|
||||
(0.0, 1.0),
|
||||
);
|
||||
}
|
||||
let mut wires = batches.finalize(&name, selected, bg_color);
|
||||
if let Some(guard) = prototype_guard.as_mut() {
|
||||
@@ -1230,6 +1209,7 @@ fn expansion_prototype_key(
|
||||
is_xref: bool,
|
||||
bg_color: [f32; 4],
|
||||
anno_scale: f32,
|
||||
suppress_root_points: bool,
|
||||
) -> ExpansionPrototypeKey {
|
||||
let matrix = &transform.matrix.m;
|
||||
let linear = [
|
||||
@@ -1262,6 +1242,7 @@ fn expansion_prototype_key(
|
||||
insert_style,
|
||||
selected,
|
||||
is_xref,
|
||||
suppress_root_points,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1384,6 +1365,7 @@ fn nested_prototype_key(
|
||||
transform: &Transform,
|
||||
ctx: &ExpandCtx<'_>,
|
||||
depth_scale: f32,
|
||||
suppress_root_points: bool,
|
||||
) -> NestedPrototypeKey {
|
||||
let matrix = &transform.matrix.m;
|
||||
let linear = [
|
||||
@@ -1411,6 +1393,7 @@ fn nested_prototype_key(
|
||||
selected: ctx.selected,
|
||||
is_xref: ctx.is_xref,
|
||||
depth_scale: depth_scale.to_bits(),
|
||||
suppress_root_points,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1527,6 +1510,7 @@ struct NestedPrototypeKey {
|
||||
selected: bool,
|
||||
is_xref: bool,
|
||||
depth_scale: u32,
|
||||
suppress_root_points: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -1766,6 +1750,7 @@ fn expand_defn(
|
||||
out: &mut Batches,
|
||||
visited: &mut Vec<String>,
|
||||
depth: usize,
|
||||
suppress_root_points: bool,
|
||||
// Block-local depth sub-range `(base, scale)` accumulated through nested
|
||||
// inserts: a child at rank r lands at `base + r * scale`, all within
|
||||
// (-1,1) of the top-level insert. Seeded `(0.0, 1.0)` by `expand_insert`.
|
||||
@@ -1778,6 +1763,9 @@ fn expand_defn(
|
||||
for sub in &defn.subs {
|
||||
match sub {
|
||||
LocalSub::Wire(lw) => {
|
||||
if suppress_root_points && lw.is_point {
|
||||
continue;
|
||||
}
|
||||
// `lw.aabb_local` is in the defn's offset frame; re-add
|
||||
// `defn_lo` (in f64) before composing with `accum_xform`
|
||||
// so culling uses correct world-space corners.
|
||||
@@ -1926,6 +1914,7 @@ fn expand_defn(
|
||||
&mut sub,
|
||||
visited,
|
||||
depth + 1,
|
||||
nref.suppress_root_points,
|
||||
nested_range,
|
||||
);
|
||||
let mut wires = sub.finalize("", ctx.selected, ctx.bg_color);
|
||||
@@ -1958,6 +1947,7 @@ fn expand_defn(
|
||||
&composed,
|
||||
&inner_ctx,
|
||||
nested_range.1,
|
||||
nref.suppress_root_points,
|
||||
);
|
||||
if let Some(cached) = out.nested_prototypes.get(&key).cloned() {
|
||||
let delta = [
|
||||
@@ -1986,6 +1976,7 @@ fn expand_defn(
|
||||
&mut sub,
|
||||
visited,
|
||||
depth + 1,
|
||||
nref.suppress_root_points,
|
||||
nested_range,
|
||||
);
|
||||
let mut wires = sub.finalize("", ctx.selected, ctx.bg_color);
|
||||
|
||||
+235
-277
@@ -17,6 +17,172 @@ fn fade_if_locked(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct BlockObjectStyle {
|
||||
color: [f32; 4],
|
||||
aci: u8,
|
||||
pattern_length: f32,
|
||||
pattern: [f32; 8],
|
||||
line_weight_px: f32,
|
||||
layer: view::render::InheritStyle,
|
||||
layer_aci: u8,
|
||||
layer_plottable: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct ExpandedBlockObject {
|
||||
pub(crate) wires: Vec<WireModel>,
|
||||
style: BlockObjectStyle,
|
||||
is_xref: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub(crate) struct BlockObjectOptions {
|
||||
pub(crate) suppress_root_points: bool,
|
||||
pub(crate) apply_xclip: bool,
|
||||
}
|
||||
|
||||
fn block_object_style(
|
||||
document: &acadrust::CadDocument,
|
||||
insert: &acadrust::entities::Insert,
|
||||
active_viewport: Option<Handle>,
|
||||
bg_color: [f32; 4],
|
||||
) -> BlockObjectStyle {
|
||||
let entity = EntityType::Insert(insert.clone());
|
||||
let (color, pattern_length, pattern, line_weight_px, aci) =
|
||||
view::render::render_style_for_viewport(document, &entity, active_viewport);
|
||||
let mut layer = view::render::layer_render_style_viewport(
|
||||
document,
|
||||
&insert.common.layer,
|
||||
active_viewport,
|
||||
);
|
||||
layer.color = view::render::adapt_to_bg(layer.color, bg_color);
|
||||
let layer_aci = document
|
||||
.layers
|
||||
.get(&insert.common.layer)
|
||||
.and_then(|layer| match &layer.color {
|
||||
acadrust::types::Color::Index(index) => Some(*index),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(0);
|
||||
let layer_plottable = document
|
||||
.layers
|
||||
.get(&insert.common.layer)
|
||||
.map(|layer| layer.is_plottable)
|
||||
.unwrap_or(true);
|
||||
BlockObjectStyle {
|
||||
color: view::render::adapt_to_bg(color, bg_color),
|
||||
aci,
|
||||
pattern_length,
|
||||
pattern,
|
||||
line_weight_px,
|
||||
layer,
|
||||
layer_aci,
|
||||
layer_plottable,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn expand_block_object(
|
||||
document: &acadrust::CadDocument,
|
||||
insert: &acadrust::entities::Insert,
|
||||
owner: Handle,
|
||||
selected: bool,
|
||||
active_viewport: Option<Handle>,
|
||||
bg_color: [f32; 4],
|
||||
anno_scale: f32,
|
||||
annotation_scale_handle: Option<Handle>,
|
||||
block_cache: Option<&cache::block_cache::BlockCache>,
|
||||
view_aabb: Option<[f32; 4]>,
|
||||
world_per_pixel: Option<f32>,
|
||||
pslt_factor: f32,
|
||||
options: BlockObjectOptions,
|
||||
) -> ExpandedBlockObject {
|
||||
let style = block_object_style(document, insert, active_viewport, bg_color);
|
||||
let fallback_depths = rustc_hash::FxHashMap::default();
|
||||
let fallback_cache;
|
||||
let cache = match block_cache {
|
||||
Some(cache) if cache.defn(&insert.block_name).is_some() => cache,
|
||||
_ => {
|
||||
fallback_cache = cache::block_cache::BlockCache::build_for_block(
|
||||
document,
|
||||
&insert.block_name,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
true,
|
||||
bg_color,
|
||||
active_viewport,
|
||||
&fallback_depths,
|
||||
);
|
||||
&fallback_cache
|
||||
}
|
||||
};
|
||||
let is_xref = document
|
||||
.block_records
|
||||
.get(&insert.block_name)
|
||||
.map(|record| record.flags.is_xref || record.flags.is_xref_overlay)
|
||||
.unwrap_or(false);
|
||||
let mut wires = cache::block_cache::expand_insert(
|
||||
cache,
|
||||
insert,
|
||||
owner,
|
||||
style.color,
|
||||
style.aci,
|
||||
style.pattern_length,
|
||||
style.pattern,
|
||||
style.line_weight_px,
|
||||
style.layer,
|
||||
style.layer_aci,
|
||||
style.layer_plottable,
|
||||
selected,
|
||||
pslt_factor,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
is_xref,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
options.suppress_root_points,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
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 polygon = pick::xclip::world_clip_polygon_for_transform(filter, &transform);
|
||||
pick::xclip::clip_wires(&mut wires, &polygon);
|
||||
for wire in &mut wires {
|
||||
if let Some(mut instance) = wire.render_instance {
|
||||
instance.source_id = cache.clip_source_id(
|
||||
instance.source_id,
|
||||
&polygon,
|
||||
instance.translation,
|
||||
);
|
||||
wire.render_instance = Some(instance);
|
||||
}
|
||||
}
|
||||
let frame_mode =
|
||||
crate::scene::frame::mode(document, crate::scene::frame::FrameKind::Xclip);
|
||||
if polygon.len() >= 3 {
|
||||
let mut frame = pick::xclip::frame_wire(
|
||||
&polygon,
|
||||
owner.value().to_string(),
|
||||
style.color,
|
||||
selected,
|
||||
style.line_weight_px,
|
||||
);
|
||||
frame.display_visible = frame_mode != 0;
|
||||
frame.plot_visible = frame_mode == 1;
|
||||
wires.push(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExpandedBlockObject {
|
||||
wires,
|
||||
style,
|
||||
is_xref,
|
||||
}
|
||||
}
|
||||
|
||||
/// The section mark's viewing direction (paper-space, unit), derived entirely
|
||||
/// from the decoded model-documentation view graph:
|
||||
///
|
||||
@@ -785,99 +951,32 @@ fn tessellate_entity_inner(
|
||||
|
||||
// Render non-annotative dimensions from their stored picture block.
|
||||
// Rebuild geometry only when no usable block exists.
|
||||
if let EntityType::Dimension(dim) = e {
|
||||
let baked = Some(dim.base().block_name.trim())
|
||||
.filter(|name| !name.is_empty())
|
||||
.filter(|_| !crate::scene::annotative::is_annotative(document, e))
|
||||
.and_then(|name| {
|
||||
document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.name.eq_ignore_ascii_case(name))
|
||||
})
|
||||
.filter(|record| !record.entity_handles.is_empty());
|
||||
if let Some(record) = baked {
|
||||
// The block's contents are already in world space and its base
|
||||
// point is the origin, so the instance that carries them is the
|
||||
// identity — it exists only to reuse the graph's ByBlock / layer-0
|
||||
// colour resolution and its handling of anything nested inside.
|
||||
let mut insert = acadrust::entities::Insert::new(
|
||||
record.name.clone(),
|
||||
acadrust::types::Vector3::ZERO,
|
||||
);
|
||||
insert.common = dim.base().common.clone();
|
||||
|
||||
let depths = rustc_hash::FxHashMap::default();
|
||||
let graph = crate::scene::render_graph::RenderSceneGraph::new(
|
||||
if matches!(e, EntityType::Dimension(_)) {
|
||||
if let Some(owned) = crate::scene::render_graph::owned_block_insert(document, e)
|
||||
.filter(|owned| owned.render_picture)
|
||||
{
|
||||
let mut wires = expand_block_object(
|
||||
document,
|
||||
None,
|
||||
annotation_scale_handle,
|
||||
true,
|
||||
&depths,
|
||||
);
|
||||
let mut wires = Vec::new();
|
||||
graph.walk_insert(
|
||||
&insert,
|
||||
&owned.insert,
|
||||
h,
|
||||
|sub, context| {
|
||||
// Direct POINTs are dimension definition markers.
|
||||
context.insert_path.len() != 1 || !matches!(sub, EntityType::Point(_))
|
||||
sel,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
pslt_factor,
|
||||
BlockObjectOptions {
|
||||
suppress_root_points: owned.suppress_root_points,
|
||||
..BlockObjectOptions::default()
|
||||
},
|
||||
|sub, context| {
|
||||
let has_book_color = view::render::has_resolved_book_color(document, sub);
|
||||
let color_byblock =
|
||||
!has_book_color && sub.common().color == acadrust::types::Color::ByBlock;
|
||||
let color_layer0 = !has_book_color
|
||||
&& view::render::is_effective_layer_zero(&sub.common().layer)
|
||||
&& sub.common().color == acadrust::types::Color::ByLayer;
|
||||
let contrast_bg = convert::tessellate::text_contrast_background(sub, bg_color);
|
||||
let sub_color = view::render::adapt_to_bg(
|
||||
view::render::render_style_for_viewport(
|
||||
document,
|
||||
sub,
|
||||
active_viewport,
|
||||
)
|
||||
.0,
|
||||
contrast_bg,
|
||||
);
|
||||
let style = context.style_for(document, sub);
|
||||
let resolved_color = view::render::adapt_to_bg(style.0, contrast_bg);
|
||||
let mut placed = sub.clone();
|
||||
placed.apply_transform(&context.transform);
|
||||
let sub_wires = tessellate_entity(
|
||||
document,
|
||||
selected,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
&placed,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
paper_space,
|
||||
);
|
||||
for mut wire in sub_wires {
|
||||
// Every wire answers to the dimension, so a pick selects
|
||||
// the dimension rather than one of its pieces.
|
||||
wire.name = h.value().to_string();
|
||||
if sel {
|
||||
wire.selected = true;
|
||||
wire.color = WireModel::SELECTED;
|
||||
} else if (color_byblock || color_layer0) && wire.color == sub_color {
|
||||
wire.color = resolved_color;
|
||||
wire.aci = style.4;
|
||||
}
|
||||
wires.push(wire);
|
||||
}
|
||||
},
|
||||
);
|
||||
)
|
||||
.wires;
|
||||
if !wires.is_empty() {
|
||||
let aabb = entity_aabb(e);
|
||||
for wire in &mut wires {
|
||||
// As below: only stroke / fill wires take the whole-dimension
|
||||
// box. An SDF-text wire keeps its own tight glyph box so the
|
||||
// text pick area hugs the text.
|
||||
if !wire.points.is_empty() || !wire.fill_tris.is_empty() {
|
||||
set_wire_aabb(wire, aabb);
|
||||
}
|
||||
@@ -927,6 +1026,10 @@ fn tessellate_entity_inner(
|
||||
entity_color,
|
||||
line_weight_px,
|
||||
anno_scale,
|
||||
active_viewport,
|
||||
annotation_scale_handle,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
bg_color,
|
||||
);
|
||||
@@ -951,92 +1054,31 @@ 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(record) = table.block_record_handle.and_then(|handle| {
|
||||
document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.handle == handle)
|
||||
}) {
|
||||
if !record.entity_handles.is_empty() {
|
||||
let mut insert =
|
||||
acadrust::entities::Insert::new(
|
||||
record.name.clone(),
|
||||
table.insertion_point,
|
||||
);
|
||||
insert.rotation = table
|
||||
.horizontal_direction
|
||||
.y
|
||||
.atan2(table.horizontal_direction.x);
|
||||
insert.common = table.common.clone();
|
||||
|
||||
let depths = rustc_hash::FxHashMap::default();
|
||||
let graph = crate::scene::render_graph::RenderSceneGraph::new(
|
||||
document,
|
||||
None,
|
||||
annotation_scale_handle,
|
||||
true,
|
||||
&depths,
|
||||
);
|
||||
let mut wires = Vec::new();
|
||||
graph.walk_insert(
|
||||
&insert,
|
||||
h,
|
||||
|_, _| true,
|
||||
|sub, context| {
|
||||
let has_book_color =
|
||||
view::render::has_resolved_book_color(document, sub);
|
||||
let color_byblock = !has_book_color
|
||||
&& sub.common().color
|
||||
== acadrust::types::Color::ByBlock;
|
||||
let color_layer0 = !has_book_color
|
||||
&& view::render::is_effective_layer_zero(
|
||||
&sub.common().layer,
|
||||
)
|
||||
&& sub.common().color
|
||||
== acadrust::types::Color::ByLayer;
|
||||
let style = context.style_for(document, sub);
|
||||
let mut placed = sub.clone();
|
||||
placed.apply_transform(&context.transform);
|
||||
let sub_wires = tessellate_entity(
|
||||
document,
|
||||
selected,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
&placed,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
paper_space,
|
||||
);
|
||||
for mut wire in sub_wires {
|
||||
wire.name = h.value().to_string();
|
||||
if color_byblock {
|
||||
wire.color = if sel {
|
||||
WireModel::SELECTED
|
||||
} else {
|
||||
view::render::adapt_to_bg(style.0, bg_color)
|
||||
};
|
||||
wire.aci = style.4;
|
||||
} else if color_layer0 && !sel {
|
||||
wire.color =
|
||||
view::render::adapt_to_bg(style.0, bg_color);
|
||||
wire.aci = style.4;
|
||||
}
|
||||
wires.push(wire);
|
||||
}
|
||||
},
|
||||
);
|
||||
if !wires.is_empty() {
|
||||
let aabb = entity_aabb(e);
|
||||
for wire in &mut wires {
|
||||
if !wire.points.is_empty() || !wire.fill_tris.is_empty() {
|
||||
set_wire_aabb(wire, aabb);
|
||||
}
|
||||
if let Some(owned) = crate::scene::render_graph::owned_block_insert(document, e) {
|
||||
let mut wires = expand_block_object(
|
||||
document,
|
||||
&owned.insert,
|
||||
h,
|
||||
sel,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
pslt_factor,
|
||||
BlockObjectOptions::default(),
|
||||
)
|
||||
.wires;
|
||||
if !wires.is_empty() {
|
||||
let aabb = entity_aabb(e);
|
||||
for wire in &mut wires {
|
||||
if !wire.points.is_empty() || !wire.fill_tris.is_empty() {
|
||||
set_wire_aabb(wire, aabb);
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1079,34 +1121,6 @@ fn tessellate_entity_inner(
|
||||
}
|
||||
|
||||
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, ins_aci) =
|
||||
view::render::render_style_for_viewport(document, e, active_viewport);
|
||||
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).
|
||||
let ins_layer = {
|
||||
let mut s = view::render::layer_render_style_viewport(
|
||||
document,
|
||||
&ins.common.layer,
|
||||
active_viewport,
|
||||
);
|
||||
s.color = view::render::adapt_to_bg(s.color, bg_color);
|
||||
s
|
||||
};
|
||||
let ins_layer_aci = document
|
||||
.layers
|
||||
.get(&ins.common.layer)
|
||||
.and_then(|layer| match &layer.color {
|
||||
acadrust::types::Color::Index(index) => Some(*index),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(0);
|
||||
let ins_layer_plottable = document
|
||||
.layers
|
||||
.get(&ins.common.layer)
|
||||
.map(|layer| layer.is_plottable)
|
||||
.unwrap_or(true);
|
||||
let ip = glam::Vec3::new(
|
||||
(ins.insert_point.x) as f32,
|
||||
(ins.insert_point.y) as f32,
|
||||
@@ -1146,83 +1160,27 @@ fn tessellate_entity_inner(
|
||||
fill_tris_low: Vec::new(),
|
||||
};
|
||||
|
||||
let fallback_depths = rustc_hash::FxHashMap::default();
|
||||
let fallback_cache;
|
||||
let cache = match block_cache {
|
||||
Some(cache) if cache.defn(&ins.block_name).is_some() => cache,
|
||||
_ => {
|
||||
fallback_cache = cache::block_cache::BlockCache::build_for_block(
|
||||
document,
|
||||
&ins.block_name,
|
||||
anno_scale,
|
||||
annotation_scale_handle,
|
||||
true,
|
||||
bg_color,
|
||||
active_viewport,
|
||||
&fallback_depths,
|
||||
);
|
||||
&fallback_cache
|
||||
}
|
||||
};
|
||||
let is_xref = document
|
||||
.block_records
|
||||
.get(&ins.block_name)
|
||||
.map(|record| record.flags.is_xref || record.flags.is_xref_overlay)
|
||||
.unwrap_or(false);
|
||||
let mut wires = cache::block_cache::expand_insert(
|
||||
cache,
|
||||
let expanded = expand_block_object(
|
||||
document,
|
||||
ins,
|
||||
h,
|
||||
ins_color,
|
||||
ins_aci,
|
||||
ins_pat_len,
|
||||
ins_pat,
|
||||
ins_lw_px,
|
||||
ins_layer,
|
||||
ins_layer_aci,
|
||||
ins_layer_plottable,
|
||||
sel,
|
||||
pslt_factor,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
is_xref,
|
||||
active_viewport,
|
||||
bg_color,
|
||||
anno_scale,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(filter) = pick::xclip::insert_spatial_filter(document, ins) {
|
||||
let transform = crate::scene::render_graph::insert_transform(document, ins);
|
||||
let polygon =
|
||||
pick::xclip::world_clip_polygon_for_transform(filter, &transform);
|
||||
pick::xclip::clip_wires(&mut wires, &polygon);
|
||||
for wire in &mut wires {
|
||||
if let Some(mut instance) = wire.render_instance {
|
||||
instance.source_id = cache.clip_source_id(
|
||||
instance.source_id,
|
||||
&polygon,
|
||||
instance.translation,
|
||||
);
|
||||
wire.render_instance = Some(instance);
|
||||
}
|
||||
}
|
||||
let frame_mode = crate::scene::frame::mode(
|
||||
document,
|
||||
crate::scene::frame::FrameKind::Xclip,
|
||||
);
|
||||
if polygon.len() >= 3 {
|
||||
let mut frame = pick::xclip::frame_wire(
|
||||
&polygon,
|
||||
h.value().to_string(),
|
||||
ins_color,
|
||||
sel,
|
||||
ins_lw_px,
|
||||
);
|
||||
frame.display_visible = frame_mode != 0;
|
||||
frame.plot_visible = frame_mode == 1;
|
||||
wires.push(frame);
|
||||
}
|
||||
}
|
||||
annotation_scale_handle,
|
||||
block_cache,
|
||||
view_aabb,
|
||||
world_per_pixel,
|
||||
pslt_factor,
|
||||
BlockObjectOptions {
|
||||
apply_xclip: true,
|
||||
..BlockObjectOptions::default()
|
||||
},
|
||||
);
|
||||
let style = expanded.style;
|
||||
let is_xref = expanded.is_xref;
|
||||
let mut wires = expanded.wires;
|
||||
|
||||
crate::entities::insert::append_insert_attribute_wires(
|
||||
&mut wires,
|
||||
@@ -1230,12 +1188,12 @@ fn tessellate_entity_inner(
|
||||
ins,
|
||||
h,
|
||||
sel,
|
||||
ins_color,
|
||||
ins_pat_len,
|
||||
ins_pat,
|
||||
ins_lw_px,
|
||||
ins_layer,
|
||||
ins_layer_plottable,
|
||||
style.color,
|
||||
style.pattern_length,
|
||||
style.pattern,
|
||||
style.line_weight_px,
|
||||
style.layer,
|
||||
style.layer_plottable,
|
||||
bg_color,
|
||||
is_xref,
|
||||
pslt_factor,
|
||||
|
||||
+69
-103
@@ -157,35 +157,6 @@ impl BlockStyle {
|
||||
InsertStyleSpec::new(document, insert, viewport).resolve(parent)
|
||||
}
|
||||
|
||||
pub fn for_owned(
|
||||
document: &CadDocument,
|
||||
entity: &EntityType,
|
||||
parent: Option<Self>,
|
||||
viewport: Option<Handle>,
|
||||
) -> Self {
|
||||
let on_layer0 = is_effective_layer_zero(&entity.common().layer);
|
||||
let insert = parent
|
||||
.map(|style| style.resolve(document, entity, viewport))
|
||||
.unwrap_or_else(|| render_style_for_viewport(document, entity, viewport));
|
||||
Self {
|
||||
insert,
|
||||
layer0: if on_layer0 {
|
||||
parent
|
||||
.map(|style| style.layer0)
|
||||
.unwrap_or_else(|| layer_render_style_viewport(document, &entity.common().layer, viewport))
|
||||
} else {
|
||||
layer_render_style_viewport(document, &entity.common().layer, viewport)
|
||||
},
|
||||
layer0_aci: if on_layer0 {
|
||||
parent
|
||||
.map(|style| style.layer0_aci)
|
||||
.unwrap_or_else(|| layer_aci(document, &entity.common().layer))
|
||||
} else {
|
||||
layer_aci(document, &entity.common().layer)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(
|
||||
self,
|
||||
document: &CadDocument,
|
||||
@@ -528,81 +499,16 @@ impl<'a> RenderSceneGraph<'a> {
|
||||
V: FnMut(&EntityType, &RenderContext) -> bool,
|
||||
F: FnMut(&EntityType, &RenderContext),
|
||||
{
|
||||
match entity {
|
||||
EntityType::Dimension(dimension) => {
|
||||
let block_name = dimension.base().block_name.trim();
|
||||
if block_name.is_empty()
|
||||
|| stack
|
||||
.iter()
|
||||
.any(|name| name.eq_ignore_ascii_case(block_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let placement =
|
||||
Transform::from_translation(dimension.base().insertion_point)
|
||||
.then(&context.transform);
|
||||
let mut owned = context.clone();
|
||||
owned.transform = placement;
|
||||
if owned.root_handle.is_null() {
|
||||
owned.root_handle = entity.common().handle;
|
||||
}
|
||||
owned.block_style = Some(BlockStyle::for_owned(
|
||||
self.document,
|
||||
entity,
|
||||
context.block_style,
|
||||
self.viewport,
|
||||
));
|
||||
owned.nesting_depth += 1;
|
||||
stack.push(block_name.to_string());
|
||||
self.walk_block(block_name, &owned, visible, leaf, stack);
|
||||
stack.pop();
|
||||
}
|
||||
EntityType::Table(table) => {
|
||||
let Some(record) = table.block_record_handle.and_then(|handle| {
|
||||
self.document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.handle == handle)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
let mut insert = Insert::new(record.name.clone(), table.insertion_point);
|
||||
insert.rotation = table
|
||||
.horizontal_direction
|
||||
.y
|
||||
.atan2(table.horizontal_direction.x);
|
||||
insert.common = table.common.clone();
|
||||
self.walk_insert_instances(&insert, context, visible, leaf);
|
||||
}
|
||||
EntityType::MultiLeader(multileader)
|
||||
if matches!(
|
||||
multileader.content_type,
|
||||
acadrust::entities::LeaderContentType::Block
|
||||
) && multileader.context.has_block_contents =>
|
||||
{
|
||||
let Some(record) = multileader.block_content_handle.and_then(|handle| {
|
||||
self.document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.handle == handle)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
let mut insert = Insert::new(
|
||||
record.name.clone(),
|
||||
multileader.context.block_content_location,
|
||||
);
|
||||
insert.common = multileader.common.clone();
|
||||
insert.common.color = multileader.block_content_color.clone();
|
||||
insert.set_x_scale(multileader.block_scale.x);
|
||||
insert.set_y_scale(multileader.block_scale.y);
|
||||
insert.set_z_scale(multileader.block_scale.z);
|
||||
insert.rotation = multileader.block_rotation;
|
||||
insert.normal = multileader.context.block_content_normal;
|
||||
self.walk_insert_instances(&insert, context, visible, leaf);
|
||||
}
|
||||
_ => {}
|
||||
let Some(owned) = owned_block_insert(self.document, entity) else {
|
||||
return;
|
||||
};
|
||||
if stack
|
||||
.iter()
|
||||
.any(|name| name.eq_ignore_ascii_case(&owned.insert.block_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.walk_insert_instances(&owned.insert, context, visible, leaf);
|
||||
}
|
||||
|
||||
fn document_visible(&self, entity: &EntityType) -> bool {
|
||||
@@ -649,6 +555,66 @@ pub fn insert_transform(document: &CadDocument, insert: &Insert) -> Transform {
|
||||
.then(&insert.get_transform())
|
||||
}
|
||||
|
||||
pub struct OwnedBlockInsert {
|
||||
pub insert: Insert,
|
||||
pub suppress_root_points: bool,
|
||||
pub render_picture: bool,
|
||||
}
|
||||
|
||||
pub fn owned_block_insert(
|
||||
document: &CadDocument,
|
||||
entity: &EntityType,
|
||||
) -> Option<OwnedBlockInsert> {
|
||||
let (insert, suppress_root_points, render_picture) = match entity {
|
||||
EntityType::Dimension(dimension) => {
|
||||
let name = dimension.base().block_name.trim();
|
||||
let record = document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.name.eq_ignore_ascii_case(name))?;
|
||||
if record.entity_handles.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut insert = Insert::new(record.name.clone(), Vector3::ZERO);
|
||||
insert.common = dimension.base().common.clone();
|
||||
(
|
||||
insert,
|
||||
true,
|
||||
!crate::scene::annotative::is_annotative(document, entity),
|
||||
)
|
||||
}
|
||||
EntityType::Table(table) => {
|
||||
let record = table.block_record_handle.and_then(|handle| {
|
||||
document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.handle == handle)
|
||||
})?;
|
||||
if record.entity_handles.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut insert = Insert::new(record.name.clone(), table.insertion_point);
|
||||
insert.rotation = table
|
||||
.horizontal_direction
|
||||
.y
|
||||
.atan2(table.horizontal_direction.x);
|
||||
insert.common = table.common.clone();
|
||||
(insert, false, true)
|
||||
}
|
||||
EntityType::MultiLeader(multileader) => (
|
||||
crate::entities::multileader::block_content_insert(document, multileader)?,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
_ => return None,
|
||||
};
|
||||
Some(OwnedBlockInsert {
|
||||
insert,
|
||||
suppress_root_points,
|
||||
render_picture,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn array_offsets(insert: &Insert) -> Vec<[f64; 3]> {
|
||||
if !insert.is_minsert() {
|
||||
return vec![[0.0; 3]];
|
||||
|
||||
Reference in new issue
Block a user