diff --git a/partitioner/src/geometry.rs b/partitioner/src/geometry.rs index 004b79ae..7ce159ed 100644 --- a/partitioner/src/geometry.rs +++ b/partitioner/src/geometry.rs @@ -10,68 +10,6 @@ use euclid::Point2D; use euclid::approxeq::ApproxEq; -use pathfinder_path_utils::curve::Curve; -use std::cmp::Ordering; - -pub(crate) trait ApproxOrdered { - fn approx_ordered(&self) -> bool; -} - -impl ApproxOrdered for [f32] { - fn approx_ordered(&self) -> bool { - let mut last_ordering = Ordering::Equal; - for window in self.windows(2) { - let (last_value, this_value) = (window[0], window[1]); - let this_ordering = if last_value - this_value < -f32::approx_epsilon() { - Ordering::Less - } else if last_value - this_value > f32::approx_epsilon() { - Ordering::Greater - } else { - Ordering::Equal - }; - if last_ordering != Ordering::Equal && this_ordering != last_ordering { - return false - } - last_ordering = this_ordering - } - true - } -} - -// https://stackoverflow.com/a/565282 -pub fn line_line_crossing_point(a_p0: &Point2D, - a_p1: &Point2D, - b_p0: &Point2D, - b_p1: &Point2D) - -> Option> { - let (p, r) = (*a_p0, *a_p1 - *a_p0); - let (q, s) = (*b_p0, *b_p1 - *b_p0); - - let rs = r.cross(s); - if rs.approx_eq(&0.0) { - return None - } - - let t = (q - p).cross(s) / rs; - if t < f32::approx_epsilon() || t > 1.0f32 - f32::approx_epsilon() { - return None - } - - let u = (q - p).cross(r) / rs; - if u < f32::approx_epsilon() || u > 1.0f32 - f32::approx_epsilon() { - return None - } - - Some(p + r * t) -} - -pub fn solve_line_t_for_x(x: f32, a: &Point2D, b: &Point2D) -> f32 { - if b.x == a.x { - 0.0 - } else { - (x - a.x) / (b.x - a.x) - } -} fn quadratic_bezier_axis_inflection_point(p0: f32, p1: f32, p2: f32) -> Option { let t = (p0 - p1) / (p0 - 2.0 * p1 + p2); diff --git a/partitioner/src/partitioner.rs b/partitioner/src/partitioner.rs index b64f9284..db3188d8 100644 --- a/partitioner/src/partitioner.rs +++ b/partitioner/src/partitioner.rs @@ -11,7 +11,7 @@ use bit_vec::BitVec; use euclid::approxeq::ApproxEq; use euclid::Point2D; -use geometry::{self, SubdividedQuadraticBezier}; +use geometry::SubdividedQuadraticBezier; use log::LogLevel; use pathfinder_path_utils::PathBuffer; use pathfinder_path_utils::curve::Curve; @@ -316,7 +316,7 @@ impl<'a> Partitioner<'a> { } if let Some(crossing_point) = - self.crossing_point_for_active_edge(upper_active_edge_index) { + self.crossing_point_for_active_edge(upper_active_edge_index, x) { debug!("found SELF-INTERSECTION point for active edges {} & {}", upper_active_edge_index, lower_active_edge_index); @@ -914,9 +914,7 @@ impl<'a> Partitioner<'a> { let right_endpoint_position = &self.endpoints[active_edge.right_endpoint_index as usize] .position; match active_edge.control_point_vertex_index { - u32::MAX => { - geometry::solve_line_t_for_x(x, left_vertex_position, right_endpoint_position) - } + u32::MAX => Line::new(left_vertex_position, right_endpoint_position).solve_t_for_x(x), control_point_vertex_index => { let control_point = &self.b_vertex_positions[control_point_vertex_index as usize]; Curve::new(left_vertex_position, @@ -948,7 +946,7 @@ impl<'a> Partitioner<'a> { } } - fn crossing_point_for_active_edge(&self, upper_active_edge_index: u32) + fn crossing_point_for_active_edge(&self, upper_active_edge_index: u32, max_x: f32) -> Option> { let lower_active_edge_index = upper_active_edge_index + 1; @@ -971,31 +969,38 @@ impl<'a> Partitioner<'a> { match (upper_active_edge.control_point_vertex_index, lower_active_edge.control_point_vertex_index) { (u32::MAX, u32::MAX) => { - geometry::line_line_crossing_point(upper_left_vertex_position, - upper_right_endpoint_position, - lower_left_vertex_position, - lower_right_endpoint_position) + let (upper_line, _) = + Line::new(upper_left_vertex_position, + upper_right_endpoint_position).subdivide_at_x(max_x); + let (lower_line, _) = + Line::new(lower_left_vertex_position, + lower_right_endpoint_position).subdivide_at_x(max_x); + upper_line.intersect_with_line(&lower_line) } (upper_control_point_vertex_index, u32::MAX) => { let upper_control_point = &self.b_vertex_positions[upper_control_point_vertex_index as usize]; - let upper_curve = Curve::new(&upper_left_vertex_position, - &upper_control_point, - &upper_right_endpoint_position); - let lower_line = Line::new(lower_left_vertex_position, - lower_right_endpoint_position); + let (upper_curve, _) = + Curve::new(&upper_left_vertex_position, + &upper_control_point, + &upper_right_endpoint_position).subdivide_at_x(max_x); + let (lower_line, _) = + Line::new(lower_left_vertex_position, + lower_right_endpoint_position).subdivide_at_x(max_x); upper_curve.intersect(&lower_line) } (u32::MAX, lower_control_point_vertex_index) => { let lower_control_point = &self.b_vertex_positions[lower_control_point_vertex_index as usize]; - let lower_curve = Curve::new(&lower_left_vertex_position, - &lower_control_point, - &lower_right_endpoint_position); - let upper_line = Line::new(upper_left_vertex_position, - upper_right_endpoint_position); + let (lower_curve, _) = + Curve::new(&lower_left_vertex_position, + &lower_control_point, + &lower_right_endpoint_position).subdivide_at_x(max_x); + let (upper_line, _) = + Line::new(upper_left_vertex_position, + upper_right_endpoint_position).subdivide_at_x(max_x); lower_curve.intersect(&upper_line) } @@ -1004,12 +1009,14 @@ impl<'a> Partitioner<'a> { &self.b_vertex_positions[upper_control_point_vertex_index as usize]; let lower_control_point = &self.b_vertex_positions[lower_control_point_vertex_index as usize]; - let upper_curve = Curve::new(&upper_left_vertex_position, - &upper_control_point, - &upper_right_endpoint_position); - let lower_curve = Curve::new(&lower_left_vertex_position, - &lower_control_point, - &lower_right_endpoint_position); + let (upper_curve, _) = + Curve::new(&upper_left_vertex_position, + &upper_control_point, + &upper_right_endpoint_position).subdivide_at_x(max_x); + let (lower_curve, _) = + Curve::new(&lower_left_vertex_position, + &lower_control_point, + &lower_right_endpoint_position).subdivide_at_x(max_x); upper_curve.intersect(&lower_curve) } } diff --git a/path-utils/src/curve.rs b/path-utils/src/curve.rs index 23c0a472..c609b808 100644 --- a/path-utils/src/curve.rs +++ b/path-utils/src/curve.rs @@ -48,6 +48,15 @@ impl Curve { (Curve::new(p0, &ap1, &ap2bp0), Curve::new(&ap2bp0, &bp1, p2)) } + pub fn subdivide_at_x(&self, x: f32) -> (Curve, Curve) { + let (prev_part, next_part) = self.subdivide(self.solve_t_for_x(x)); + if self.endpoints[0].x <= self.endpoints[1].x { + (prev_part, next_part) + } else { + (next_part, prev_part) + } + } + #[inline] pub fn to_path_segment(&self) -> PathSegment { PathSegment::CurveTo(self.control_point, self.endpoints[1]) diff --git a/path-utils/src/intersection.rs b/path-utils/src/intersection.rs index 5a6ba1c5..834d957b 100644 --- a/path-utils/src/intersection.rs +++ b/path-utils/src/intersection.rs @@ -65,7 +65,7 @@ impl Intersect for Line { #[inline] fn solve_y_for_x(&self, x: f32) -> f32 { - self.sample((x - self.endpoints[0].x) / (self.endpoints[1].x - self.endpoints[0].x)).y + Line::solve_y_for_x(self, x) } } diff --git a/path-utils/src/line.rs b/path-utils/src/line.rs index 70dbe066..37696a84 100644 --- a/path-utils/src/line.rs +++ b/path-utils/src/line.rs @@ -10,6 +10,7 @@ //! Geometry utilities for straight line segments. +use euclid::approxeq::ApproxEq; use euclid::{Point2D, Vector2D}; use intersection::Intersect; @@ -32,6 +33,36 @@ impl Line { self.endpoints[0].lerp(self.endpoints[1], t) } + #[inline] + pub fn solve_t_for_x(&self, x: f32) -> f32 { + let x_span = self.endpoints[1].x - self.endpoints[0].x; + if x_span != 0.0 { + (x - self.endpoints[0].x) / x_span + } else { + 0.0 + } + } + + #[inline] + pub fn solve_y_for_x(&self, x: f32) -> f32 { + self.sample(self.solve_t_for_x(x)).y + } + + #[inline] + pub fn subdivide(&self, t: f32) -> (Line, Line) { + let midpoint = self.sample(t); + (Line::new(&self.endpoints[0], &midpoint), Line::new(&midpoint, &self.endpoints[1])) + } + + pub fn subdivide_at_x(&self, x: f32) -> (Line, Line) { + let (prev_part, next_part) = self.subdivide(self.solve_t_for_x(x)); + if self.endpoints[0].x <= self.endpoints[1].x { + (prev_part, next_part) + } else { + (next_part, prev_part) + } + } + #[inline] pub fn side(&self, point: &Point2D) -> f32 { self.to_vector().cross(*point - self.endpoints[0]) @@ -46,4 +77,29 @@ impl Line { pub fn intersect(&self, other: &T) -> Option> where T: Intersect { ::intersect(self, other) } + + /// A faster version of `intersect` for the special case of two lines. + /// + /// https://stackoverflow.com/a/565282 + pub fn intersect_with_line(&self, other: &Line) -> Option> { + let (p, r) = (self.endpoints[0], self.to_vector()); + let (q, s) = (self.endpoints[1], other.to_vector()); + + let rs = r.cross(s); + if rs.approx_eq(&0.0) { + return None + } + + let t = (q - p).cross(s) / rs; + if t < f32::approx_epsilon() || t > 1.0f32 - f32::approx_epsilon() { + return None + } + + let u = (q - p).cross(r) / rs; + if u < f32::approx_epsilon() || u > 1.0f32 - f32::approx_epsilon() { + return None + } + + Some(p + r * t) + } }