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::Point2D;
|
||||||
use euclid::approxeq::ApproxEq;
|
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> {
|
fn quadratic_bezier_axis_inflection_point(p0: f32, p1: f32, p2: f32) -> Option<f32> {
|
||||||
let t = (p0 - p1) / (p0 - 2.0 * p1 + p2);
|
let t = (p0 - p1) / (p0 - 2.0 * p1 + p2);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
use euclid::approxeq::ApproxEq;
|
use euclid::approxeq::ApproxEq;
|
||||||
use euclid::Point2D;
|
use euclid::Point2D;
|
||||||
use geometry::{self, SubdividedQuadraticBezier};
|
use geometry::SubdividedQuadraticBezier;
|
||||||
use log::LogLevel;
|
use log::LogLevel;
|
||||||
use pathfinder_path_utils::PathBuffer;
|
use pathfinder_path_utils::PathBuffer;
|
||||||
use pathfinder_path_utils::curve::Curve;
|
use pathfinder_path_utils::curve::Curve;
|
||||||
|
@ -316,7 +316,7 @@ impl<'a> Partitioner<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(crossing_point) =
|
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 {} & {}",
|
debug!("found SELF-INTERSECTION point for active edges {} & {}",
|
||||||
upper_active_edge_index,
|
upper_active_edge_index,
|
||||||
lower_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]
|
let right_endpoint_position = &self.endpoints[active_edge.right_endpoint_index as usize]
|
||||||
.position;
|
.position;
|
||||||
match active_edge.control_point_vertex_index {
|
match active_edge.control_point_vertex_index {
|
||||||
u32::MAX => {
|
u32::MAX => Line::new(left_vertex_position, right_endpoint_position).solve_t_for_x(x),
|
||||||
geometry::solve_line_t_for_x(x, left_vertex_position, right_endpoint_position)
|
|
||||||
}
|
|
||||||
control_point_vertex_index => {
|
control_point_vertex_index => {
|
||||||
let control_point = &self.b_vertex_positions[control_point_vertex_index as usize];
|
let control_point = &self.b_vertex_positions[control_point_vertex_index as usize];
|
||||||
Curve::new(left_vertex_position,
|
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>> {
|
-> Option<Point2D<f32>> {
|
||||||
let lower_active_edge_index = upper_active_edge_index + 1;
|
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,
|
match (upper_active_edge.control_point_vertex_index,
|
||||||
lower_active_edge.control_point_vertex_index) {
|
lower_active_edge.control_point_vertex_index) {
|
||||||
(u32::MAX, u32::MAX) => {
|
(u32::MAX, u32::MAX) => {
|
||||||
geometry::line_line_crossing_point(upper_left_vertex_position,
|
let (upper_line, _) =
|
||||||
upper_right_endpoint_position,
|
Line::new(upper_left_vertex_position,
|
||||||
lower_left_vertex_position,
|
upper_right_endpoint_position).subdivide_at_x(max_x);
|
||||||
lower_right_endpoint_position)
|
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) => {
|
(upper_control_point_vertex_index, u32::MAX) => {
|
||||||
let upper_control_point =
|
let upper_control_point =
|
||||||
&self.b_vertex_positions[upper_control_point_vertex_index as usize];
|
&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_control_point,
|
||||||
&upper_right_endpoint_position);
|
&upper_right_endpoint_position).subdivide_at_x(max_x);
|
||||||
let lower_line = Line::new(lower_left_vertex_position,
|
let (lower_line, _) =
|
||||||
lower_right_endpoint_position);
|
Line::new(lower_left_vertex_position,
|
||||||
|
lower_right_endpoint_position).subdivide_at_x(max_x);
|
||||||
upper_curve.intersect(&lower_line)
|
upper_curve.intersect(&lower_line)
|
||||||
}
|
}
|
||||||
|
|
||||||
(u32::MAX, lower_control_point_vertex_index) => {
|
(u32::MAX, lower_control_point_vertex_index) => {
|
||||||
let lower_control_point =
|
let lower_control_point =
|
||||||
&self.b_vertex_positions[lower_control_point_vertex_index as usize];
|
&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_control_point,
|
||||||
&lower_right_endpoint_position);
|
&lower_right_endpoint_position).subdivide_at_x(max_x);
|
||||||
let upper_line = Line::new(upper_left_vertex_position,
|
let (upper_line, _) =
|
||||||
upper_right_endpoint_position);
|
Line::new(upper_left_vertex_position,
|
||||||
|
upper_right_endpoint_position).subdivide_at_x(max_x);
|
||||||
lower_curve.intersect(&upper_line)
|
lower_curve.intersect(&upper_line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1004,12 +1009,14 @@ impl<'a> Partitioner<'a> {
|
||||||
&self.b_vertex_positions[upper_control_point_vertex_index as usize];
|
&self.b_vertex_positions[upper_control_point_vertex_index as usize];
|
||||||
let lower_control_point =
|
let lower_control_point =
|
||||||
&self.b_vertex_positions[lower_control_point_vertex_index as usize];
|
&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_control_point,
|
||||||
&upper_right_endpoint_position);
|
&upper_right_endpoint_position).subdivide_at_x(max_x);
|
||||||
let lower_curve = Curve::new(&lower_left_vertex_position,
|
let (lower_curve, _) =
|
||||||
|
Curve::new(&lower_left_vertex_position,
|
||||||
&lower_control_point,
|
&lower_control_point,
|
||||||
&lower_right_endpoint_position);
|
&lower_right_endpoint_position).subdivide_at_x(max_x);
|
||||||
upper_curve.intersect(&lower_curve)
|
upper_curve.intersect(&lower_curve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,15 @@ impl Curve {
|
||||||
(Curve::new(p0, &ap1, &ap2bp0), Curve::new(&ap2bp0, &bp1, p2))
|
(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]
|
#[inline]
|
||||||
pub fn to_path_segment(&self) -> PathSegment {
|
pub fn to_path_segment(&self) -> PathSegment {
|
||||||
PathSegment::CurveTo(self.control_point, self.endpoints[1])
|
PathSegment::CurveTo(self.control_point, self.endpoints[1])
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl Intersect for Line {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn solve_y_for_x(&self, x: f32) -> f32 {
|
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.
|
//! Geometry utilities for straight line segments.
|
||||||
|
|
||||||
|
use euclid::approxeq::ApproxEq;
|
||||||
use euclid::{Point2D, Vector2D};
|
use euclid::{Point2D, Vector2D};
|
||||||
|
|
||||||
use intersection::Intersect;
|
use intersection::Intersect;
|
||||||
|
@ -32,6 +33,36 @@ impl Line {
|
||||||
self.endpoints[0].lerp(self.endpoints[1], t)
|
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]
|
#[inline]
|
||||||
pub fn side(&self, point: &Point2D<f32>) -> f32 {
|
pub fn side(&self, point: &Point2D<f32>) -> f32 {
|
||||||
self.to_vector().cross(*point - self.endpoints[0])
|
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 {
|
pub fn intersect<T>(&self, other: &T) -> Option<Point2D<f32>> where T: Intersect {
|
||||||
<Line as Intersect>::intersect(self, other)
|
<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