diff --git a/Cargo.lock b/Cargo.lock index f85069b6b..7b66c0e6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.4.1" -source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=3a87743ad7b0fc74f0137ccd48f598116e2904b0#3a87743ad7b0fc74f0137ccd48f598116e2904b0" +source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=c91a1c1de89a0fbc006dc360ddb0251ac8c207d1#c91a1c1de89a0fbc006dc360ddb0251ac8c207d1" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 74f7792a8..79f2cf17a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ glam = { version = "0.33", features = ["bytemuck"] } rfd = "0.17" clap = { version = "4", features = ["derive"] } env_logger = "0.11" -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", features = ["serde"] } cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "48c634995fa60d3928c37bae49b12b421c56c886", features = ["acis", "offset"] } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" @@ -61,7 +61,7 @@ iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aa iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } [patch."https://github.com/HakanSeven12/cadcodec.git"] -acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0" } +acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/crates/ocs_plugin_api/Cargo.toml b/crates/ocs_plugin_api/Cargo.toml index bd69e4060..49e520059 100644 --- a/crates/ocs_plugin_api/Cargo.toml +++ b/crates/ocs_plugin_api/Cargo.toml @@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] } # Pulled in only by the `host` feature, which adds the `acadrust`-typed # `HostApi` runtime surface. The default crate stays dependency-free so engine # crates and external tooling can depend on the manifest/ribbon contract cheaply. -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", optional = true, features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", optional = true, features = ["serde"] } # Runtime IPC and serialization (host feature only). interprocess = { version = "2", optional = true } @@ -37,7 +37,7 @@ serde_json = "1" serde = { version = "1", features = ["derive"] } cargo-lock = "11" # acadrust is scanned at build time to generate the embedded type registry. -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_plugin_api/build.rs b/crates/ocs_plugin_api/build.rs index d818a0dd6..87b9bafb9 100644 --- a/crates/ocs_plugin_api/build.rs +++ b/crates/ocs_plugin_api/build.rs @@ -123,6 +123,10 @@ fn add_enum_samples(tracer: &mut Tracer, samples: &mut Samples) { let _ = tracer.trace_value(samples, &acadrust::LineWeight::Default); let _ = tracer.trace_value(samples, &acadrust::LineWeight::Value(0)); + let _ = tracer.trace_value(samples, &acadrust::Transparency::BY_LAYER); + let _ = tracer.trace_value(samples, &acadrust::Transparency::BY_BLOCK); + let _ = tracer.trace_value(samples, &acadrust::Transparency::OPAQUE); + let _ = tracer.trace_value(samples, &acadrust::entities::SmoothSurfaceType::None); let _ = tracer.trace_value( samples, diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index bef08417a..a700c08c3 100644 --- a/crates/ocs_web_worker/Cargo.toml +++ b/crates/ocs_web_worker/Cargo.toml @@ -8,7 +8,7 @@ publish = false crate-type = ["cdylib"] [dependencies] -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "c91a1c1de89a0fbc006dc360ddb0251ac8c207d1", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } console_error_panic_hook = "0.1" diff --git a/src/entities/raster_image.rs b/src/entities/raster_image.rs index 51937cf58..c91aaf793 100644 --- a/src/entities/raster_image.rs +++ b/src/entities/raster_image.rs @@ -290,7 +290,7 @@ impl PropertyEditable for RasterImage { .flags .contains(acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY); let clip_inverted = self.clip_boundary.clip_mode == acadrust::entities::ClipMode::Inside; - let transparency = format!("{:.0}%", self.common.transparency.as_percent() * 100.0); + let transparency = self.common.transparency.to_string(); vec![ PropSection { title: t!("Geometry").into_owned(), diff --git a/src/scene/cache/block_cache.rs b/src/scene/cache/block_cache.rs index f38ba11c5..aaf12f4da 100644 --- a/src/scene/cache/block_cache.rs +++ b/src/scene/cache/block_cache.rs @@ -88,6 +88,7 @@ pub struct LocalWire { /// `points.is_empty() && !fill_tris.is_empty()`. pub is_fill_only: bool, pub color_is_byblock: bool, + pub transparency_is_byblock: bool, pub lt_is_byblock: bool, pub lw_is_byblock: bool, /// Set when this child sits on layer "0" and the matching property is @@ -710,7 +711,8 @@ fn tessellate_sub_local( .unwrap_or(true); let color_l0 = !has_book_color && on_l0 && sub.common().color == AcadColor::ByLayer; - let transparency_l0 = on_l0 && sub.common().transparency.alpha() == 0; + let transparency_is_byblock = sub.common().transparency.is_by_block(); + let transparency_l0 = on_l0 && sub.common().transparency.is_by_layer(); let lt_l0 = on_l0 && { let lt = &sub.common().linetype; lt.is_empty() || lt.eq_ignore_ascii_case("bylayer") @@ -846,10 +848,11 @@ fn tessellate_sub_local( hide_unselected: frame_mode == Some(0), is_fill_only, color_is_byblock: color_is_byblock && wire_on_base_color, + transparency_is_byblock, lt_is_byblock, lw_is_byblock, color_l0: color_l0 && wire_on_base_color, - transparency_l0: transparency_l0 && wire_on_base_color, + transparency_l0, lt_l0, lw_l0, aabb_local, @@ -1990,24 +1993,28 @@ fn expand_defn( /// layer-0 rule → insert-layer colour, else the cached colour; finally xref /// fade. Shared by the stroke, fill, and greeked-text emit paths. fn resolve_wire_color(lw: &LocalWire, ctx: &ExpandCtx) -> [f32; 4] { - let c = if ctx.selected { + let mut color = if ctx.selected { WireModel::SELECTED } else if lw.color_is_byblock { ctx.ins_color } else if lw.color_l0 { - let alpha = if lw.transparency_l0 { + ctx.l0.color + } else { + lw.color + }; + if !ctx.selected { + color[3] = if lw.transparency_is_byblock { + ctx.ins_color[3] + } else if lw.transparency_l0 { ctx.l0.color[3] } else { lw.color[3] }; - [ctx.l0.color[0], ctx.l0.color[1], ctx.l0.color[2], alpha] - } else { - lw.color - }; + } if ctx.is_xref && !ctx.selected { - fade_toward_bg(c, ctx.bg_color) + fade_toward_bg(color, ctx.bg_color) } else { - c + color } } @@ -2592,7 +2599,7 @@ fn emit_wire( pos: [hx, hy, hz], pos_low: [lx, ly, lz], uv: tv.uv, - color: [rgb[0], rgb[1], rgb[2], tv.color[3]], + color: [rgb[0], rgb[1], rgb[2], final_color[3]], draw_depth: tv.draw_depth, }); } diff --git a/src/scene/cache/properties.rs b/src/scene/cache/properties.rs index 8b3d04711..1e463ccd8 100644 --- a/src/scene/cache/properties.rs +++ b/src/scene/cache/properties.rs @@ -1,4 +1,4 @@ -use acadrust::{EntityType, Handle}; +use acadrust::{EntityType, Handle, Transparency}; use crate::t; use crate::scene::model::object::{PropSection, PropValue, Property}; @@ -10,15 +10,12 @@ pub fn general_section(entity: &EntityType) -> PropSection { } else { common.linetype.clone() }; - // Alpha 0 is ByLayer, Alpha 1 is ByBlock; show them by name and fall back - // to a rounded percentage for explicit values. - let transp_display = match common.transparency.alpha() { - 0 => "ByLayer".to_string(), - 1 => "ByBlock".to_string(), - alpha => format!( - "{}", - (alpha as f64 / 255.0 * 100.0).round() as u32 - ), + let transp_display = match common.transparency { + Transparency::ByLayer => "ByLayer".to_string(), + Transparency::ByBlock => "ByBlock".to_string(), + Transparency::Explicit(alpha) => { + ((alpha as f64 / 255.0 * 100.0).round() as u32).to_string() + } }; // Hyperlink is stored in XDATA under the "PE_URL" application. diff --git a/src/scene/render_graph.rs b/src/scene/render_graph.rs index dd737aaef..69400ad4f 100644 --- a/src/scene/render_graph.rs +++ b/src/scene/render_graph.rs @@ -67,6 +67,8 @@ pub struct InsertStyleSpec { own: BlockStyle, color_byblock: bool, color_bylayer: bool, + transparency_byblock: bool, + transparency_bylayer: bool, linetype_byblock: bool, linetype_bylayer: bool, lineweight_byblock: bool, @@ -83,6 +85,8 @@ impl InsertStyleSpec { own: BlockStyle::for_entity(document, &entity, viewport), color_byblock: !has_book_color && insert.common.color == Color::ByBlock, color_bylayer: !has_book_color && insert.common.color == Color::ByLayer, + transparency_byblock: insert.common.transparency.is_by_block(), + transparency_bylayer: insert.common.transparency.is_by_layer(), linetype_byblock: linetype.eq_ignore_ascii_case("byblock"), linetype_bylayer: linetype.is_empty() || linetype.eq_ignore_ascii_case("bylayer"), lineweight_byblock: matches!( @@ -101,12 +105,23 @@ impl InsertStyleSpec { pub fn resolve(self, parent: BlockStyle) -> BlockStyle { let mut insert = self.own.insert; if self.color_byblock { - insert.0 = parent.insert.0; + insert.0[0] = parent.insert.0[0]; + insert.0[1] = parent.insert.0[1]; + insert.0[2] = parent.insert.0[2]; insert.4 = parent.insert.4; } else if self.layer0 && self.color_bylayer { - insert.0 = parent.layer0.color; + insert.0[0] = parent.layer0.color[0]; + insert.0[1] = parent.layer0.color[1]; + insert.0[2] = parent.layer0.color[2]; insert.4 = parent.layer0_aci; } + insert.0[3] = if self.transparency_byblock { + parent.insert.0[3] + } else if self.layer0 && self.transparency_bylayer { + parent.layer0.color[3] + } else { + insert.0[3] + }; if self.linetype_byblock { insert.1 = parent.insert.1; insert.2 = parent.insert.2; diff --git a/src/scene/selection.rs b/src/scene/selection.rs index 925b7d93d..0c67902e7 100644 --- a/src/scene/selection.rs +++ b/src/scene/selection.rs @@ -675,11 +675,12 @@ impl Scene { .to_string(), ), "lineweight" => Some(Self::format_lineweight(entity.common().line_weight)), - "transparency" => Some(if entity.common().transparency.alpha() == 0 { - "ByLayer".to_string() - } else { - ((entity.common().transparency.alpha() as f64 / 255.0 * 100.0).round() as u32) - .to_string() + "transparency" => Some(match entity.common().transparency { + acadrust::types::Transparency::ByLayer => "ByLayer".to_string(), + acadrust::types::Transparency::ByBlock => "ByBlock".to_string(), + acadrust::types::Transparency::Explicit(alpha) => { + ((alpha as f64 / 255.0 * 100.0).round() as u32).to_string() + } }), "hyperlink" => Some( entity diff --git a/src/scene/view/dispatch.rs b/src/scene/view/dispatch.rs index 34c441329..4c47da101 100644 --- a/src/scene/view/dispatch.rs +++ b/src/scene/view/dispatch.rs @@ -137,9 +137,13 @@ pub fn apply_common_prop(entity: &mut EntityType, field: &str, value: &str) { "transparency" => { let s = value.trim(); if s.eq_ignore_ascii_case("ByLayer") { - entity.as_entity_mut().set_transparency(Transparency::new(0)); + entity + .as_entity_mut() + .set_transparency(Transparency::BY_LAYER); } else if s.eq_ignore_ascii_case("ByBlock") { - entity.as_entity_mut().set_transparency(Transparency::new(1)); + entity + .as_entity_mut() + .set_transparency(Transparency::BY_BLOCK); } else { let clean = s.trim_end_matches('%').trim(); if let Ok(pct) = clean.parse::() { diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index c2d8d2f2b..de64fc1df 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -2863,7 +2863,7 @@ pub(crate) fn render_style_for_viewport( _ => 0, }; let [r, g, b, _] = tess_util::aci_to_rgba(resolved); - let transparency = if common.transparency.alpha() == 0 { + let transparency = if common.transparency.is_by_layer() { viewport_override( document, layer_name, @@ -3077,18 +3077,21 @@ pub(crate) fn render_style_for_block_sub_viewport( let on_l0 = is_effective_layer_zero(&common.layer); let has_book_color = has_resolved_book_color(document, e); - let final_color = if !has_book_color && common.color == AcadColor::ByBlock { + let resolved_rgb = if !has_book_color && common.color == AcadColor::ByBlock { insert_color } else if !has_book_color && on_l0 && common.color == AcadColor::ByLayer { - let alpha = if common.transparency.alpha() == 0 { - l0.color[3] - } else { - color[3] - }; - [l0.color[0], l0.color[1], l0.color[2], alpha] + l0.color } else { color }; + let alpha = if common.transparency.is_by_block() { + insert_color[3] + } else if on_l0 && common.transparency.is_by_layer() { + l0.color[3] + } else { + color[3] + }; + let final_color = [resolved_rgb[0], resolved_rgb[1], resolved_rgb[2], alpha]; let lt_bylayer = common.linetype.is_empty() || common.linetype.eq_ignore_ascii_case("bylayer"); @@ -4319,4 +4322,22 @@ mod layer0_inherit_tests { assert_eq!(&c[..3], &walls[..3], "RGB inherited from the insert layer"); assert!((c[3] - 0.5).abs() < 0.02, "child's own 50% transparency is kept, got {}", c[3]); } + + #[test] + fn byblock_transparency_inherits_insert_alpha() { + let d = doc(); + let mut entity = child("Other", Color::Index(3)); + entity.common_mut().transparency = Transparency::BY_BLOCK; + let color = resolve(&d, &entity, [0.2, 0.4, 0.6, 0.25]); + assert_eq!(color[3], 0.25); + } + + #[test] + fn explicit_opaque_does_not_inherit_alpha() { + let d = doc(); + let mut entity = child("0", Color::ByLayer); + entity.common_mut().transparency = Transparency::OPAQUE; + let color = resolve(&d, &entity, [0.2, 0.4, 0.6, 0.25]); + assert_eq!(color[3], 1.0); + } }