Auto merge of #392 - pcwalton:docs-outline, r=pcwalton
Document the `pathfinder_content::outline` module
This commit is contained in:
commit
f62f85354f
|
@ -8,9 +8,7 @@
|
||||||
// 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.
|
||||||
|
|
||||||
//! Pathfinder's representation of a vector scene.
|
//! Components of a vector scene, and various path utilities.
|
||||||
//!
|
|
||||||
//! This module also contains various path utilities.
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// 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.
|
||||||
|
|
||||||
//! A compressed in-memory representation of paths.
|
//! A compressed in-memory representation of a vector path.
|
||||||
|
|
||||||
use crate::clip::{self, ContourPolygonClipper};
|
use crate::clip::{self, ContourPolygonClipper};
|
||||||
use crate::dilation::ContourDilator;
|
use crate::dilation::ContourDilator;
|
||||||
|
@ -25,12 +25,20 @@ use std::f32::consts::PI;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
/// A vector path to be filled. Outlines (a.k.a. paths) consist of *contours* (a.k.a. subpaths),
|
||||||
|
/// which can be filled according to a fill rule.
|
||||||
|
///
|
||||||
|
/// The names "outline" and "contour" come from the TrueType specification. They were chosen to
|
||||||
|
/// avoid conflicting with the Rust use of "path" for filesystem paths.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Outline {
|
pub struct Outline {
|
||||||
pub(crate) contours: Vec<Contour>,
|
pub(crate) contours: Vec<Contour>,
|
||||||
pub(crate) bounds: RectF,
|
pub(crate) bounds: RectF,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An individual subpath, consisting of a series of endpoints and/or control points. Contours can
|
||||||
|
/// be either open (first and last points disconnected) or closed (first point implicitly joined to
|
||||||
|
/// last point with a line).
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Contour {
|
pub struct Contour {
|
||||||
pub(crate) points: Vec<Vector2F>,
|
pub(crate) points: Vec<Vector2F>,
|
||||||
|
@ -40,20 +48,29 @@ pub struct Contour {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// Flags that each point can have, indicating whether it is on-curve or whether it's a control
|
||||||
|
/// point.
|
||||||
pub struct PointFlags: u8 {
|
pub struct PointFlags: u8 {
|
||||||
|
/// This point is the first control point of a cubic Bézier curve or the only control point
|
||||||
|
/// of a quadratic Bézier curve.
|
||||||
const CONTROL_POINT_0 = 0x01;
|
const CONTROL_POINT_0 = 0x01;
|
||||||
|
/// This point is the second point of a quadratic Bézier curve.
|
||||||
const CONTROL_POINT_1 = 0x02;
|
const CONTROL_POINT_1 = 0x02;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct PushSegmentFlags: u8 {
|
// Flags specifying what actions to take when pushing a segment onto a contour.
|
||||||
|
pub(crate) struct PushSegmentFlags: u8 {
|
||||||
|
/// The bounds should be updated.
|
||||||
const UPDATE_BOUNDS = 0x01;
|
const UPDATE_BOUNDS = 0x01;
|
||||||
|
/// The "from" point of the segme
|
||||||
const INCLUDE_FROM_POINT = 0x02;
|
const INCLUDE_FROM_POINT = 0x02;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Outline {
|
impl Outline {
|
||||||
|
/// Creates a new empty outline with no contours.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Outline {
|
pub fn new() -> Outline {
|
||||||
Outline {
|
Outline {
|
||||||
|
@ -71,11 +88,9 @@ impl Outline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new outline from a list of segments.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_segments<I>(segments: I) -> Outline
|
pub fn from_segments<I>(segments: I) -> Outline where I: Iterator<Item = Segment> {
|
||||||
where
|
|
||||||
I: Iterator<Item = Segment>,
|
|
||||||
{
|
|
||||||
let mut outline = Outline::new();
|
let mut outline = Outline::new();
|
||||||
let mut current_contour = Contour::new();
|
let mut current_contour = Contour::new();
|
||||||
|
|
||||||
|
@ -120,6 +135,7 @@ impl Outline {
|
||||||
outline
|
outline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new outline that represents a single axis-aligned rectangle.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_rect(rect: RectF) -> Outline {
|
pub fn from_rect(rect: RectF) -> Outline {
|
||||||
let mut outline = Outline::new();
|
let mut outline = Outline::new();
|
||||||
|
@ -127,6 +143,7 @@ impl Outline {
|
||||||
outline
|
outline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new outline that represents a rounded rectangle.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_rect_rounded(rect: RectF, radius: Vector2F) -> Outline {
|
pub fn from_rect_rounded(rect: RectF, radius: Vector2F) -> Outline {
|
||||||
let mut outline = Outline::new();
|
let mut outline = Outline::new();
|
||||||
|
@ -134,16 +151,19 @@ impl Outline {
|
||||||
outline
|
outline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the dimensions of an axis-aligned box that encloses the entire outline.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn bounds(&self) -> RectF {
|
pub fn bounds(&self) -> RectF {
|
||||||
self.bounds
|
self.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of the subpaths in this path.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contours(&self) -> &[Contour] {
|
pub fn contours(&self) -> &[Contour] {
|
||||||
&self.contours
|
&self.contours
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destroys this outline and returns a list of its subpaths.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_contours(self) -> Vec<Contour> {
|
pub fn into_contours(self) -> Vec<Contour> {
|
||||||
self.contours
|
self.contours
|
||||||
|
@ -156,6 +176,7 @@ impl Outline {
|
||||||
self.bounds = RectF::default();
|
self.bounds = RectF::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new subpath to this outline.
|
||||||
pub fn push_contour(&mut self, contour: Contour) {
|
pub fn push_contour(&mut self, contour: Contour) {
|
||||||
if contour.is_empty() {
|
if contour.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
@ -170,6 +191,7 @@ impl Outline {
|
||||||
self.contours.push(contour);
|
self.contours.push(contour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the last subpath from this outline and returns it.
|
||||||
pub fn pop_contour(&mut self) -> Option<Contour> {
|
pub fn pop_contour(&mut self) -> Option<Contour> {
|
||||||
let last_contour = self.contours.pop();
|
let last_contour = self.contours.pop();
|
||||||
|
|
||||||
|
@ -182,6 +204,7 @@ impl Outline {
|
||||||
last_contour
|
last_contour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies an affine transform to this outline and all its subpaths.
|
||||||
pub fn transform(&mut self, transform: &Transform2F) {
|
pub fn transform(&mut self, transform: &Transform2F) {
|
||||||
if transform.is_identity() {
|
if transform.is_identity() {
|
||||||
return;
|
return;
|
||||||
|
@ -195,11 +218,16 @@ impl Outline {
|
||||||
self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
|
self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies an affine transform to this outline and all its subpaths, consuming this outline
|
||||||
|
/// instead of mutating it.
|
||||||
pub fn transformed(mut self, transform: &Transform2F) -> Outline {
|
pub fn transformed(mut self, transform: &Transform2F) -> Outline {
|
||||||
self.transform(transform);
|
self.transform(transform);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies a perspective transform to this outline.
|
||||||
|
#[deprecated]
|
||||||
|
#[allow(deprecated)]
|
||||||
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
||||||
let mut new_bounds = None;
|
let mut new_bounds = None;
|
||||||
for contour in &mut self.contours {
|
for contour in &mut self.contours {
|
||||||
|
@ -209,6 +237,9 @@ impl Outline {
|
||||||
self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
|
self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Thickens the outline by the given amount.
|
||||||
|
///
|
||||||
|
/// This is implemented by pushing vectors out along their normals.
|
||||||
pub fn dilate(&mut self, amount: Vector2F) {
|
pub fn dilate(&mut self, amount: Vector2F) {
|
||||||
let orientation = Orientation::from_outline(self);
|
let orientation = Orientation::from_outline(self);
|
||||||
self.contours
|
self.contours
|
||||||
|
@ -217,6 +248,10 @@ impl Outline {
|
||||||
self.bounds = self.bounds.dilate(amount);
|
self.bounds = self.bounds.dilate(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this outline is obviously completely outside the closed polygon with the
|
||||||
|
/// given vertices, via a quick check.
|
||||||
|
///
|
||||||
|
/// Even if the outline is outside the polygon, this might return false.
|
||||||
pub fn is_outside_polygon(&self, clip_polygon: &[Vector2F]) -> bool {
|
pub fn is_outside_polygon(&self, clip_polygon: &[Vector2F]) -> bool {
|
||||||
clip::rect_is_outside_polygon(self.bounds, clip_polygon)
|
clip::rect_is_outside_polygon(self.bounds, clip_polygon)
|
||||||
}
|
}
|
||||||
|
@ -225,6 +260,9 @@ impl Outline {
|
||||||
clip::rect_is_inside_polygon(self.bounds, clip_polygon)
|
clip::rect_is_inside_polygon(self.bounds, clip_polygon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clips this outline against the given closed polygon with the given vertices.
|
||||||
|
///
|
||||||
|
/// This is implemented with Sutherland-Hodgman clipping.
|
||||||
pub fn clip_against_polygon(&mut self, clip_polygon: &[Vector2F]) {
|
pub fn clip_against_polygon(&mut self, clip_polygon: &[Vector2F]) {
|
||||||
// Quick check.
|
// Quick check.
|
||||||
if self.is_inside_polygon(clip_polygon) {
|
if self.is_inside_polygon(clip_polygon) {
|
||||||
|
@ -236,11 +274,13 @@ impl Outline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marks all contours as closed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn close_all_contours(&mut self) {
|
pub fn close_all_contours(&mut self) {
|
||||||
self.contours.iter_mut().for_each(|contour| contour.close());
|
self.contours.iter_mut().for_each(|contour| contour.close());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this outline has no points.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.contours.iter().all(Contour::is_empty)
|
self.contours.iter().all(Contour::is_empty)
|
||||||
|
@ -252,7 +292,7 @@ impl Outline {
|
||||||
self.contours.len()
|
self.contours.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends the contours in another outline to this one.
|
/// Appends the contours of another outline to this one.
|
||||||
pub fn push_outline(&mut self, other: Outline) {
|
pub fn push_outline(&mut self, other: Outline) {
|
||||||
if other.is_empty() {
|
if other.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
@ -281,6 +321,7 @@ impl Debug for Outline {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Contour {
|
impl Contour {
|
||||||
|
/// Creates a new empty unclosed subpath.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Contour {
|
pub fn new() -> Contour {
|
||||||
Contour {
|
Contour {
|
||||||
|
@ -291,6 +332,8 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new empty unclosed subpath with space preallocated for the given number of
|
||||||
|
/// points.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_capacity(length: usize) -> Contour {
|
pub fn with_capacity(length: usize) -> Contour {
|
||||||
Contour {
|
Contour {
|
||||||
|
@ -301,6 +344,7 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a closed subpath representing the given axis-aligned rectangle.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_rect(rect: RectF) -> Contour {
|
pub fn from_rect(rect: RectF) -> Contour {
|
||||||
let mut contour = Contour::with_capacity(4);
|
let mut contour = Contour::with_capacity(4);
|
||||||
|
@ -313,6 +357,7 @@ impl Contour {
|
||||||
contour
|
contour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a closed subpath representing the given axis-aligned rounded rectangle.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_rect_rounded(rect: RectF, radius: Vector2F) -> Contour {
|
pub fn from_rect_rounded(rect: RectF, radius: Vector2F) -> Contour {
|
||||||
use std::f32::consts::SQRT_2;
|
use std::f32::consts::SQRT_2;
|
||||||
|
@ -397,7 +442,8 @@ impl Contour {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// restore self to the state of Contour::new(), but keep the points buffer allocated
|
/// Restores this contour to the state of `Contour::new()` but keeps the points buffer
|
||||||
|
/// allocated.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.points.clear();
|
self.points.clear();
|
||||||
|
@ -406,6 +452,7 @@ impl Contour {
|
||||||
self.closed = false;
|
self.closed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the segments in this contour.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter(&self, flags: ContourIterFlags) -> ContourIter {
|
pub fn iter(&self, flags: ContourIterFlags) -> ContourIter {
|
||||||
ContourIter {
|
ContourIter {
|
||||||
|
@ -415,36 +462,46 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this contour has no points.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.points.is_empty()
|
self.points.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of points (including on-curve and control points) in this contour.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> u32 {
|
pub fn len(&self) -> u32 {
|
||||||
self.points.len() as u32
|
self.points.len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the dimensions of an axis-aligned rectangle that encloses this contour.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn bounds(&self) -> RectF {
|
pub fn bounds(&self) -> RectF {
|
||||||
self.bounds
|
self.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this contour is closed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_closed(&self) -> bool {
|
pub fn is_closed(&self) -> bool {
|
||||||
self.closed
|
self.closed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the position of the point (which can be an on-curve point or a control point) with
|
||||||
|
/// the given index.
|
||||||
|
///
|
||||||
|
/// Panics if the index is out of bounds.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn position_of(&self, index: u32) -> Vector2F {
|
pub fn position_of(&self, index: u32) -> Vector2F {
|
||||||
self.points[index as usize]
|
self.points[index as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the position of the first point in this subpath.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_position(&self) -> Option<Vector2F> {
|
pub fn first_position(&self) -> Option<Vector2F> {
|
||||||
self.points.first().cloned()
|
self.points.first().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the position of the last point in this subpath.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_position(&self) -> Option<Vector2F> {
|
pub fn last_position(&self) -> Option<Vector2F> {
|
||||||
self.points.last().cloned()
|
self.points.last().cloned()
|
||||||
|
@ -455,29 +512,39 @@ impl Contour {
|
||||||
self.points[self.points.len() - index as usize]
|
self.points[self.points.len() - index as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a set of flags that describes the type of the point with the given index.
|
||||||
|
///
|
||||||
|
/// Panics if the index is out of range.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn flags_of(&self, index: u32) -> PointFlags {
|
pub fn flags_of(&self, index: u32) -> PointFlags {
|
||||||
self.flags[index as usize]
|
self.flags[index as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new on-curve point at the given position to this contour.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push_endpoint(&mut self, point: Vector2F) {
|
pub fn push_endpoint(&mut self, to: Vector2F) {
|
||||||
self.push_point(point, PointFlags::empty(), true);
|
self.push_point(to, PointFlags::empty(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new quadratic Bézier curve to the given on-curve position and control point to this
|
||||||
|
/// contour.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push_quadratic(&mut self, ctrl: Vector2F, point: Vector2F) {
|
pub fn push_quadratic(&mut self, ctrl: Vector2F, to: Vector2F) {
|
||||||
self.push_point(ctrl, PointFlags::CONTROL_POINT_0, true);
|
self.push_point(ctrl, PointFlags::CONTROL_POINT_0, true);
|
||||||
self.push_point(point, PointFlags::empty(), true);
|
self.push_point(to, PointFlags::empty(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new cubic Bézier curve to the given on-curve position and control points to this
|
||||||
|
/// contour.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push_cubic(&mut self, ctrl0: Vector2F, ctrl1: Vector2F, point: Vector2F) {
|
pub fn push_cubic(&mut self, ctrl0: Vector2F, ctrl1: Vector2F, to: Vector2F) {
|
||||||
self.push_point(ctrl0, PointFlags::CONTROL_POINT_0, true);
|
self.push_point(ctrl0, PointFlags::CONTROL_POINT_0, true);
|
||||||
self.push_point(ctrl1, PointFlags::CONTROL_POINT_1, true);
|
self.push_point(ctrl1, PointFlags::CONTROL_POINT_1, true);
|
||||||
self.push_point(point, PointFlags::empty(), true);
|
self.push_point(to, PointFlags::empty(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marks this contour as closed, which results in an implicit line from the end back to the
|
||||||
|
/// starting point.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
self.closed = true;
|
self.closed = true;
|
||||||
|
@ -526,6 +593,19 @@ impl Contour {
|
||||||
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
|
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds Bézier curves approximating a possibly-transformed unit arc to this contour.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
///
|
||||||
|
/// * `transform`: An affine transform to apply to the unit arc. This can be used to reposition
|
||||||
|
/// and resize the arc.
|
||||||
|
///
|
||||||
|
/// * `start_angle`: The starting angle in radians. 0 represents the +x axis.
|
||||||
|
///
|
||||||
|
/// * `end_angle`: The ending angle in radians. 0 represents the +x axis.
|
||||||
|
///
|
||||||
|
/// * `direction`: Whether the arc should be drawn clockwise or counterclockwise from the +x
|
||||||
|
/// axis.
|
||||||
pub fn push_arc(&mut self,
|
pub fn push_arc(&mut self,
|
||||||
transform: &Transform2F,
|
transform: &Transform2F,
|
||||||
start_angle: f32,
|
start_angle: f32,
|
||||||
|
@ -540,6 +620,8 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given the endpoints of a unit arc, adds Bézier curves to approximate that arc to the
|
||||||
|
/// current contour. The given transform is applied to the resulting arc.
|
||||||
pub fn push_arc_from_unit_chord(&mut self,
|
pub fn push_arc_from_unit_chord(&mut self,
|
||||||
transform: &Transform2F,
|
transform: &Transform2F,
|
||||||
mut chord: LineSegment2F,
|
mut chord: LineSegment2F,
|
||||||
|
@ -590,12 +672,17 @@ impl Contour {
|
||||||
const EPSILON: f32 = 0.001;
|
const EPSILON: f32 = 0.001;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a SVG arc
|
/// Adds an arc specified in SVG form to the current contour.
|
||||||
///
|
///
|
||||||
/// Draws an ellipse section with radii given in `radius` rotated by `x_axis_rotation` to 'to' in the given `direction`.
|
/// Draws an ellipse section with radii given by `radius` rotated by `x_axis_rotation` in
|
||||||
/// If `large_arc` is true, draws an arc bigger than a 180°, otherwise smaller than 180°.
|
/// radians to `to` in the given direction. If `large_arc` is true, draws an arc bigger than
|
||||||
/// note that `x_axis_rotation` is in radians.
|
/// π radians, otherwise smaller than π radians.
|
||||||
pub fn push_svg_arc(&mut self, radius: Vector2F, x_axis_rotation: f32, large_arc: bool, direction: ArcDirection, to: Vector2F) {
|
pub fn push_svg_arc(&mut self,
|
||||||
|
radius: Vector2F,
|
||||||
|
x_axis_rotation: f32,
|
||||||
|
large_arc: bool,
|
||||||
|
direction: ArcDirection,
|
||||||
|
to: Vector2F) {
|
||||||
let r = radius;
|
let r = radius;
|
||||||
let p = to;
|
let p = to;
|
||||||
let last = self.last_position().unwrap_or_default();
|
let last = self.last_position().unwrap_or_default();
|
||||||
|
@ -648,6 +735,9 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds an unit circle to this contour, transformed with the given transform.
|
||||||
|
///
|
||||||
|
/// Non-uniform scales can be used to transform this circle into an ellipse.
|
||||||
pub fn push_ellipse(&mut self, transform: &Transform2F) {
|
pub fn push_ellipse(&mut self, transform: &Transform2F) {
|
||||||
let segment = Segment::quarter_circle_arc();
|
let segment = Segment::quarter_circle_arc();
|
||||||
let mut rotation;
|
let mut rotation;
|
||||||
|
@ -664,6 +754,11 @@ impl Contour {
|
||||||
PushSegmentFlags::UPDATE_BOUNDS);
|
PushSegmentFlags::UPDATE_BOUNDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the segment starting at the point with the given index.
|
||||||
|
///
|
||||||
|
/// The index must represent an on-curve point.
|
||||||
|
///
|
||||||
|
/// Panics if `point_index` is out of range.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn segment_after(&self, point_index: u32) -> Segment {
|
pub fn segment_after(&self, point_index: u32) -> Segment {
|
||||||
debug_assert!(self.point_is_endpoint(point_index));
|
debug_assert!(self.point_is_endpoint(point_index));
|
||||||
|
@ -694,6 +789,10 @@ impl Contour {
|
||||||
segment
|
segment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a line segment from the point with the given index to the next point, whether
|
||||||
|
/// on-curve or off-curve.
|
||||||
|
///
|
||||||
|
/// Panics if `prev_point_index` is not in range.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegment2F {
|
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegment2F {
|
||||||
let next_point_index = self.next_point_index_of(prev_point_index);
|
let next_point_index = self.next_point_index_of(prev_point_index);
|
||||||
|
@ -703,12 +802,14 @@ impl Contour {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the given point is on-curve or false if it is off-curve.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn point_is_endpoint(&self, point_index: u32) -> bool {
|
pub fn point_is_endpoint(&self, point_index: u32) -> bool {
|
||||||
!self.flags[point_index as usize]
|
!self.flags[point_index as usize]
|
||||||
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
|
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `point_index + addend` modulo the number of points in this contour.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_to_point_index(&self, point_index: u32, addend: u32) -> u32 {
|
pub fn add_to_point_index(&self, point_index: u32, addend: u32) -> u32 {
|
||||||
let (index, limit) = (point_index + addend, self.len());
|
let (index, limit) = (point_index + addend, self.len());
|
||||||
|
@ -719,12 +820,10 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Returns the first on-curve point strictly before the point with the given index.
|
||||||
pub fn point_is_logically_above(&self, a: u32, b: u32) -> bool {
|
///
|
||||||
let (a_y, b_y) = (self.points[a as usize].y(), self.points[b as usize].y());
|
/// This takes closed paths into account, so the returned index might be greater than
|
||||||
a_y < b_y || (a_y == b_y && a < b)
|
/// `point_index`.
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_endpoint_index_of(&self, mut point_index: u32) -> u32 {
|
pub fn prev_endpoint_index_of(&self, mut point_index: u32) -> u32 {
|
||||||
loop {
|
loop {
|
||||||
|
@ -735,6 +834,10 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the first on-curve point strictly after the point with the given index.
|
||||||
|
///
|
||||||
|
/// This takes closed paths into account, so the returned index might be less than
|
||||||
|
/// `point_index`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_endpoint_index_of(&self, mut point_index: u32) -> u32 {
|
pub fn next_endpoint_index_of(&self, mut point_index: u32) -> u32 {
|
||||||
loop {
|
loop {
|
||||||
|
@ -745,6 +848,10 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the index of the point before the given `point_index`.
|
||||||
|
///
|
||||||
|
/// If the index of the first point is passed in, then this returns the index of the last
|
||||||
|
/// point.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_point_index_of(&self, point_index: u32) -> u32 {
|
pub fn prev_point_index_of(&self, point_index: u32) -> u32 {
|
||||||
if point_index == 0 {
|
if point_index == 0 {
|
||||||
|
@ -754,6 +861,10 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the index of the point after the given `point_index`.
|
||||||
|
///
|
||||||
|
/// If the index of the last point is passed in, then this returns the index of the first
|
||||||
|
/// point.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_point_index_of(&self, point_index: u32) -> u32 {
|
pub fn next_point_index_of(&self, point_index: u32) -> u32 {
|
||||||
if point_index == self.len() - 1 {
|
if point_index == self.len() - 1 {
|
||||||
|
@ -763,6 +874,7 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies the given affine transform to this subpath.
|
||||||
pub fn transform(&mut self, transform: &Transform2F) {
|
pub fn transform(&mut self, transform: &Transform2F) {
|
||||||
if transform.is_identity() {
|
if transform.is_identity() {
|
||||||
return;
|
return;
|
||||||
|
@ -774,12 +886,16 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies the given affine transform to this contour, returning a new contour instead of
|
||||||
|
/// mutating this one.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transformed(mut self, transform: &Transform2F) -> Contour {
|
pub fn transformed(mut self, transform: &Transform2F) -> Contour {
|
||||||
self.transform(transform);
|
self.transform(transform);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies a perspective transform to this subpath.
|
||||||
|
#[deprecated]
|
||||||
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
||||||
for (point_index, point) in self.points.iter_mut().enumerate() {
|
for (point_index, point) in self.points.iter_mut().enumerate() {
|
||||||
*point = *perspective * *point;
|
*point = *perspective * *point;
|
||||||
|
@ -787,6 +903,8 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Thickens the outline by the given amount. The `orientation` parameter specifies the winding
|
||||||
|
/// of the path (clockwise or counterclockwise) and is necessary to avoid flipped normals.
|
||||||
pub fn dilate(&mut self, amount: Vector2F, orientation: Orientation) {
|
pub fn dilate(&mut self, amount: Vector2F, orientation: Orientation) {
|
||||||
ContourDilator::new(self, amount, orientation).dilate();
|
ContourDilator::new(self, amount, orientation).dilate();
|
||||||
self.bounds = self.bounds.dilate(amount);
|
self.bounds = self.bounds.dilate(amount);
|
||||||
|
@ -858,10 +976,14 @@ impl Debug for Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The index of a point within an outline, either on-curve or off-curve.
|
||||||
|
///
|
||||||
|
/// This packs a contour index with a point index into a single 32-bit value.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct PointIndex(u32);
|
pub struct PointIndex(u32);
|
||||||
|
|
||||||
impl PointIndex {
|
impl PointIndex {
|
||||||
|
/// Packs a contour index and the index of a point within that contour into a single value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(contour: u32, point: u32) -> PointIndex {
|
pub fn new(contour: u32, point: u32) -> PointIndex {
|
||||||
debug_assert!(contour <= 0xfff);
|
debug_assert!(contour <= 0xfff);
|
||||||
|
@ -869,17 +991,20 @@ impl PointIndex {
|
||||||
PointIndex((contour << 20) | point)
|
PointIndex((contour << 20) | point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts the index of the contour and returns it.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contour(self) -> u32 {
|
pub fn contour(self) -> u32 {
|
||||||
self.0 >> 20
|
self.0 >> 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts the index of the point within that contour and returns it.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn point(self) -> u32 {
|
pub fn point(self) -> u32 {
|
||||||
self.0 & 0x000f_ffff
|
self.0 & 0x000f_ffff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterates over all Bézier segments within a contour.
|
||||||
pub struct ContourIter<'a> {
|
pub struct ContourIter<'a> {
|
||||||
contour: &'a Contour,
|
contour: &'a Contour,
|
||||||
index: u32,
|
index: u32,
|
||||||
|
@ -933,14 +1058,20 @@ impl<'a> Iterator for ContourIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The direction of an arc: clockwise or counterclockwise.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum ArcDirection {
|
pub enum ArcDirection {
|
||||||
|
/// Clockwise, starting from the +x axis.
|
||||||
CW,
|
CW,
|
||||||
|
/// Counterclockwise, starting from the +x axis.
|
||||||
CCW,
|
CCW,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// Flags that control the behavior of `Contour::iter()`.
|
||||||
pub struct ContourIterFlags: u8 {
|
pub struct ContourIterFlags: u8 {
|
||||||
|
/// Set to true to avoid iterating over the implicit line segment that joins the last point
|
||||||
|
/// to the first point for closed contours.
|
||||||
const IGNORE_CLOSE_SEGMENT = 1;
|
const IGNORE_CLOSE_SEGMENT = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,7 @@ impl Scene {
|
||||||
self.epoch.next();
|
self.epoch.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
pub(crate) fn apply_render_options(&self,
|
pub(crate) fn apply_render_options(&self,
|
||||||
original_outline: &Outline,
|
original_outline: &Outline,
|
||||||
options: &PreparedBuildOptions)
|
options: &PreparedBuildOptions)
|
||||||
|
|
Loading…
Reference in New Issue