Ignore intersections beyond the sweep line.

Improves the outer stroke of the tiger.
This commit is contained in:
Patrick Walton 2017-09-25 19:15:36 -07:00
parent 93de8383f9
commit c37fd1f1e8
5 changed files with 99 additions and 89 deletions

View File

@ -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<f32>,
a_p1: &Point2D<f32>,
b_p0: &Point2D<f32>,
b_p1: &Point2D<f32>)
-> Option<Point2D<f32>> {
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<f32>, b: &Point2D<f32>) -> 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<f32> {
let t = (p0 - p1) / (p0 - 2.0 * p1 + p2);

View File

@ -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<Point2D<f32>> {
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)
}
}

View File

@ -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])

View File

@ -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)
}
}

View File

@ -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>) -> f32 {
self.to_vector().cross(*point - self.endpoints[0])
@ -46,4 +77,29 @@ impl Line {
pub fn intersect<T>(&self, other: &T) -> Option<Point2D<f32>> where T: Intersect {
<Line as 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<Point2D<f32>> {
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)
}
}