forked from 1b-agent/opencadstudio-upstream
fix(polyline): render width bands for polylines inside blocks (#222)
Wide LwPolyline / Polyline2D fills only drew for entities directly in the layout — the model-space loop skips block-internal ones, and the block-explode fill pass handled hatches but not wide polylines. So a polyline with a width nested in a block drew at zero width (its centreline came from the wire path, the band fill never materialized). Extend the block-explode fill walk (exploded_insert_hatch_models) to emit wide-polyline bands at world position, gated by a new block_has_wide_poly scan mirroring block_has_hatch, with a shared push_block_wide_fills helper that resolves block-child colour inheritance and applies XCLIP. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
4df41cdf34
commit
910caa6f67
2 files changed
+148
-1
No files matched your search
+104
-1
@@ -618,6 +618,11 @@ impl Scene {
|
||||
// contain a hatch still explode exactly as before, so output is unchanged.
|
||||
let mut hatch_block_memo: std::collections::HashMap<String, bool> =
|
||||
std::collections::HashMap::new();
|
||||
// Same gate for wide LwPolyline / Polyline2D fills — a block with a wide
|
||||
// polyline (but no hatch) must still explode so its width band renders
|
||||
// at world position, like a block-internal hatch. (#222)
|
||||
let mut wide_block_memo: std::collections::HashMap<String, bool> =
|
||||
std::collections::HashMap::new();
|
||||
for entity in self.document.entities() {
|
||||
let EntityType::Insert(ins) = entity else {
|
||||
continue;
|
||||
@@ -632,7 +637,9 @@ impl Scene {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if !self.block_has_hatch(&ins.block_name, &mut hatch_block_memo) {
|
||||
if !self.block_has_hatch(&ins.block_name, &mut hatch_block_memo)
|
||||
&& !self.block_has_wide_poly(&ins.block_name, &mut wide_block_memo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let selected = tint_selected && self.selected.contains(&ins.common.handle);
|
||||
@@ -741,6 +748,45 @@ impl Scene {
|
||||
models.push(model);
|
||||
}
|
||||
}
|
||||
// Wide LwPolyline / Polyline2D inside a block: materialize its
|
||||
// width bands at world position, same as a block-internal
|
||||
// hatch. Without this a wide polyline in a block drew at zero
|
||||
// width (its centreline came from the wire path, the band
|
||||
// fill only from the model-space loop that skips blocks). (#222)
|
||||
EntityType::LwPolyline(pl) => {
|
||||
if pl.common.invisible || layer_hidden(&pl.common.layer) {
|
||||
continue;
|
||||
}
|
||||
let (origin, fills) =
|
||||
crate::entities::lwpolyline::wide_fills(&pl);
|
||||
self.push_block_wide_fills(
|
||||
&mut models,
|
||||
&EntityType::LwPolyline(pl),
|
||||
origin,
|
||||
fills,
|
||||
sub_ins_color,
|
||||
sub_l0,
|
||||
&clip_poly,
|
||||
selected,
|
||||
);
|
||||
}
|
||||
EntityType::Polyline2D(pl) => {
|
||||
if pl.common.invisible || layer_hidden(&pl.common.layer) {
|
||||
continue;
|
||||
}
|
||||
let (origin, fills) =
|
||||
crate::entities::polyline::wide_fills(&pl);
|
||||
self.push_block_wide_fills(
|
||||
&mut models,
|
||||
&EntityType::Polyline2D(pl),
|
||||
origin,
|
||||
fills,
|
||||
sub_ins_color,
|
||||
sub_l0,
|
||||
&clip_poly,
|
||||
selected,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -748,6 +794,63 @@ impl Scene {
|
||||
models
|
||||
}
|
||||
|
||||
/// Materialize a block-exploded wide polyline's width bands (from
|
||||
/// `wide_fills`, already in world coords) as solid `HatchModel`s: resolve the
|
||||
/// block-child colour inheritance (NO background adaptation — a width band is
|
||||
/// the polyline's own colour, not a hatch) and apply any XCLIP. Shared by the
|
||||
/// LwPolyline / Polyline2D arms of the block-explode fill walk. (#222)
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn push_block_wide_fills(
|
||||
&self,
|
||||
models: &mut Vec<HatchModel>,
|
||||
sub_entity: &EntityType,
|
||||
origin: [f64; 2],
|
||||
fills: Vec<Vec<[f32; 2]>>,
|
||||
sub_ins_color: [f32; 4],
|
||||
sub_l0: crate::scene::view::render::InheritStyle,
|
||||
clip_poly: &Option<Vec<[f32; 2]>>,
|
||||
selected: bool,
|
||||
) {
|
||||
if fills.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut color = crate::scene::view::render::render_style_for_block_sub(
|
||||
&self.document,
|
||||
sub_entity,
|
||||
sub_ins_color,
|
||||
0.0,
|
||||
[0.0; 8],
|
||||
0.0,
|
||||
sub_l0,
|
||||
)
|
||||
.0;
|
||||
if selected {
|
||||
color = [0.15, 0.55, 1.00, color[3]];
|
||||
}
|
||||
for boundary in fills {
|
||||
let ring = if let Some(poly) = clip_poly {
|
||||
let clipped = pick::xclip::clip_hatch_boundary(&boundary, origin, poly);
|
||||
if clipped.is_empty() {
|
||||
continue;
|
||||
}
|
||||
clipped
|
||||
} else {
|
||||
boundary
|
||||
};
|
||||
models.push(HatchModel {
|
||||
boundary: Arc::new(ring),
|
||||
pattern: model::hatch_model::HatchPattern::Solid,
|
||||
name: "SOLID".into(),
|
||||
color,
|
||||
angle_offset: 0.0,
|
||||
scale: 1.0,
|
||||
world_origin: origin,
|
||||
vp_scissor: None,
|
||||
draw_depth: 0.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Wipeout fill models — rendered in a separate pass AFTER wires so that
|
||||
/// wipeouts correctly mask everything below them in the draw order.
|
||||
pub(crate) fn wipeout_models(&self) -> Vec<HatchModel> {
|
||||
|
||||
@@ -2707,6 +2707,50 @@ impl Scene {
|
||||
result
|
||||
}
|
||||
|
||||
/// Transitively true when a block (or a block it nests) contains a wide
|
||||
/// LwPolyline / Polyline2D — one carrying a non-zero width. Gate for the
|
||||
/// block-explode wide-fill pass, mirroring [`Self::block_has_hatch`] so a
|
||||
/// solid-only block is never exploded just to look for width bands. (#222)
|
||||
fn block_has_wide_poly(
|
||||
&self,
|
||||
block_name: &str,
|
||||
memo: &mut std::collections::HashMap<String, bool>,
|
||||
) -> bool {
|
||||
if let Some(&v) = memo.get(block_name) {
|
||||
return v;
|
||||
}
|
||||
// Seed `false` first so a cyclic block reference terminates.
|
||||
memo.insert(block_name.to_string(), false);
|
||||
let result = self
|
||||
.document
|
||||
.block_records
|
||||
.get(block_name)
|
||||
.map(|br| {
|
||||
br.entity_handles.iter().any(|&h| match self.document.get_entity(h) {
|
||||
Some(EntityType::LwPolyline(p)) => {
|
||||
p.constant_width > 1e-9
|
||||
|| p.vertices
|
||||
.iter()
|
||||
.any(|v| v.start_width > 1e-9 || v.end_width > 1e-9)
|
||||
}
|
||||
Some(EntityType::Polyline2D(p)) => {
|
||||
p.start_width > 1e-9
|
||||
|| p.end_width > 1e-9
|
||||
|| p.vertices
|
||||
.iter()
|
||||
.any(|v| v.start_width > 1e-9 || v.end_width > 1e-9)
|
||||
}
|
||||
Some(EntityType::Insert(ins)) => {
|
||||
self.block_has_wide_poly(&ins.block_name, memo)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
.unwrap_or(false);
|
||||
memo.insert(block_name.to_string(), result);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn insert_hatches_for_click(&self) -> Arc<Vec<(Handle, HatchModel)>> {
|
||||
{
|
||||
let c = self.insert_hatch_cache.borrow();
|
||||
|
||||
Reference in new issue
Block a user