From 0a4fcb48415a7f6d1ff28b09ec87abd255cbc13f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 23 Oct 2017 20:05:36 -0700 Subject: [PATCH] Add a cubic curve command stream. This is a prerequisite for handling CFF fonts. --- demo/server/src/main.rs | 2 +- path-utils/src/cubic.rs | 91 +++++++++++++++++++++++++++++++++++++---- path-utils/src/lib.rs | 1 - 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index 5386821f..8fbe80df 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -366,7 +366,7 @@ fn partition_svg_paths(request: Json) &control_point_1, &endpoint_1); last_point = endpoint_1; - stream.extend(cubic.approximate_curve(CUBIC_ERROR_TOLERANCE) + stream.extend(cubic.approx_curve(CUBIC_ERROR_TOLERANCE) .map(|curve| curve.to_path_segment())); } 'Z' => stream.push(PathCommand::ClosePath), diff --git a/path-utils/src/cubic.rs b/path-utils/src/cubic.rs index 59da8b78..56c54f8a 100644 --- a/path-utils/src/cubic.rs +++ b/path-utils/src/cubic.rs @@ -13,6 +13,7 @@ use euclid::Point2D; use curve::Curve; +use PathCommand; const MAX_APPROXIMATION_ITERATIONS: u8 = 32; @@ -53,21 +54,97 @@ impl CubicCurve { CubicCurve::new(&p0p1p2p3, &p1p2p3, &p2p3, &p3)) } - pub fn approximate_curve(&self, error_bound: f32) -> ApproximateCurveIter { - ApproximateCurveIter::new(self, error_bound) + pub fn approx_curve(&self, error_bound: f32) -> ApproxCurveIter { + ApproxCurveIter::new(self, error_bound) } } -pub struct ApproximateCurveIter { +/// A series of path commands that can contain cubic Bézier segments. +#[derive(Clone, Copy, Debug)] +pub enum CubicPathCommand { + MoveTo(Point2D), + LineTo(Point2D), + QuadCurveTo(Point2D, Point2D), + CubicCurveTo(Point2D, Point2D, Point2D), + ClosePath, +} + +pub struct CubicPathCommandApproxStream { + inner: I, + error_bound: f32, + last_endpoint: Point2D, + approx_curve_iter: Option, +} + +impl CubicPathCommandApproxStream where I: Iterator { + #[inline] + pub fn new(inner: I, error_bound: f32) -> CubicPathCommandApproxStream { + CubicPathCommandApproxStream { + inner: inner, + error_bound: error_bound, + last_endpoint: Point2D::zero(), + approx_curve_iter: None, + } + } +} + +impl Iterator for CubicPathCommandApproxStream where I: Iterator { + type Item = PathCommand; + + fn next(&mut self) -> Option { + loop { + if let Some(ref mut approx_curve_iter) = self.approx_curve_iter { + if let Some(curve) = approx_curve_iter.next() { + return Some(curve.to_path_segment()) + } + } + self.approx_curve_iter = None; + + let next_command = match self.inner.next() { + None => return None, + Some(next_command) => next_command, + }; + + match next_command { + CubicPathCommand::ClosePath => { + self.last_endpoint = Point2D::zero(); + return Some(PathCommand::ClosePath) + } + CubicPathCommand::MoveTo(endpoint) => { + self.last_endpoint = endpoint; + return Some(PathCommand::MoveTo(endpoint)) + } + CubicPathCommand::LineTo(endpoint) => { + self.last_endpoint = endpoint; + return Some(PathCommand::LineTo(endpoint)) + } + CubicPathCommand::QuadCurveTo(control_point, endpoint) => { + self.last_endpoint = endpoint; + return Some(PathCommand::CurveTo(control_point, endpoint)) + } + CubicPathCommand::CubicCurveTo(control_point_0, control_point_1, endpoint) => { + let curve = CubicCurve::new(&self.last_endpoint, + &control_point_0, + &control_point_1, + &endpoint); + self.last_endpoint = endpoint; + self.approx_curve_iter = Some(ApproxCurveIter::new(&curve, self.error_bound)); + } + } + } + } +} + +pub struct ApproxCurveIter { curves: Vec, error_bound: f32, iteration: u8, } -impl ApproximateCurveIter { - fn new(cubic: &CubicCurve, error_bound: f32) -> ApproximateCurveIter { +impl ApproxCurveIter { + fn new(cubic: &CubicCurve, error_bound: f32) -> ApproxCurveIter { let (curve_a, curve_b) = cubic.subdivide(0.5); - ApproximateCurveIter { + ApproxCurveIter { curves: vec![curve_b, curve_a], error_bound: error_bound, iteration: 0, @@ -75,7 +152,7 @@ impl ApproximateCurveIter { } } -impl Iterator for ApproximateCurveIter { +impl Iterator for ApproxCurveIter { type Item = Curve; fn next(&mut self) -> Option { diff --git a/path-utils/src/lib.rs b/path-utils/src/lib.rs index 307b7c10..912b3c7a 100644 --- a/path-utils/src/lib.rs +++ b/path-utils/src/lib.rs @@ -13,7 +13,6 @@ extern crate euclid; #[macro_use] extern crate serde_derive; -use euclid::approxeq::ApproxEq; use euclid::{Point2D, Transform2D}; use std::mem; use std::ops::Range;