From fdc01e13c8b1020e9af9cefe8e63534622532dfb Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 25 Sep 2026 19:48:36 +0300 Subject: [PATCH] fix(render): show Inventor drawing views, each in its own viewport (#1285) Inventor DWG exports place every sheet block with AcIdBlockReference, which the codec skipped, so these drawings opened blank. opencadcodec 7c20aa7 decodes it as an INSERT and keeps the viewport each model-space view reference is bound to. Those view blocks overlap in model space, so a content viewport now also hides model-space references bound to a different viewport. The handles join the viewport's frozen-layer set (entity and layer handles never collide), which the wire and render-graph visibility checks and every per-viewport cache key already use. Pins opencadcodec 7c20aa7, opencadkernel d71a828, opencadgraph 1543a5a. Co-Authored-By: Claude Opus 5.5 --- Cargo.lock | 8 ++++---- Cargo.toml | 8 ++++---- crates/ocs_plugin_api/Cargo.toml | 4 ++-- crates/ocs_web_worker/Cargo.toml | 2 +- plugins/opencad-python/Cargo.lock | 2 +- src/scene/mod.rs | 30 ++++++++++++++++++++++++++++++ src/scene/paper.rs | 8 +------- src/scene/render_graph.rs | 3 ++- src/scene/view/render.rs | 5 +---- 9 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6295effe8..26659a805 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4201,7 +4201,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.5.5" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=d6d534f#d6d534fcfec5823e47ef5630668d8e16d62c1641" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=7c20aa7#7c20aa7fad405845c5bd5cdcc2a232cdc2a4687d" dependencies = [ "ahash 0.8.12", "anyhow", @@ -4224,7 +4224,7 @@ dependencies = [ [[package]] name = "opencadgraph" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=5c9b004#5c9b004a2fd10b9eb62b8d54ce443120ee417a95" +source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=1543a5a#1543a5aeb0b871a2f23a3b55117c7f5af1e1d229" dependencies = [ "opencadkernel", "serde_json", @@ -4233,7 +4233,7 @@ dependencies = [ [[package]] name = "opencadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=5d9e921#5d9e92104830444ca48ac7c5a8cdcfa24c21786b" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=d71a828#d71a8284f0388025e07f219c7b8f6bb86e7188ec" dependencies = [ "cavalier_contours", "opencadcodec", @@ -4244,7 +4244,7 @@ dependencies = [ [[package]] name = "opencadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=5d9e921#5d9e92104830444ca48ac7c5a8cdcfa24c21786b" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=d71a828#d71a8284f0388025e07f219c7b8f6bb86e7188ec" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index 9d4b1f7a7..7ad93dc7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,10 +33,10 @@ rfd = "0.17" clap = { version = "4", features = ["derive", "string"] } env_logger = "0.11" log = "0.4" -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d6d534f", features = ["serde"] } -kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "5d9e921", features = ["acis", "offset"] } -kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "5d9e921" } -graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "5c9b004" } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", features = ["serde"] } +kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "d71a828", features = ["acis", "offset"] } +kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "d71a828" } +graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "1543a5a" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } diff --git a/crates/ocs_plugin_api/Cargo.toml b/crates/ocs_plugin_api/Cargo.toml index 022c95c09..91efdc937 100644 --- a/crates/ocs_plugin_api/Cargo.toml +++ b/crates/ocs_plugin_api/Cargo.toml @@ -15,7 +15,7 @@ serde_json = { version = "1", optional = true } # Pulled in only by the `host` feature, which adds the `opencadcodec`-typed # `HostApi` runtime surface. The default crate stays dependency-free so engine # crates and external tooling can depend on the manifest/ribbon contract cheaply. -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d6d534f", optional = true, features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", optional = true, features = ["serde"] } # Runtime IPC and serialization (host feature only). interprocess = { version = "2", optional = true } @@ -39,7 +39,7 @@ serde_json = "1" serde = { version = "1", features = ["derive"] } cargo-lock = "11" # opencadcodec is scanned at build time to generate the embedded type registry. -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d6d534f", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index fd592806b..229386607 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] -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "d6d534f", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } console_error_panic_hook = "0.1" diff --git a/plugins/opencad-python/Cargo.lock b/plugins/opencad-python/Cargo.lock index dcca42de7..039e59eaf 100644 --- a/plugins/opencad-python/Cargo.lock +++ b/plugins/opencad-python/Cargo.lock @@ -1265,7 +1265,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.5.5" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=d6d534f#d6d534fcfec5823e47ef5630668d8e16d62c1641" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=7c20aa7#7c20aa7fad405845c5bd5cdcc2a232cdc2a4687d" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 3e3264fe5..6c1f1a012 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -8108,6 +8108,33 @@ impl Scene { .unwrap_or(false) } + /// Handles hidden inside content viewport `vp`: its VP-frozen layers plus + /// model-space references bound to another viewport (Inventor drawing + /// views overlap in model space; each is drawn only in its own viewport). + /// Entity and layer handles never collide, so both ride the same set and + /// its cache signature. + pub(super) fn viewport_hidden_handles(&self, vp: Handle) -> HashSet { + let mut hidden: HashSet = match self.document.get_entity(vp) { + Some(EntityType::Viewport(v)) => v.frozen_layers.iter().copied().collect(), + _ => HashSet::default(), + }; + // ponytail: full entity scan per call, gated to drawings declaring the + // class; memoize per geometry epoch if large Inventor sheets show up. + if self.document.classes.get_by_name("ACIDBLOCKREFERENCE").is_some() { + let model = self.model_space_block_handle(); + hidden.extend(self.document.entities().filter_map(|e| match e { + EntityType::Insert(i) + if i.common.owner_handle == model + && i.view_rep_handle.is_some_and(|bound| bound != vp) => + { + Some(i.common.handle) + } + _ => None, + })); + } + hidden + } + /// True when `layer`'s handle is in a content viewport's per-viewport /// frozen-layer set (VP freeze). Mirrors the wire path's test in /// [`Scene::resident_entity_visible`] so fills / images / meshes hide on the @@ -10199,6 +10226,9 @@ impl Scene { } if let Some(frozen) = frozen_layers { if !frozen.is_empty() { + if frozen.contains(&c.handle) { + return false; + } if let Some(lh) = layer.map(|l| l.handle) { if frozen.contains(&lh) { return false; diff --git a/src/scene/paper.rs b/src/scene/paper.rs index 58ccd48af..1ad157080 100644 --- a/src/scene/paper.rs +++ b/src/scene/paper.rs @@ -828,13 +828,7 @@ impl Scene { // wheel tick whenever the drawing contained one annotative object. // Explicit viewport annotation-scale changes still rebuild the resident // set; PSLTSCALE is a viewport GPU uniform. - let frozen = match self.document.get_entity(vp_handle) { - Some(EntityType::Viewport(vp)) => { - let f: HSet = vp.frozen_layers.iter().cloned().collect(); - f - } - _ => HSet::default(), - }; + let frozen: HSet = self.viewport_hidden_handles(vp_handle); let scale_handle = self.viewport_scale_handle(vp_handle); self.resident_wires_for( diff --git a/src/scene/render_graph.rs b/src/scene/render_graph.rs index 5e98f2ee0..dcb8a0199 100644 --- a/src/scene/render_graph.rs +++ b/src/scene/render_graph.rs @@ -612,7 +612,8 @@ impl<'a> RenderSceneGraph<'a> { return false; } if self.frozen_layers.is_some_and(|frozen| { - layer.is_some_and(|layer| frozen.contains(&layer.handle)) + frozen.contains(&common.handle) + || layer.is_some_and(|layer| frozen.contains(&layer.handle)) }) { return false; } diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index e6256f8ab..143c0b1f7 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -4537,10 +4537,7 @@ impl Scene { && inst.tile_idx.is_none() && inst.handle != codec::Handle::NULL { - match self.document.get_entity(inst.handle) { - Some(EntityType::Viewport(vp)) => vp.frozen_layers.iter().cloned().collect(), - _ => rustc_hash::FxHashSet::default(), - } + self.viewport_hidden_handles(inst.handle) } else { rustc_hash::FxHashSet::default() };