Bezier-rs: Use nonzero winding order for Poisson-disk insideness test (#1590)

* Proper winding order for poisson dist

* More robust cubic solving

* Fix test expecting roots in a different order

* Manual sort impl

* Code review nits

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2024-02-04 06:05:26 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 349ec5da72
commit 5f72a6a8a1
3 changed files with 84 additions and 86 deletions
+33 -10
View File
@@ -134,15 +134,20 @@ impl Bezier {
match self.handles {
BezierHandles::Linear => [[None; 3]; 2],
BezierHandles::Quadratic { handle } => {
let a = handle - self.start;
let b = self.end - handle;
let b_minus_a = b - a;
[utils::solve_linear(b_minus_a.x, a.x), utils::solve_linear(b_minus_a.y, a.y)]
let d0 = handle - self.start;
let d1 = self.end - handle;
let dd = d1 - d0;
let a = (dd.x != 0.).then(|| -d0.x / dd.x);
let b = (dd.y != 0.).then(|| -d0.y / dd.y);
[[a, None, None], [b, None, None]]
}
BezierHandles::Cubic { handle_start, handle_end } => {
let a = 3. * (-self.start + 3. * handle_start - 3. * handle_end + self.end);
let b = 6. * (self.start - 2. * handle_start + handle_end);
let c = 3. * (handle_start - self.start);
let d0 = handle_start - self.start;
let d1 = handle_end - handle_start;
let d2 = self.end - handle_end;
let a = d0 - 2. * d1 + d2;
let b = 2. * (d1 - d0);
let c = d0;
let discriminant = b * b - 4. * a * c;
let two_times_a = 2. * a;
[
@@ -592,9 +597,27 @@ impl Bezier {
///
/// Cast a ray to the left and count intersections.
pub fn winding(&self, target_point: DVec2) -> i32 {
let extrema = self.get_extrema_t_list();
extrema
let [x_extrema_t, y_extrema_t] = self.unrestricted_local_extrema();
let mut x_extrema_t = x_extrema_t.map(|t| t.filter(|&t| t > 0. && t < 1.));
let mut y_extrema_t = y_extrema_t.map(|t| t.filter(|&t| t > 0. && t < 1.));
let mut results = [None; 8];
results[7] = Some(1.);
for i in (0..7).rev() {
let Some(min) = x_extrema_t.iter_mut().chain(y_extrema_t.iter_mut()).max_by(|a, b| a.partial_cmp(b).unwrap()) else {
results[i] = Some(0.);
break;
};
if let Some(value) = min.take() {
results[i] = Some(value);
} else {
results[i] = Some(0.);
break;
}
}
results
.windows(2)
.flat_map(|t| t[0].and_then(|first| t[1].map(|second| [first, second])))
.map(|t| self.trim(TValue::Parametric(t[0]), TValue::Parametric(t[1])).pre_split_winding_number(target_point))
.sum()
}
@@ -737,7 +760,7 @@ mod tests {
let p = DVec2::new(127., 121.);
validate(bz, p);
let bz = Bezier::from_cubic_coordinates(55.0, 30.0, 85.0, 140.0, 175.0, 30.0, 185.0, 160.0);
let bz = Bezier::from_cubic_coordinates(55., 30., 85., 140., 175., 30., 185., 160.);
let p = DVec2::new(17., 172.);
validate(bz, p);
}