Implement curve-curve and curve-line intersection, untested as of yet
This commit is contained in:
parent
dbd83d17ef
commit
d644f1b6a6
|
@ -65,27 +65,6 @@ pub fn line_line_crossing_point(a_p0: &Point2D<f32>,
|
|||
Some(p + r * t)
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Implement this.
|
||||
pub fn line_quadratic_bezier_crossing_point(a_p0: &Point2D<f32>,
|
||||
a_p1: &Point2D<f32>,
|
||||
b_p0: &Point2D<f32>,
|
||||
b_p1: &Point2D<f32>,
|
||||
b_p2: &Point2D<f32>)
|
||||
-> Option<Point2D<f32>> {
|
||||
None
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Implement this.
|
||||
pub fn quadratic_bezier_quadratic_bezier_crossing_point(_a_p0: &Point2D<f32>,
|
||||
_a_p1: &Point2D<f32>,
|
||||
_a_p2: &Point2D<f32>,
|
||||
_b_p0: &Point2D<f32>,
|
||||
_b_p1: &Point2D<f32>,
|
||||
_b_p2: &Point2D<f32>)
|
||||
-> Option<Point2D<f32>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn solve_line_t_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
|
||||
if b.x == a.x {
|
||||
0.0
|
||||
|
|
|
@ -14,11 +14,14 @@ use geometry::{self, SubdividedQuadraticBezier};
|
|||
use log::LogLevel;
|
||||
use pathfinder_path_utils::PathBuffer;
|
||||
use pathfinder_path_utils::curve::Curve;
|
||||
use pathfinder_path_utils::intersection::Intersection;
|
||||
use pathfinder_path_utils::line::Line;
|
||||
use std::collections::BinaryHeap;
|
||||
use std::cmp::Ordering;
|
||||
use std::f32;
|
||||
use std::iter;
|
||||
use std::u32;
|
||||
|
||||
use {BQuad, BVertexLoopBlinnData, BVertexKind, CurveIndices, Endpoint, FillRule};
|
||||
use {LineIndices, Subpath};
|
||||
|
||||
|
@ -811,36 +814,50 @@ impl<'a> Partitioner<'a> {
|
|||
lower_left_vertex_position,
|
||||
lower_right_endpoint_position)
|
||||
}
|
||||
|
||||
(upper_control_point_vertex_index, u32::MAX) => {
|
||||
let upper_control_point =
|
||||
&self.b_vertex_positions[upper_control_point_vertex_index as usize];
|
||||
geometry::line_quadratic_bezier_crossing_point(lower_left_vertex_position,
|
||||
lower_right_endpoint_position,
|
||||
upper_left_vertex_position,
|
||||
upper_control_point,
|
||||
upper_right_endpoint_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);
|
||||
|
||||
Intersection::calculate(&upper_curve, &lower_line).map(|intersection| {
|
||||
lower_line.sample(intersection.t_b)
|
||||
})
|
||||
}
|
||||
|
||||
(u32::MAX, lower_control_point_vertex_index) => {
|
||||
let lower_control_point =
|
||||
&self.b_vertex_positions[lower_control_point_vertex_index as usize];
|
||||
geometry::line_quadratic_bezier_crossing_point(upper_left_vertex_position,
|
||||
upper_right_endpoint_position,
|
||||
lower_left_vertex_position,
|
||||
lower_control_point,
|
||||
lower_right_endpoint_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);
|
||||
|
||||
Intersection::calculate(&upper_line, &lower_curve).map(|intersection| {
|
||||
upper_line.sample(intersection.t_a)
|
||||
})
|
||||
}
|
||||
|
||||
(upper_control_point_vertex_index, lower_control_point_vertex_index) => {
|
||||
let upper_control_point =
|
||||
&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];
|
||||
geometry::quadratic_bezier_quadratic_bezier_crossing_point(
|
||||
upper_left_vertex_position,
|
||||
upper_control_point,
|
||||
upper_right_endpoint_position,
|
||||
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);
|
||||
let lower_curve = Curve::new(&lower_left_vertex_position,
|
||||
&lower_control_point,
|
||||
&lower_right_endpoint_position);
|
||||
|
||||
Intersection::calculate(&upper_curve, &lower_curve).map(|intersection| {
|
||||
upper_curve.sample(intersection.t_a)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
//! Geometry utilities for Bézier curves.
|
||||
|
||||
use euclid::Point2D;
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use euclid::Point2D;
|
||||
use std::f32;
|
||||
|
||||
use PathSegment;
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
// pathfinder/path-utils/src/intersection.rs
|
||||
//
|
||||
// Copyright © 2017 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Intersections of two segments.
|
||||
|
||||
use euclid::{Point2D, Rect};
|
||||
|
||||
use curve::Curve;
|
||||
use line::Line;
|
||||
use lerp;
|
||||
|
||||
const SUBDIVISION_TOLERANCE: f32 = 0.0001;
|
||||
const MAX_SUBDIVISIONS: u32 = 1000;
|
||||
|
||||
pub struct Intersection {
|
||||
pub t_a: f32,
|
||||
pub t_b: f32,
|
||||
}
|
||||
|
||||
impl Intersection {
|
||||
/// Requires that any curves be monotonic. (See the `monotonic` module for that.)
|
||||
///
|
||||
/// This should work for line segments, but it is inefficient.
|
||||
///
|
||||
/// See T.W. Sederberg, "Computer Aided Geometric Design Course Notes" § 7.6.
|
||||
pub fn calculate<A, B>(a: &A, b: &B) -> Option<Intersection> where A: Intersect, B: Intersect {
|
||||
let (mut a_lower_t, mut a_upper_t) = (0.0, 1.0);
|
||||
let (mut b_lower_t, mut b_upper_t) = (0.0, 1.0);
|
||||
|
||||
for _ in 0..MAX_SUBDIVISIONS {
|
||||
let a_lower_point = a.sample(a_lower_t);
|
||||
let a_upper_point = a.sample(a_upper_t);
|
||||
let b_lower_point = b.sample(b_lower_t);
|
||||
let b_upper_point = b.sample(b_upper_t);
|
||||
|
||||
let a_distance = (a_upper_point - a_lower_point).length();
|
||||
let b_distance = (b_upper_point - b_lower_point).length();
|
||||
|
||||
let need_to_subdivide_a = a_distance >= SUBDIVISION_TOLERANCE;
|
||||
let need_to_subdivide_b = b_distance >= SUBDIVISION_TOLERANCE;
|
||||
if !need_to_subdivide_b && !need_to_subdivide_a {
|
||||
break
|
||||
}
|
||||
|
||||
let a_rect;
|
||||
if need_to_subdivide_a {
|
||||
let a_middle_t = lerp(a_lower_t, a_upper_t, 0.5);
|
||||
let a_middle_point = a.sample(a_middle_t);
|
||||
|
||||
let a_lower_rect =
|
||||
Rect::from_points(&[a_lower_point, a_middle_point]);
|
||||
let a_upper_rect =
|
||||
Rect::from_points(&[a_middle_point, a_upper_point]);
|
||||
let b_rect = Rect::from_points(&[b_lower_point, b_upper_point]);
|
||||
|
||||
if a_lower_rect.intersects(&b_rect) {
|
||||
a_upper_t = a_middle_t;
|
||||
a_rect = a_lower_rect;
|
||||
} else if a_upper_rect.intersects(&b_rect) {
|
||||
a_lower_t = a_middle_t;
|
||||
a_rect = a_upper_rect;
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
} else {
|
||||
a_rect = Rect::from_points(&[a_lower_point, a_upper_point])
|
||||
}
|
||||
|
||||
if need_to_subdivide_b {
|
||||
let b_middle_t = lerp(b_lower_t, b_upper_t, 0.5);
|
||||
let b_middle_point = b.sample(b_middle_t);
|
||||
|
||||
let b_lower_rect = Rect::from_points(&[b_lower_point, b_middle_point]);
|
||||
let b_upper_rect = Rect::from_points(&[b_middle_point, b_upper_point]);
|
||||
|
||||
if b_lower_rect.intersects(&a_rect) {
|
||||
b_upper_t = b_middle_t
|
||||
} else if b_upper_rect.intersects(&a_rect) {
|
||||
b_lower_t = b_middle_t
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(Intersection {
|
||||
t_a: lerp(a_lower_t, a_upper_t, 0.5),
|
||||
t_b: lerp(b_lower_t, b_upper_t, 0.5),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Intersect {
|
||||
fn sample(&self, t: f32) -> Point2D<f32>;
|
||||
}
|
||||
|
||||
impl Intersect for Line {
|
||||
#[inline]
|
||||
fn sample(&self, t: f32) -> Point2D<f32> {
|
||||
Line::sample(self, t)
|
||||
}
|
||||
}
|
||||
|
||||
impl Intersect for Curve {
|
||||
#[inline]
|
||||
fn sample(&self, t: f32) -> Point2D<f32> {
|
||||
Curve::sample(self, t)
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@ use std::u32;
|
|||
|
||||
pub mod curve;
|
||||
pub mod freetype;
|
||||
pub mod intersection;
|
||||
pub mod line;
|
||||
pub mod monotonic;
|
||||
pub mod stroke;
|
||||
|
||||
|
@ -195,3 +197,8 @@ impl<I> Iterator for Transform2DPathStream<I> where I: Iterator<Item = PathSegme
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
|
||||
a + (b - a) * t
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// pathfinder/path-utils/src/line.rs
|
||||
//
|
||||
// Copyright © 2017 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Geometry utilities for straight line segments.
|
||||
|
||||
use euclid::Point2D;
|
||||
|
||||
pub struct Line {
|
||||
pub endpoints: [Point2D<f32>; 2],
|
||||
}
|
||||
|
||||
impl Line {
|
||||
#[inline]
|
||||
pub fn new(endpoint_0: &Point2D<f32>, endpoint_1: &Point2D<f32>) -> Line {
|
||||
Line {
|
||||
endpoints: [*endpoint_0, *endpoint_1],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sample(&self, t: f32) -> Point2D<f32> {
|
||||
self.endpoints[0].lerp(self.endpoints[1], t)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue