diff --git a/crates/three-d/src/solid.rs b/crates/three-d/src/solid.rs index bb16efa21..f6f3e3661 100644 --- a/crates/three-d/src/solid.rs +++ b/crates/three-d/src/solid.rs @@ -886,8 +886,8 @@ struct Membrane { } impl Membrane { - fn solve(rings: &[Vec], origin: Point, step: f64, nx: usize, ny: usize) -> Self { - let edges = Edges::new(rings); + /// `edges` is the index of `rings`' edges (`Edges::new(rings)`). + fn solve(rings: &[Vec], edges: &Edges, origin: Point, step: f64, nx: usize, ny: usize) -> Self { let node = |i: usize, j: usize| Point::new(origin.x + i as f64 * step, origin.y + j as f64 * step); let idx = |i: usize, j: usize| j * nx + i; // A node on the outline itself is held at zero with the outside. @@ -1035,8 +1035,8 @@ pub fn inflate(path: &PathData, rule: FillRule, bounds: Rect, options: Inflate, const CELLS: usize = 48; let cell = size / CELLS as f64; let (nx, ny) = (((bounds.width() / cell).ceil() as usize).max(1), ((bounds.height() / cell).ceil() as usize).max(1)); - let m = Membrane::solve(&rings, bounds.origin(), cell, nx + 1, ny + 1); let edges = Edges::new(&rings); + let m = Membrane::solve(&rings, &edges, bounds.origin(), cell, nx + 1, ny + 1); let outline = PathData::new(rings.iter().map(|r| vectorcraft_geom::SubPath::polyline(r, true)).collect()); // A circle of radius r puffs into a half sphere of radius r at 50 % (its membrane peaks at r²/4). let height = 2.0 * m.peak.sqrt() * o.volume / 50.0; diff --git a/crates/three-d/src/tests_solid.rs b/crates/three-d/src/tests_solid.rs index 72f007cef..14c5ebca6 100644 --- a/crates/three-d/src/tests_solid.rs +++ b/crates/three-d/src/tests_solid.rs @@ -284,10 +284,12 @@ fn the_inflate_membrane_matches_known_solutions() { let mut c = BezPath::new(); c.extend(vectorcraft_geom::kurbo::Shape::path_elements(&vectorcraft_geom::kurbo::Circle::new((100.0, 100.0), 50.0), 0.01)); let (p, b) = shape(&c); - let disc = Membrane::solve(&rings(&p, FillRule::EvenOdd, 100.0, MAX_SOLID_POINTS).unwrap(), b.origin(), b.width() / 48.0, 49, 49); + let r = rings(&p, FillRule::EvenOdd, 100.0, MAX_SOLID_POINTS).unwrap(); + let disc = Membrane::solve(&r, &Edges::new(&r), b.origin(), b.width() / 48.0, 49, 49); assert!(close(disc.peak, 50.0 * 50.0 / 4.0, 625.0 * 0.02), "{}", disc.peak); let (p, b) = shape(&square(0.0, 0.0, 100.0)); - let sq = Membrane::solve(&rings(&p, FillRule::EvenOdd, 100.0, MAX_SOLID_POINTS).unwrap(), b.origin(), 100.0 / 48.0, 49, 49); + let r = rings(&p, FillRule::EvenOdd, 100.0, MAX_SOLID_POINTS).unwrap(); + let sq = Membrane::solve(&r, &Edges::new(&r), b.origin(), 100.0 / 48.0, 49, 49); assert!(close(sq.peak, 0.07367 * 100.0 * 100.0, 736.7 * 0.02), "{}", sq.peak); // Smooth across the middle: no crease where a distance-to-the-edge height would have one // (the diagonals). The second difference along a diagonal stays small and even.