Document most of `pathfinder_path_utils`.

This commit is contained in:
Patrick Walton 2018-01-05 14:20:50 -08:00
parent 4b887174f8
commit 32c20ec649
3 changed files with 67 additions and 6 deletions

View File

@ -80,8 +80,8 @@ pub enum CubicPathCommand {
QuadCurveTo(Point2D<f32>, Point2D<f32>), QuadCurveTo(Point2D<f32>, Point2D<f32>),
/// Draws a cubic cubic curve with the two control points to the endpoint, respectively. /// Draws a cubic cubic curve with the two control points to the endpoint, respectively.
CubicCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>), CubicCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>),
/// Closes the current subpath by drawing a line from the given point to the first point of the /// Closes the current subpath by drawing a line from the current point to the first point of
/// subpath. /// the subpath.
ClosePath, ClosePath,
} }

View File

@ -8,6 +8,13 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
//! Various utilities for manipulating Bézier curves.
//!
//! On its own, the partitioner can only generate meshes for fill operations on quadratic Bézier
//! curves. Frequently, however, other vector drawing operations are desired: for example,
//! rendering cubic Béziers or stroking paths. These utilities can convert those complex operations
//! into simpler sequences of quadratic Béziers that the partitioner can handle.
extern crate arrayvec; extern crate arrayvec;
extern crate euclid; extern crate euclid;
#[macro_use] #[macro_use]
@ -26,23 +33,38 @@ pub mod monotonic;
pub mod stroke; pub mod stroke;
pub mod svg; pub mod svg;
/// A series of commands that define quadratic Bézier paths.
///
/// For cubics, see the `cubic` module.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum PathCommand { pub enum PathCommand {
/// Moves the pen to the given point.
MoveTo(Point2D<f32>), MoveTo(Point2D<f32>),
/// Draws a line to the given point.
LineTo(Point2D<f32>), LineTo(Point2D<f32>),
/// Control point and endpoint, respectively. /// Draws a quadratic curve with the control point to the endpoint, respectively.
CurveTo(Point2D<f32>, Point2D<f32>), CurveTo(Point2D<f32>, Point2D<f32>),
/// Closes the current subpath by drawing a line from the current point to the first point of
/// the subpath.
ClosePath, ClosePath,
} }
/// Holds one or more paths in memory in an efficient form.
///
/// This structure is generally preferable to `Vec<PathCommand>` if you need to buffer paths in
/// memory. It is both smaller and offers random access to individual subpaths.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PathBuffer { pub struct PathBuffer {
/// All endpoints of all subpaths.
pub endpoints: Vec<Endpoint>, pub endpoints: Vec<Endpoint>,
/// All control points of all subpaths.
pub control_points: Vec<Point2D<f32>>, pub control_points: Vec<Point2D<f32>>,
/// A series of ranges defining each subpath.
pub subpaths: Vec<Subpath>, pub subpaths: Vec<Subpath>,
} }
impl PathBuffer { impl PathBuffer {
/// Creates a new, empty path buffer.
#[inline] #[inline]
pub fn new() -> PathBuffer { pub fn new() -> PathBuffer {
PathBuffer { PathBuffer {
@ -52,6 +74,7 @@ impl PathBuffer {
} }
} }
/// Appends a sequence of path commands to this path buffer.
pub fn add_stream<I>(&mut self, stream: I) where I: Iterator<Item = PathCommand> { pub fn add_stream<I>(&mut self, stream: I) where I: Iterator<Item = PathCommand> {
let mut first_subpath_endpoint_index = self.endpoints.len() as u32; let mut first_subpath_endpoint_index = self.endpoints.len() as u32;
for segment in stream { for segment in stream {
@ -116,6 +139,7 @@ impl PathBuffer {
*first_subpath_endpoint_index = last_subpath_endpoint_index; *first_subpath_endpoint_index = last_subpath_endpoint_index;
} }
/// Reverses the winding order of the subpath with the given index.
pub fn reverse_subpath(&mut self, subpath_index: u32) { pub fn reverse_subpath(&mut self, subpath_index: u32) {
let subpath = &self.subpaths[subpath_index as usize]; let subpath = &self.subpaths[subpath_index as usize];
let endpoint_range = subpath.range(); let endpoint_range = subpath.range();
@ -134,6 +158,7 @@ impl PathBuffer {
} }
} }
/// Converts a path buffer back into a series of path commands.
#[derive(Clone)] #[derive(Clone)]
pub struct PathBufferStream<'a> { pub struct PathBufferStream<'a> {
path_buffer: &'a PathBuffer, path_buffer: &'a PathBuffer,
@ -143,6 +168,7 @@ pub struct PathBufferStream<'a> {
} }
impl<'a> PathBufferStream<'a> { impl<'a> PathBufferStream<'a> {
/// Prepares a path buffer stream to stream all subpaths from the given path buffer.
#[inline] #[inline]
pub fn new<'b>(path_buffer: &'b PathBuffer) -> PathBufferStream<'b> { pub fn new<'b>(path_buffer: &'b PathBuffer) -> PathBufferStream<'b> {
PathBufferStream { PathBufferStream {
@ -153,6 +179,8 @@ impl<'a> PathBufferStream<'a> {
} }
} }
/// Prepares a path buffer stream to stream only a subset of subpaths from the given path
/// buffer.
#[inline] #[inline]
pub fn subpath_range<'b>(path_buffer: &'b PathBuffer, subpath_range: Range<u32>) pub fn subpath_range<'b>(path_buffer: &'b PathBuffer, subpath_range: Range<u32>)
-> PathBufferStream<'b> { -> PathBufferStream<'b> {
@ -206,38 +234,56 @@ impl<'a> Iterator for PathBufferStream<'a> {
} }
} }
/// Describes a path endpoint in a path buffer.
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Endpoint { pub struct Endpoint {
/// The 2D position of the endpoint.
pub position: Point2D<f32>, pub position: Point2D<f32>,
/// `u32::MAX` if not present. /// The index of the control point *before* this endpoint in the `control_points` vector, or
/// `u32::MAX` if this endpoint is the end of a line segment.
pub control_point_index: u32, pub control_point_index: u32,
/// The index of the subpath that this endpoint belongs to.
pub subpath_index: u32, pub subpath_index: u32,
} }
/// Stores the endpoint indices of each subpath.
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Subpath { pub struct Subpath {
/// The index of the first endpoint that makes up this subpath.
pub first_endpoint_index: u32, pub first_endpoint_index: u32,
/// One plus the index of the last endpoint that makes up this subpath.
pub last_endpoint_index: u32, pub last_endpoint_index: u32,
/// Whether the subpath is closed (i.e. fully connected).
pub closed: bool, pub closed: bool,
} }
impl Subpath { impl Subpath {
/// Returns the endpoint indices as a `Range`.
#[inline] #[inline]
pub fn range(self) -> Range<usize> { pub fn range(self) -> Range<usize> {
(self.first_endpoint_index as usize)..(self.last_endpoint_index as usize) (self.first_endpoint_index as usize)..(self.last_endpoint_index as usize)
} }
} }
/// Represents a single path segment (i.e. a single side of a Béziergon).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum PathSegment { pub enum PathSegment {
/// First endpoint and second endpoint, respectively. /// A line segment with two endpoints.
Line(Point2D<f32>, Point2D<f32>), Line(Point2D<f32>, Point2D<f32>),
/// First endpoint, control point, and second endpoint, in that order. /// A quadratic Bézier curve with an endpoint, a control point, and another endpoint, in that
/// order.
Curve(Point2D<f32>, Point2D<f32>, Point2D<f32>), Curve(Point2D<f32>, Point2D<f32>, Point2D<f32>),
} }
/// Yields a set of `PathSegment`s corresponding to a list of `PathCommand`s.
///
/// For example, the path commands `[MoveTo(A), LineTo(B), LineTo(C), ClosePath]` become
/// `[Line(A, B), Line(B, C), Line(C, A)]`.
///
/// This representation can simplify the implementation of certain geometric algorithms, such as
/// offset paths (stroking).
pub struct PathSegmentStream<I> { pub struct PathSegmentStream<I> {
inner: I, inner: I,
current_subpath_index: u32, current_subpath_index: u32,
@ -246,6 +292,8 @@ pub struct PathSegmentStream<I> {
} }
impl<I> PathSegmentStream<I> where I: Iterator<Item = PathCommand> { impl<I> PathSegmentStream<I> where I: Iterator<Item = PathCommand> {
/// Creates a new path segment stream that will yield path segments from the given collection
/// of path commands.
pub fn new(inner: I) -> PathSegmentStream<I> { pub fn new(inner: I) -> PathSegmentStream<I> {
PathSegmentStream { PathSegmentStream {
inner: inner, inner: inner,
@ -301,12 +349,14 @@ impl<I> Iterator for PathSegmentStream<I> where I: Iterator<Item = PathCommand>
} }
} }
/// Applies an affine transform to a path stream and yields the resulting path stream.
pub struct Transform2DPathStream<I> { pub struct Transform2DPathStream<I> {
inner: I, inner: I,
transform: Transform2D<f32>, transform: Transform2D<f32>,
} }
impl<I> Transform2DPathStream<I> where I: Iterator<Item = PathCommand> { impl<I> Transform2DPathStream<I> where I: Iterator<Item = PathCommand> {
/// Creates a new transformed path stream from a path stream.
pub fn new(inner: I, transform: &Transform2D<f32>) -> Transform2DPathStream<I> { pub fn new(inner: I, transform: &Transform2D<f32>) -> Transform2DPathStream<I> {
Transform2DPathStream { Transform2DPathStream {
inner: inner, inner: inner,
@ -336,11 +386,16 @@ impl<I> Iterator for Transform2DPathStream<I> where I: Iterator<Item = PathComma
} }
} }
/// Linear interpolation: `lerp(a, b, t)` = `a + (b - a) * t`.
#[inline] #[inline]
pub(crate) fn lerp(a: f32, b: f32, t: f32) -> f32 { pub(crate) fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t a + (b - a) * t
} }
/// Returns -1.0 if the value is negative, 1.0 if the value is positive, or 0.0 if the value is
/// zero.
///
/// Returns NaN when given NaN.
#[inline] #[inline]
pub(crate) fn sign(x: f32) -> f32 { pub(crate) fn sign(x: f32) -> f32 {
if x < 0.0 { if x < 0.0 {

View File

@ -17,6 +17,10 @@ use std::mem;
use PathCommand; use PathCommand;
use curve::Curve; use curve::Curve;
/// Converts a stream of path commands into one in which all curves are monotonically increasing
/// or decreasing.
///
/// Equivalently, all curves have X or Y inflection points only at endpoints, if at all.
#[derive(Clone)] #[derive(Clone)]
pub struct MonotonicPathCommandStream<I> { pub struct MonotonicPathCommandStream<I> {
inner: I, inner: I,
@ -25,6 +29,8 @@ pub struct MonotonicPathCommandStream<I> {
} }
impl<I> MonotonicPathCommandStream<I> where I: Iterator<Item = PathCommand> { impl<I> MonotonicPathCommandStream<I> where I: Iterator<Item = PathCommand> {
/// Creates a new monotonic path command stream, which converts a stream of path commands into
/// one in which each curve is monotonially increasing or decreasing.
pub fn new(inner: I) -> MonotonicPathCommandStream<I> { pub fn new(inner: I) -> MonotonicPathCommandStream<I> {
MonotonicPathCommandStream { MonotonicPathCommandStream {
inner: inner, inner: inner,