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)
|
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 {
|
pub fn solve_line_t_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
|
||||||
if b.x == a.x {
|
if b.x == a.x {
|
||||||
0.0
|
0.0
|
||||||
|
|
|
@ -14,11 +14,14 @@ use geometry::{self, 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;
|
||||||
|
use pathfinder_path_utils::intersection::Intersection;
|
||||||
|
use pathfinder_path_utils::line::Line;
|
||||||
use std::collections::BinaryHeap;
|
use std::collections::BinaryHeap;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
|
||||||
use {BQuad, BVertexLoopBlinnData, BVertexKind, CurveIndices, Endpoint, FillRule};
|
use {BQuad, BVertexLoopBlinnData, BVertexKind, CurveIndices, Endpoint, FillRule};
|
||||||
use {LineIndices, Subpath};
|
use {LineIndices, Subpath};
|
||||||
|
|
||||||
|
@ -811,36 +814,50 @@ impl<'a> Partitioner<'a> {
|
||||||
lower_left_vertex_position,
|
lower_left_vertex_position,
|
||||||
lower_right_endpoint_position)
|
lower_right_endpoint_position)
|
||||||
}
|
}
|
||||||
|
|
||||||
(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];
|
||||||
geometry::line_quadratic_bezier_crossing_point(lower_left_vertex_position,
|
let upper_curve = Curve::new(&upper_left_vertex_position,
|
||||||
lower_right_endpoint_position,
|
&upper_control_point,
|
||||||
upper_left_vertex_position,
|
&upper_right_endpoint_position);
|
||||||
upper_control_point,
|
let lower_line = Line::new(lower_left_vertex_position,
|
||||||
upper_right_endpoint_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) => {
|
(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];
|
||||||
geometry::line_quadratic_bezier_crossing_point(upper_left_vertex_position,
|
let lower_curve = Curve::new(&lower_left_vertex_position,
|
||||||
upper_right_endpoint_position,
|
&lower_control_point,
|
||||||
lower_left_vertex_position,
|
&lower_right_endpoint_position);
|
||||||
lower_control_point,
|
let upper_line = Line::new(upper_left_vertex_position,
|
||||||
lower_right_endpoint_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) => {
|
(upper_control_point_vertex_index, lower_control_point_vertex_index) => {
|
||||||
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 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];
|
||||||
geometry::quadratic_bezier_quadratic_bezier_crossing_point(
|
let upper_curve = Curve::new(&upper_left_vertex_position,
|
||||||
upper_left_vertex_position,
|
&upper_control_point,
|
||||||
upper_control_point,
|
&upper_right_endpoint_position);
|
||||||
upper_right_endpoint_position,
|
let lower_curve = Curve::new(&lower_left_vertex_position,
|
||||||
lower_left_vertex_position,
|
&lower_control_point,
|
||||||
lower_control_point,
|
&lower_right_endpoint_position);
|
||||||
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.
|
//! Geometry utilities for Bézier curves.
|
||||||
|
|
||||||
use euclid::Point2D;
|
|
||||||
use euclid::approxeq::ApproxEq;
|
use euclid::approxeq::ApproxEq;
|
||||||
|
use euclid::Point2D;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
use PathSegment;
|
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 curve;
|
||||||
pub mod freetype;
|
pub mod freetype;
|
||||||
|
pub mod intersection;
|
||||||
|
pub mod line;
|
||||||
pub mod monotonic;
|
pub mod monotonic;
|
||||||
pub mod stroke;
|
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