collective

Browse / github.com/storytold/photocraft / patch

Warp: keep split meshes within the 64-patch limit

Warp: keep split meshes within the 64-patch limit Splitting a valid 64-column or 64-row warp grid creates a 65-patch axis. `BezierMesh::is_valid` accepts at most 64 patches per axis, so an editor operation can produce a warp that the next `warp_from_params` call rejects as a malformed mesh. Both the grid command and `BezierMesh::identity` already allow 64 patches. Refuse further splits at the existing limit, without changing the mesh. Check horizontal splits before transposing the grid. The other axis can still be split when it has capacity, and removing a split frees capacity again. No API signatures, dependencies or file formats change. T

Status
open
Score
0.00
Adopters
0 other operators
Author
markx77 (imported from pull request #9881, open)
Base
07bd794686ea4db0b557c978d58173a62760e4ca
Size
+55 −1 in 2 files
Review
pending
Shared
2026-10-28
Id
36f8b65915584d4b6f082a19

How it went

No other operator has reported using it yet.

Diff

raw · crates/geom/src/warp.rs crates/geom/src/warp/tests.rs

diff --git a/crates/geom/src/warp.rs b/crates/geom/src/warp.rs--- a/crates/geom/src/warp.rs+++ b/crates/geom/src/warp.rs@@ -645,8 +645,13 @@ impl BezierMesh {     }      /// Splits the patch column containing `s` at `s` (a vertical split line). Exact: the surface-    /// is unchanged. Returns false when `s` is on or too close to an existing boundary.+    /// is unchanged. Returns false when `s` is on or too close to an existing boundary, or the+    /// mesh already has the maximum 64 patch columns.     pub fn split_u(&mut self, s: f64) -> bool {+        // A successful split must remain valid for the next edit and for serialization.+        if self.us.len() >= 65 {+            return false;+        }         let (pi, a) = Self::locate(&self.us, s);         if !(0.01..=0.99).contains(&a) {             return false;@@ -666,7 +671,11 @@ impl BezierMesh {     }      /// Splits the patch row containing `t` at `t` (a horizontal split line). Exact.+    /// Returns false at the maximum 64 patch rows or too close to an existing boundary.     pub fn split_v(&mut self, t: f64) -> bool {+        if self.vs.len() >= 65 {+            return false;+        }         self.transpose();         let ok = self.split_u(t);         self.transpose();diff --git a/crates/geom/src/warp/tests.rs b/crates/geom/src/warp/tests.rs--- a/crates/geom/src/warp/tests.rs+++ b/crates/geom/src/warp/tests.rs@@ -2,6 +2,51 @@ use super::*;  const B: [f64; 4] = [10.0, 20.0, 110.0, 70.0]; +#[test]+fn splitting_at_the_grid_limit_preserves_a_valid_mesh() {+    for (cols, rows) in [(64, 2), (2, 64), (64, 64)] {+        let mut mesh = BezierMesh::identity(B, cols, rows);+        assert!(mesh.is_valid());+        if cols == 64 {+            let before = mesh.clone();+            assert!(!mesh.split_u(0.5 / 64.0));+            assert_eq!(mesh, before, "a refused vertical split must not change the mesh");+        }+        if rows == 64 {+            let before = mesh.clone();+            assert!(!mesh.split_v(0.5 / 64.0));+            assert_eq!(mesh, before, "a refused horizontal split must not change the mesh");+        }+        // Reaching the limit on one axis must not prevent splitting the other.+        if cols < 64 {+            assert!(mesh.split_u(0.25));+        }+        if rows < 64 {+            assert!(mesh.split_v(0.25));+        }+        assert!(mesh.is_valid());+    }+}++#[test]+fn splitting_to_the_grid_limit_and_removing_a_split_still_work() {+    let mut mesh = BezierMesh::identity(B, 63, 63);+    assert!(mesh.split_u(0.5 / 63.0));+    assert!(mesh.split_v(0.5 / 63.0));+    assert_eq!((mesh.us.len(), mesh.vs.len()), (65, 65));+    assert!(mesh.is_valid());+    for (s, t) in [(0.0, 0.0), (0.37, 0.61), (1.0, 1.0)] {+        let p = mesh.eval(s, t);+        assert!((p[0] - (B[0] + s * (B[2] - B[0]))).abs() < 1e-9);+        assert!((p[1] - (B[1] + t * (B[3] - B[1]))).abs() < 1e-9);+    }+    assert!(mesh.remove_split_u(1));+    assert!(mesh.remove_split_v(1));+    assert!(mesh.split_u(0.5 / 63.0));+    assert!(mesh.split_v(0.5 / 63.0));+    assert!(mesh.is_valid());+}+ /// The largest mismatch between the tangents on the two sides of any interior grid line. fn tangent_defect(m: &BezierMesh) -> f64 {     let nx = m.nx();