From 81bb8d48cf1c056f6f4d523d2b94d2ab7f51967a Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Tue, 6 Oct 2026 13:57:55 +0300 Subject: [PATCH] Cargo: codec aad7053, kernel 1a52eaf, graph 263de59; honour viewport clipping Brings codec PRs #84 (cross-version visual styles, table styles, subentity managers), #88 (viewport clipping flag, DXF boundary handle), #89 (DXF viewport snap/twist in degrees) and #92 (field format codes, event updates). With #88 a viewport keeps a stored boundary whether or not clipping is on, so a linked boundary alone no longer means "clipped": rendering, paper picking, VPCLIP and the Clipped property check the clipping flag too, and VPCLIP / MVIEW Object and Polygon set it. Before, OCS never set the flag, so its clipped viewports opened rectangular elsewhere. 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/app/command_driver/viewport.rs | 3 +++ src/app/viewport_dimension_tests.rs | 2 ++ src/entities/viewport.rs | 11 +++++++++-- src/modules/insert/xclip.rs | 2 +- src/modules/layout/mview.rs | 2 +- src/scene/mspace.rs | 3 ++- src/scene/project.rs | 4 ++-- 12 files changed, 32 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d079feccb..66a68575d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4205,7 +4205,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.6.0" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=1ca7d33#1ca7d3360a34384b59297ffe8aa18b157d2701d9" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=aad7053#aad70533211bd9192545729f7eb3bccb827bcf30" dependencies = [ "anyhow", "bitflags 2.13.1", @@ -4228,7 +4228,7 @@ dependencies = [ [[package]] name = "opencadgraph" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=d7908a8#d7908a8d24a42896b03f7f83b3a1c0187692043a" +source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=263de59#263de5924e3fffcbb78377db5ed1117f50620666" dependencies = [ "opencadkernel", "serde_json", @@ -4237,7 +4237,7 @@ dependencies = [ [[package]] name = "opencadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=7fb0abb#7fb0abb4ee8e4266a770eb673ac7757c71321513" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=1a52eaf#1a52eaf237949a4492a7b56907922218ab2d1ea9" dependencies = [ "cavalier_contours", "opencadcodec", @@ -4248,7 +4248,7 @@ dependencies = [ [[package]] name = "opencadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=7fb0abb#7fb0abb4ee8e4266a770eb673ac7757c71321513" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=1a52eaf#1a52eaf237949a4492a7b56907922218ab2d1ea9" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index a1638dc46..8f209a6f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,10 +34,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 = "1ca7d33", features = ["serde"] } -kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "7fb0abb", features = ["acis", "offset"] } -kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "7fb0abb" } -graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "d7908a8" } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", features = ["serde"] } +kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "1a52eaf", features = ["acis", "offset"] } +kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "1a52eaf" } +graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "263de59" } 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 852c56700..9ffb949a5 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 = "1ca7d33", optional = true, features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", 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 = "1ca7d33", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index a6be242eb..c57e180c2 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 = "1ca7d33", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", 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 17eec73b6..75aab19a9 100644 --- a/plugins/opencad-python/Cargo.lock +++ b/plugins/opencad-python/Cargo.lock @@ -1264,7 +1264,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.6.0" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=1ca7d33#1ca7d3360a34384b59297ffe8aa18b157d2701d9" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=aad7053#aad70533211bd9192545729f7eb3bccb827bcf30" dependencies = [ "anyhow", "bitflags", diff --git a/src/app/command_driver/viewport.rs b/src/app/command_driver/viewport.rs index f8eadd485..8218d1a37 100644 --- a/src/app/command_driver/viewport.rs +++ b/src/app/command_driver/viewport.rs @@ -90,6 +90,7 @@ impl OpenCADStudio { let mut vp = vp; if clip.is_null() { vp.clip_boundary_handle = Handle::NULL; + vp.status.non_rectangular_clipping = false; } else { let (lo, hi) = clip_boundary_extents(scene, clip) .map(|(lo, hi)| (glam::DVec2::from(lo), glam::DVec2::from(hi))) @@ -117,6 +118,7 @@ impl OpenCADStudio { vp.height = hi.y - lo.y; vp.view_height = vp.height * scale; vp.clip_boundary_handle = clip; + vp.status.non_rectangular_clipping = true; if let Some(entity) = scene.document.get_entity_mut(clip) { let common = entity.common_mut(); if !common.reactors.contains(&viewport) { @@ -228,6 +230,7 @@ impl OpenCADStudio { viewport.height = max_y - min_y; viewport.id = 2; viewport.clip_boundary_handle = clip_handle; + viewport.status.non_rectangular_clipping = !clip_handle.is_null(); let viewport_handle = self.commit_entity_handle(codec::EntityType::Viewport(viewport)); if let Some(viewport_handle) = viewport_handle { if !created_boundary { diff --git a/src/app/viewport_dimension_tests.rs b/src/app/viewport_dimension_tests.rs index da0825e8f..04e09759f 100644 --- a/src/app/viewport_dimension_tests.rs +++ b/src/app/viewport_dimension_tests.rs @@ -147,6 +147,7 @@ fn viewport_dimension_curved_clip_includes_the_exact_boundary() { panic!() }; vp.clip_boundary_handle = clip; + vp.status.non_rectangular_clipping = true; assert!(scene .viewport_displays_paper_point(frame.viewport, glam::DVec2::new(boundary.x, boundary.y))); assert!(!scene.viewport_displays_paper_point(frame.viewport, glam::DVec2::new(60.01, 50.0))); @@ -463,6 +464,7 @@ fn viewport_dimension_eligibility_is_shared_and_uses_clipping() { let clip = scene.add_entity(EntityType::LwPolyline(clip)); if let Some(EntityType::Viewport(vp)) = scene.document.get_entity_mut(top) { vp.clip_boundary_handle = clip; + vp.status.non_rectangular_clipping = true; } assert_eq!( scene diff --git a/src/entities/viewport.rs b/src/entities/viewport.rs index 68b45ad8b..3e65cca64 100644 --- a/src/entities/viewport.rs +++ b/src/entities/viewport.rs @@ -119,7 +119,7 @@ fn properties(vp: &Viewport) -> Vec { ro( "Clipped", "vp_clipped", - if vp.clip_boundary_handle.is_null() { "No" } else { "Yes" }, + if is_clipped(vp) { "Yes" } else { "No" }, ), Property { label: "Display locked".into(), @@ -342,6 +342,13 @@ fn apply_grip(vp: &mut Viewport, grip_id: usize, apply: GripApply) { /// Stretch a rectangular paper-space viewport by the corners captured by a /// crossing window. Bounds that have no captured corner stay anchored. +/// A viewport clipped by its boundary object: the boundary is linked and +/// non-rectangular clipping is on. A stored boundary with clipping off draws +/// the rectangular viewport. +pub(crate) fn is_clipped(vp: &Viewport) -> bool { + !vp.clip_boundary_handle.is_null() && vp.status.non_rectangular_clipping +} + pub fn stretch( vp: &mut Viewport, win_min: glam::DVec3, @@ -435,7 +442,7 @@ impl crate::entities::traits::FallbackTess for Viewport { // A clipped viewport uses its linked boundary entity as its visible // frame. Drawing the viewport's rectangular extents as well leaves an // incorrect box around polygonal/Object MVIEW results. - if !self.clip_boundary_handle.is_null() { + if is_clipped(self) { return (vec![], vec![], vec![], vec![]); } let cx = self.center.x; diff --git a/src/modules/insert/xclip.rs b/src/modules/insert/xclip.rs index 6fdbb8cd3..57e3837ec 100644 --- a/src/modules/insert/xclip.rs +++ b/src/modules/insert/xclip.rs @@ -497,7 +497,7 @@ impl CadCommand for ClipCommand { } // The layout's own paper-space viewport (id 1) is not clippable. Some(EntityType::Viewport(vp)) if vp.id != 1 => { - let clipped = !vp.clip_boundary_handle.is_null(); + let clipped = crate::entities::viewport::is_clipped(&vp); self.inner = Some(Box::new(MviewCommand::vpclip(handle, clipped))); CmdResult::NeedPoint } diff --git a/src/modules/layout/mview.rs b/src/modules/layout/mview.rs index e9c6aa396..01304ff30 100644 --- a/src/modules/layout/mview.rs +++ b/src/modules/layout/mview.rs @@ -799,7 +799,7 @@ impl CadCommand for MviewCommand { if self.step == Step::ClipSelect { return match self.picked.take() { Some(EntityType::Viewport(vp)) if vp.id != 1 => { - self.clip = Some((handle, !vp.clip_boundary_handle.is_null())); + self.clip = Some((handle, crate::entities::viewport::is_clipped(&vp))); self.step = Step::ClipChoice; CmdResult::NeedPoint } diff --git a/src/scene/mspace.rs b/src/scene/mspace.rs index d3ad2a908..2d4a0b305 100644 --- a/src/scene/mspace.rs +++ b/src/scene/mspace.rs @@ -189,7 +189,7 @@ impl Scene { if (paper.x - vp.center.x).abs() > hw || (paper.y - vp.center.y).abs() > hh { return false; } - if vp.clip_boundary_handle.is_null() { + if !crate::entities::viewport::is_clipped(vp) { return true; } let Some(boundary) = self.document.get_entity(vp.clip_boundary_handle) @@ -321,6 +321,7 @@ mod clip_tests { viewport.width = 20.0; viewport.height = 20.0; viewport.clip_boundary_handle = clip; + viewport.status.non_rectangular_clipping = true; let viewport = scene.add_entity(EntityType::Viewport(viewport)); for (x, y, inside) in [ (2.0, 2.0, true), (8.0, 2.0, true), (8.0, 8.0, false), diff --git a/src/scene/project.rs b/src/scene/project.rs index fec1f4684..765c6169a 100644 --- a/src/scene/project.rs +++ b/src/scene/project.rs @@ -507,7 +507,7 @@ impl Scene { [xmin as f64, ymin as f64], [xmax as f64, ymin as f64], [xmax as f64, ymax as f64], [xmin as f64, ymax as f64], ]]; - if !self.images.is_empty() && !viewport.clip_boundary_handle.is_null() { + if !self.images.is_empty() && crate::entities::viewport::is_clipped(viewport) { let boundary = self.clip_boundary_polygon( viewport.clip_boundary_handle, viewport.center.z as f32); if boundary.len() >= 3 { @@ -663,7 +663,7 @@ impl Scene { if hw.abs() < 1e-6 || hh.abs() < 1e-6 { return vec![]; } - let poly = if vp.clip_boundary_handle.is_null() { + let poly = if !crate::entities::viewport::is_clipped(vp) { // Rectangular viewport → its own four corners (paper coords). These // map to full-rect NDC [-1, 1], a no-op mask over a render target // that already clips to the rectangle, but it keeps rect and