From 7a1c0ead0df0eecdf6bdd5ab27247347016b974f Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 24 Jul 2026 18:43:04 +0300 Subject: [PATCH] fix(xref): preserve layer-zero colors Treat namespaced XREF layer |0 as layer zero during block inheritance so ByLayer geometry keeps its source color.\n\nCloses #437 --- src/scene/cache/block_cache.rs | 7 ++++--- src/scene/convert/tess.rs | 6 ++++-- src/scene/entity.rs | 9 +++++---- src/scene/mod.rs | 10 ++++++---- src/scene/view/render.rs | 14 +++++++++++++- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/scene/cache/block_cache.rs b/src/scene/cache/block_cache.rs index 6cf62b8de..dfec7d4b4 100644 --- a/src/scene/cache/block_cache.rs +++ b/src/scene/cache/block_cache.rs @@ -531,7 +531,9 @@ fn build_nested_ref( nested_ins.common.line_weight, LineWeight::ByLayer | LineWeight::Default ), - layer_is_zero: nested_ins.common.layer == "0", + layer_is_zero: crate::scene::view::render::is_effective_layer_zero( + &nested_ins.common.layer, + ), l0, instance_offsets: array_offsets(nested_ins), clip_poly, @@ -572,7 +574,7 @@ fn tessellate_sub_local( // Layer-0 rule: a child on layer "0" with ByLayer properties inherits the // INSERT's layer at expand time. Flag each ByLayer property so emit_wire // can override the cached (layer-0-resolved) value with the insert layer's. - let on_l0 = sub.common().layer == "0"; + let on_l0 = crate::scene::view::render::is_effective_layer_zero(&sub.common().layer); let color_l0 = on_l0 && sub.common().color == AcadColor::ByLayer; let lt_l0 = on_l0 && { let lt = &sub.common().linetype; @@ -1649,4 +1651,3 @@ fn array_offsets(ins: &acadrust::entities::Insert) -> Vec<[f64; 3]> { } offsets } - diff --git a/src/scene/convert/tess.rs b/src/scene/convert/tess.rs index de6426725..35e08d734 100644 --- a/src/scene/convert/tess.rs +++ b/src/scene/convert/tess.rs @@ -756,7 +756,8 @@ pub(crate) fn tessellate_entity( // they should inherit from the Dimension entity. let sub_color_is_byblock = sub.common().color == acadrust::types::Color::ByBlock; - let sub_is_l0_bylayer = sub.common().layer == "0" + let sub_is_l0_bylayer = + view::render::is_effective_layer_zero(&sub.common().layer) && sub.common().color == acadrust::types::Color::ByLayer; let sub_wires = tessellate_entity( document, @@ -909,7 +910,8 @@ pub(crate) fn tessellate_entity( }; let sub_color_is_byblock = sub.common().color == acadrust::types::Color::ByBlock; - let sub_is_l0_bylayer = sub.common().layer == "0" + let sub_is_l0_bylayer = + view::render::is_effective_layer_zero(&sub.common().layer) && sub.common().color == acadrust::types::Color::ByLayer; let mut placed = sub.clone(); { diff --git a/src/scene/entity.rs b/src/scene/entity.rs index 07fc148e9..84e673b8a 100644 --- a/src/scene/entity.rs +++ b/src/scene/entity.rs @@ -983,11 +983,12 @@ impl Scene { // mirroring expand_insert's nested resolution: ByBlock → // parent source; layer-0 + ByLayer → parent layer-0 // target; else the nested insert's own resolved style. + let on_l0 = crate::scene::view::render::is_effective_layer_zero( + &nins.common.layer, + ); let child_ins_color = if nins.common.color == Color::ByBlock { sub_ins_color - } else if nins.common.layer == "0" - && nins.common.color == Color::ByLayer - { + } else if on_l0 && nins.common.color == Color::ByLayer { sub_l0.color } else { crate::scene::view::render::render_style_for( @@ -996,7 +997,7 @@ impl Scene { ) .0 }; - let child_l0 = if nins.common.layer == "0" { + let child_l0 = if on_l0 { sub_l0 } else { crate::scene::view::render::layer_render_style( diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 494c02da2..b69a3f844 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -3973,9 +3973,11 @@ impl Scene { ) -> ([f32; 4], [f32; 4]) { use acadrust::types::Color; let bg = self.current_bg(); + let on_l0 = + crate::scene::view::render::is_effective_layer_zero(&ins.common.layer); let child_ins_color = if ins.common.color == Color::ByBlock { parent_ins_color - } else if ins.common.layer == "0" && ins.common.color == Color::ByLayer { + } else if on_l0 && ins.common.color == Color::ByLayer { parent_l0 } else { crate::scene::view::render::adapt_to_bg( @@ -3987,7 +3989,7 @@ impl Scene { bg, ) }; - let child_l0 = if ins.common.layer == "0" { + let child_l0 = if on_l0 { parent_l0 } else { crate::scene::view::render::adapt_to_bg( @@ -4013,9 +4015,10 @@ impl Scene { let (ins_color, l0_color) = inherit?; use acadrust::types::Color; let common = e.common(); + let on_l0 = crate::scene::view::render::is_effective_layer_zero(&common.layer); let mut c = if common.color == Color::ByBlock { ins_color - } else if common.layer == "0" && common.color == Color::ByLayer { + } else if on_l0 && common.color == Color::ByLayer { // Inherit the insert layer's RGB but keep the solid's own alpha, // matching the wire/hatch path (render_style_for_block_sub). [l0_color[0], l0_color[1], l0_color[2], own_alpha] @@ -5650,4 +5653,3 @@ mod delta_undo_tests { assert_eq!(ms_occurrences(&scene, copy_h), 1); } } - diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index ba8d5f5f4..9a75f0467 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -918,6 +918,18 @@ pub(crate) fn layer_render_style(document: &CadDocument, layer_name: &str) -> In } } +/// Whether a block child uses layer-0 inheritance semantics. +/// +/// XREF merge keeps dependent layers distinct by namespacing them as +/// `xref|layer`; its source layer `0` therefore becomes `xref|0` but must still +/// inherit through the containing INSERT exactly like an unprefixed layer 0. +pub(crate) fn is_effective_layer_zero(layer_name: &str) -> bool { + layer_name.eq_ignore_ascii_case("0") + || layer_name + .rsplit_once('|') + .is_some_and(|(_, dependent)| dependent.eq_ignore_ascii_case("0")) +} + /// Like `render_style_for` but resolves a block sub-entity's inherited /// properties: ByBlock inherits the INSERT's style, and (the layer-0 rule) a /// sub-entity on layer "0" with ByLayer properties inherits the INSERT's @@ -934,7 +946,7 @@ pub(crate) fn render_style_for_block_sub( ) -> ([f32; 4], f32, [f32; 8], f32, u8) { let (color, pat_len, pat, lw_px, aci) = render_style_for(document, e); let common = e.common(); - let on_l0 = common.layer == "0"; + let on_l0 = is_effective_layer_zero(&common.layer); let final_color = if common.color == AcadColor::ByBlock { insert_color