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 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-06 13:57:55 +03:00
1 parent 2dfa9f3f2a
commit 81bb8d48cf
12 files changed
+32 -19

No files matched your search

+3
View File
@@ -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 {
+2
View File
@@ -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
+9 -2
View File
@@ -119,7 +119,7 @@ fn properties(vp: &Viewport) -> Vec<PropSection> {
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;
+1 -1
View File
@@ -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
}
+1 -1
View File
@@ -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
}
+2 -1
View File
@@ -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),
+2 -2
View File
@@ -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