mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
3D and Materials: Inflate builds its edge index once
inflate built an edge index (Edges) of the shape's rings, and Membrane::solve, which inflate calls first, built a second index of the same rings. Membrane::solve now takes the index that inflate builds. In a release build, one Edges::new takes 0.08 ms of the 27 ms that inflate takes on a ring of 2,000 points, and 2.0 ms of 219 ms on a ring of 1,000 spikes whose 2,000 edges fill 693,816 bucket entries. The facets do not change: for nine shapes, three views and 96 combinations of the Inflate settings, the Debug text of all 2,592 results is byte for byte the same on main and with the change.
This commit is contained in:
@@ -886,8 +886,8 @@ struct Membrane {
|
||||
}
|
||||
|
||||
impl Membrane {
|
||||
fn solve(rings: &[Vec<Point>], 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<Point>], 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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user