fix(wipeout): mask block-internal wipeouts once inserted
A wipeout defined inside a block never masked the geometry once the block was inserted — wipeout_models only scanned top-level entities, so the wipeout was invisible unless you entered block-edit (where it is top-level). It also drew a stray mask at its raw block-local coordinates because the top-level scan lacked the block-defn reject the hatch path uses. Descend visible inserts and place each block-internal wipeout in world space, applying the accumulated insert transform via apply / apply_rotation. Add the belongs_to_visible_block filter so block-defn wipeouts only appear through the descent, in world space. Bumps acadrust for the matching transform_wipeout fix: its u/v basis was double-scaled (apply_rotation then another scale-factor multiply), so a scaled insert grew the mask; the descent sidesteps that path but the explode/copy commands need the crate fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d54fb9e2aa
commit
de5c35d44a
2 changed files with 138 additions and 1 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -74,7 +74,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
|||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#022290a3a2a548c54c8fdf580ef5e44de36b4520"
|
||||
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#824270e9c0463b8fb926424c9ac61eda5c09d752"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
|
|
|||
|
|
@ -1101,6 +1101,7 @@ impl Scene {
|
|||
// pipeline's `wipeout_skip_flags` (compute_wipeout_lod) does
|
||||
// the per-frame skip at draw time instead.
|
||||
let depth_map = self.draw_depth_map();
|
||||
let layout_block = self.current_layout_block_handle();
|
||||
let mut models = Vec::new();
|
||||
for entity in self.document.entities() {
|
||||
let EntityType::Wipeout(wo) = entity else {
|
||||
|
|
@ -1109,6 +1110,17 @@ impl Scene {
|
|||
if entity.common().invisible {
|
||||
continue;
|
||||
}
|
||||
// Reject block-defn-only wipeouts (owned by a BLOCK record that is
|
||||
// neither model nor a paper layout block): those are placed via
|
||||
// insert descent below, in world space. Without this they also draw
|
||||
// once at their raw block-local coordinates. Mirrors the hatch path.
|
||||
if !self.belongs_to_visible_block(
|
||||
wo.common.handle,
|
||||
wo.common.owner_handle,
|
||||
layout_block,
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if self
|
||||
.document
|
||||
.layers
|
||||
|
|
@ -1150,9 +1162,134 @@ impl Scene {
|
|||
});
|
||||
}
|
||||
}
|
||||
// Wipeouts nested inside block inserts: the loop above only sees
|
||||
// top-level wipeouts, so a wipeout defined inside a block never masked
|
||||
// once the block was inserted (it did in block-edit, where it is
|
||||
// top-level). Descend visible inserts and place each block-internal
|
||||
// wipeout in world space. The insert transform is applied here (apply /
|
||||
// apply_rotation) rather than through Insert::explode — the latter
|
||||
// double-scales the u/v basis.
|
||||
for entity in self.document.entities() {
|
||||
let EntityType::Insert(ins) = entity else {
|
||||
continue;
|
||||
};
|
||||
let c = &ins.common;
|
||||
if c.invisible
|
||||
|| self
|
||||
.document
|
||||
.layers
|
||||
.get(&c.layer)
|
||||
.map(|l| l.flags.off || l.flags.frozen)
|
||||
.unwrap_or(false)
|
||||
|| self.layer_frozen_in(&c.layer, frozen)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if !self.belongs_to_visible_block(c.handle, c.owner_handle, layout_block) {
|
||||
continue;
|
||||
}
|
||||
if frozen.is_none()
|
||||
&& crate::scene::annotative::annotative_offscale(&self.document, c)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
self.collect_block_wipeouts(
|
||||
&ins.get_transform(),
|
||||
&ins.block_name,
|
||||
0,
|
||||
frozen,
|
||||
bg_color,
|
||||
&depth_map,
|
||||
&mut models,
|
||||
);
|
||||
}
|
||||
models
|
||||
}
|
||||
|
||||
/// Recursively collect wipeout masks defined inside a block, transformed to
|
||||
/// world space by the accumulated insert transform. See `wipeout_models`.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn collect_block_wipeouts(
|
||||
&self,
|
||||
xform: &acadrust::types::Transform,
|
||||
block_name: &str,
|
||||
depth: usize,
|
||||
frozen: Option<&rustc_hash::FxHashSet<Handle>>,
|
||||
bg_color: [f32; 4],
|
||||
depth_map: &HashMap<u64, [f32; 2]>,
|
||||
models: &mut Vec<HatchModel>,
|
||||
) {
|
||||
if depth > 32 {
|
||||
return;
|
||||
}
|
||||
let Some(br) = self
|
||||
.document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|b| b.name.eq_ignore_ascii_case(block_name))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
for &eh in &br.entity_handles {
|
||||
let Some(e) = self.document.get_entity(eh) else {
|
||||
continue;
|
||||
};
|
||||
let c = e.common();
|
||||
if c.invisible
|
||||
|| self
|
||||
.document
|
||||
.layers
|
||||
.get(&c.layer)
|
||||
.map(|l| l.flags.off || l.flags.frozen)
|
||||
.unwrap_or(false)
|
||||
|| self.layer_frozen_in(&c.layer, frozen)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
match e {
|
||||
EntityType::Wipeout(wo) => {
|
||||
let mut w = wo.clone();
|
||||
w.insertion_point = xform.apply(wo.insertion_point);
|
||||
w.u_vector = xform.apply_rotation(wo.u_vector);
|
||||
w.v_vector = xform.apply_rotation(wo.v_vector);
|
||||
let (fill_origin, boundary) = Self::wipeout_boundary_2d(&w);
|
||||
if boundary.len() >= 3 {
|
||||
let mut fill_color = bg_color;
|
||||
if self.selected.contains(&c.handle) {
|
||||
fill_color = [0.15, 0.55, 1.00, 0.35];
|
||||
}
|
||||
models.push(HatchModel {
|
||||
boundary: Arc::new(boundary),
|
||||
boundary_wcs: None,
|
||||
pattern: model::hatch_model::HatchPattern::Solid,
|
||||
name: "WIPEOUT_FILL".into(),
|
||||
color: fill_color,
|
||||
angle_offset: 0.0,
|
||||
scale: 1.0,
|
||||
world_origin: fill_origin,
|
||||
draw_depth: depth_map.get(&c.handle.value()).map_or(0.0, |d| d[0]),
|
||||
});
|
||||
}
|
||||
}
|
||||
EntityType::Insert(ni) => {
|
||||
// `compose` applies the nested insert's transform first,
|
||||
// then the accumulated parent transform.
|
||||
let child = xform.compose(&ni.get_transform());
|
||||
self.collect_block_wipeouts(
|
||||
&child,
|
||||
&ni.block_name,
|
||||
depth + 1,
|
||||
frozen,
|
||||
bg_color,
|
||||
depth_map,
|
||||
models,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the 2D (XY) boundary polygon for a Wipeout entity.
|
||||
/// Wipeout fill boundary as small f32 offsets from the returned `world_origin`
|
||||
/// (the insertion point, kept in f64). Building it in absolute WCS f32
|
||||
|
|
|
|||
Loading…
Reference in a new issue