Ignore intersections beyond the sweep line.
Improves the outer stroke of the tiger.
This commit is contained in:
parent
93de8383f9
commit
c37fd1f1e8
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
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);
|
||||
&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,
|
||||
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);
|
||||
&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,
|
||||
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,
|
||||
&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);
|
||||
&lower_right_endpoint_position).subdivide_at_x(max_x);
|
||||
upper_curve.intersect(&lower_curve)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue