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 <noreply@anthropic.com>
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.
|
// contain a hatch still explode exactly as before, so output is unchanged.
|
||||||
let mut hatch_block_memo: std::collections::HashMap<String, bool> =
|
let mut hatch_block_memo: std::collections::HashMap<String, bool> =
|
||||||
std::collections::HashMap::new();
|
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() {
|
for entity in self.document.entities() {
|
||||||
let EntityType::Insert(ins) = entity else {
|
let EntityType::Insert(ins) = entity else {
|
||||||
continue;
|
continue;
|
||||||
@@ -632,7 +637,9 @@ impl Scene {
|
|||||||
) {
|
) {
|
||||||
continue;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
let selected = tint_selected && self.selected.contains(&ins.common.handle);
|
let selected = tint_selected && self.selected.contains(&ins.common.handle);
|
||||||
@@ -741,6 +748,45 @@ impl Scene {
|
|||||||
models.push(model);
|
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
|
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
|
/// Wipeout fill models — rendered in a separate pass AFTER wires so that
|
||||||
/// wipeouts correctly mask everything below them in the draw order.
|
/// wipeouts correctly mask everything below them in the draw order.
|
||||||
pub(crate) fn wipeout_models(&self) -> Vec<HatchModel> {
|
pub(crate) fn wipeout_models(&self) -> Vec<HatchModel> {
|
||||||
|
|||||||
@@ -2707,6 +2707,50 @@ impl Scene {
|
|||||||
result
|
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)>> {
|
pub fn insert_hatches_for_click(&self) -> Arc<Vec<(Handle, HatchModel)>> {
|
||||||
{
|
{
|
||||||
let c = self.insert_hatch_cache.borrow();
|
let c = self.insert_hatch_cache.borrow();
|
||||||
|
|||||||
Reference in new issue
Block a user