Recursively approximate cubic Béziers with quadratics to an error bound.
Gets the tiger rendering properly, as far as I can tell.
This commit is contained in:
parent
613bc7c29d
commit
e78d7ed575
|
@ -42,6 +42,8 @@ use std::path::{Path, PathBuf};
|
|||
use std::time::{Duration, Instant};
|
||||
use std::u32;
|
||||
|
||||
const CUBIC_ERROR_TOLERANCE: f32 = 0.1;
|
||||
|
||||
static STATIC_INDEX_PATH: &'static str = "../client/index.html";
|
||||
static STATIC_TEXT_DEMO_PATH: &'static str = "../client/text-demo.html";
|
||||
static STATIC_SVG_DEMO_PATH: &'static str = "../client/svg-demo.html";
|
||||
|
@ -528,7 +530,8 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
|
|||
&control_point_1,
|
||||
&endpoint_1);
|
||||
last_point = endpoint_1;
|
||||
stream.extend(cubic.approximate_curve().map(|curve| curve.to_path_segment()));
|
||||
stream.extend(cubic.approximate_curve(CUBIC_ERROR_TOLERANCE)
|
||||
.map(|curve| curve.to_path_segment()));
|
||||
}
|
||||
'Z' => stream.push(PathSegment::ClosePath),
|
||||
_ => return Json(Err(PartitionSvgPathsError::UnknownSvgPathSegmentType)),
|
||||
|
|
|
@ -14,6 +14,8 @@ use euclid::Point2D;
|
|||
|
||||
use curve::Curve;
|
||||
|
||||
const MAX_APPROXIMATION_ITERATIONS: u8 = 32;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct CubicCurve {
|
||||
pub endpoints: [Point2D<f32>; 2],
|
||||
|
@ -51,22 +53,23 @@ impl CubicCurve {
|
|||
CubicCurve::new(&p0p1p2p3, &p1p2p3, &p2p3, &p3))
|
||||
}
|
||||
|
||||
pub fn approximate_curve(&self) -> ApproximateCurveIter {
|
||||
ApproximateCurveIter::new(self)
|
||||
pub fn approximate_curve(&self, error_bound: f32) -> ApproximateCurveIter {
|
||||
ApproximateCurveIter::new(self, error_bound)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(pcwalton): Do better. See: https://github.com/googlei18n/cu2qu
|
||||
pub struct ApproximateCurveIter {
|
||||
curves: [CubicCurve; 2],
|
||||
curves: Vec<CubicCurve>,
|
||||
error_bound: f32,
|
||||
iteration: u8,
|
||||
}
|
||||
|
||||
impl ApproximateCurveIter {
|
||||
fn new(cubic: &CubicCurve) -> ApproximateCurveIter {
|
||||
fn new(cubic: &CubicCurve, error_bound: f32) -> ApproximateCurveIter {
|
||||
let (curve_a, curve_b) = cubic.subdivide(0.5);
|
||||
ApproximateCurveIter {
|
||||
curves: [curve_a, curve_b],
|
||||
curves: vec![curve_b, curve_a],
|
||||
error_bound: error_bound,
|
||||
iteration: 0,
|
||||
}
|
||||
}
|
||||
|
@ -76,12 +79,30 @@ impl Iterator for ApproximateCurveIter {
|
|||
type Item = Curve;
|
||||
|
||||
fn next(&mut self) -> Option<Curve> {
|
||||
let cubic = match self.curves.get(self.iteration as usize) {
|
||||
Some(cubic) => *cubic,
|
||||
let mut cubic = match self.curves.pop() {
|
||||
Some(cubic) => cubic,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
while self.iteration < MAX_APPROXIMATION_ITERATIONS {
|
||||
self.iteration += 1;
|
||||
|
||||
// See Sederberg § 2.6, "Distance Between Two Bézier Curves".
|
||||
let delta_control_point_0 = (cubic.endpoints[0] - cubic.control_points[0] * 3.0) +
|
||||
(cubic.control_points[1] * 3.0 - cubic.endpoints[1]);
|
||||
let delta_control_point_1 = (cubic.control_points[0] * 3.0 - cubic.endpoints[0]) +
|
||||
(cubic.endpoints[1] - cubic.control_points[1] * 3.0);
|
||||
let max_error = f32::max(delta_control_point_1.length(),
|
||||
delta_control_point_0.length()) / 6.0;
|
||||
if max_error < self.error_bound {
|
||||
break
|
||||
}
|
||||
|
||||
let (cubic_a, cubic_b) = cubic.subdivide(0.5);
|
||||
self.curves.push(cubic_b);
|
||||
cubic = cubic_a
|
||||
}
|
||||
|
||||
let approx_control_point_0 = (cubic.control_points[0] * 3.0 - cubic.endpoints[0]) * 0.5;
|
||||
let approx_control_point_1 = (cubic.control_points[1] * 3.0 - cubic.endpoints[1]) * 0.5;
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ use curve::Curve;
|
|||
use line::Line;
|
||||
use {lerp, sign};
|
||||
|
||||
const MAX_ITERATIONS: u8 = 32;
|
||||
|
||||
pub trait Intersect {
|
||||
fn min_x(&self) -> f32;
|
||||
fn max_x(&self) -> f32;
|
||||
|
@ -31,8 +33,11 @@ pub trait Intersect {
|
|||
fn intersect<T>(&self, other: &T) -> Option<Point2D<f32>> where T: Intersect {
|
||||
let mut min_x = f32::max(self.min_x(), other.min_x());
|
||||
let mut max_x = f32::min(self.max_x(), other.max_x());
|
||||
let mut iteration = 0;
|
||||
|
||||
while iteration < MAX_ITERATIONS && max_x - min_x > f32::approx_epsilon() {
|
||||
iteration += 1;
|
||||
|
||||
while max_x - min_x > f32::approx_epsilon() {
|
||||
let mid_x = lerp(min_x, max_x, 0.5);
|
||||
let min_sign = sign(self.solve_y_for_x(min_x) - other.solve_y_for_x(min_x));
|
||||
let mid_sign = sign(self.solve_y_for_x(mid_x) - other.solve_y_for_x(mid_x));
|
||||
|
|
Loading…
Reference in New Issue