2019-06-21 13:06:19 -04:00
|
|
|
// pathfinder/content/src/outline.rs
|
2019-01-14 13:59:48 -05:00
|
|
|
//
|
|
|
|
// Copyright © 2019 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.
|
|
|
|
|
|
|
|
//! A compressed in-memory representation of paths.
|
|
|
|
|
2020-05-15 22:13:18 -04:00
|
|
|
use crate::clip::{self, ContourPolygonClipper};
|
2019-02-05 23:10:20 -05:00
|
|
|
use crate::dilation::ContourDilator;
|
2019-02-06 16:12:53 -05:00
|
|
|
use crate::orientation::Orientation;
|
2019-01-14 13:59:48 -05:00
|
|
|
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
2019-06-21 13:06:19 -04:00
|
|
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
|
|
|
use pathfinder_geometry::rect::RectF;
|
2019-07-11 17:35:06 -04:00
|
|
|
use pathfinder_geometry::transform2d::Transform2F;
|
2019-06-21 13:06:19 -04:00
|
|
|
use pathfinder_geometry::transform3d::Perspective;
|
|
|
|
use pathfinder_geometry::unit_vector::UnitVector;
|
2020-03-31 14:28:34 -04:00
|
|
|
use pathfinder_geometry::vector::{Vector2F, vec2f};
|
2019-05-29 15:12:12 -04:00
|
|
|
use std::f32::consts::PI;
|
2019-01-14 13:59:48 -05:00
|
|
|
use std::fmt::{self, Debug, Formatter};
|
|
|
|
use std::mem;
|
|
|
|
|
2019-01-29 17:50:15 -05:00
|
|
|
#[derive(Clone)]
|
2019-01-14 13:59:48 -05:00
|
|
|
pub struct Outline {
|
2019-05-03 15:35:19 -04:00
|
|
|
pub(crate) contours: Vec<Contour>,
|
2019-05-29 22:13:42 -04:00
|
|
|
pub(crate) bounds: RectF,
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
2019-01-15 14:42:25 -05:00
|
|
|
#[derive(Clone)]
|
2019-01-14 13:59:48 -05:00
|
|
|
pub struct Contour {
|
2019-06-03 15:39:29 -04:00
|
|
|
pub(crate) points: Vec<Vector2F>,
|
2019-01-22 14:46:01 -05:00
|
|
|
pub(crate) flags: Vec<PointFlags>,
|
2019-05-29 22:13:42 -04:00
|
|
|
pub(crate) bounds: RectF,
|
2019-02-20 19:32:40 -05:00
|
|
|
pub(crate) closed: bool,
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bitflags! {
|
|
|
|
pub struct PointFlags: u8 {
|
|
|
|
const CONTROL_POINT_0 = 0x01;
|
|
|
|
const CONTROL_POINT_1 = 0x02;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-01 15:02:53 -04:00
|
|
|
bitflags! {
|
|
|
|
pub struct PushSegmentFlags: u8 {
|
|
|
|
const UPDATE_BOUNDS = 0x01;
|
|
|
|
const INCLUDE_FROM_POINT = 0x02;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
impl Outline {
|
|
|
|
#[inline]
|
|
|
|
pub fn new() -> Outline {
|
2019-04-29 19:46:35 -04:00
|
|
|
Outline {
|
|
|
|
contours: vec![],
|
2019-05-29 22:13:42 -04:00
|
|
|
bounds: RectF::default(),
|
2019-04-29 19:46:35 -04:00
|
|
|
}
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn from_segments<I>(segments: I) -> Outline
|
|
|
|
where
|
|
|
|
I: Iterator<Item = Segment>,
|
|
|
|
{
|
|
|
|
let mut outline = Outline::new();
|
|
|
|
let mut current_contour = Contour::new();
|
|
|
|
|
|
|
|
for segment in segments {
|
|
|
|
if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) {
|
|
|
|
if !current_contour.is_empty() {
|
|
|
|
outline
|
|
|
|
.contours
|
|
|
|
.push(mem::replace(&mut current_contour, Contour::new()));
|
|
|
|
}
|
2019-02-04 19:04:13 -05:00
|
|
|
current_contour.push_point(segment.baseline.from(), PointFlags::empty(), true);
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
|
|
|
|
if !current_contour.is_empty() {
|
2019-02-20 19:32:40 -05:00
|
|
|
current_contour.close();
|
2019-02-01 20:57:26 -05:00
|
|
|
let contour = mem::replace(&mut current_contour, Contour::new());
|
2019-05-03 15:35:19 -04:00
|
|
|
outline.push_contour(contour);
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if segment.is_none() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !segment.is_line() {
|
2019-02-04 19:04:13 -05:00
|
|
|
current_contour.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0, true);
|
2019-01-14 13:59:48 -05:00
|
|
|
if !segment.is_quadratic() {
|
2019-04-29 19:46:35 -04:00
|
|
|
current_contour.push_point(
|
|
|
|
segment.ctrl.to(),
|
|
|
|
PointFlags::CONTROL_POINT_1,
|
|
|
|
true,
|
|
|
|
);
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-04 19:04:13 -05:00
|
|
|
current_contour.push_point(segment.baseline.to(), PointFlags::empty(), true);
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
2019-05-03 15:35:19 -04:00
|
|
|
outline.push_contour(current_contour);
|
2019-01-14 13:59:48 -05:00
|
|
|
outline
|
|
|
|
}
|
|
|
|
|
2020-04-03 01:03:42 -04:00
|
|
|
#[inline]
|
|
|
|
pub fn from_rect(rect: RectF) -> Outline {
|
|
|
|
let mut outline = Outline::new();
|
|
|
|
outline.push_contour(Contour::from_rect(rect));
|
|
|
|
outline
|
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
#[inline]
|
2019-05-29 22:13:42 -04:00
|
|
|
pub fn bounds(&self) -> RectF {
|
2019-02-05 13:03:20 -05:00
|
|
|
self.bounds
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
2019-01-15 14:42:25 -05:00
|
|
|
|
2019-05-03 15:35:19 -04:00
|
|
|
#[inline]
|
|
|
|
pub fn contours(&self) -> &[Contour] {
|
|
|
|
&self.contours
|
|
|
|
}
|
|
|
|
|
2020-03-27 15:33:06 -04:00
|
|
|
#[inline]
|
|
|
|
pub fn into_contours(self) -> Vec<Contour> {
|
|
|
|
self.contours
|
|
|
|
}
|
|
|
|
|
2020-03-26 11:34:47 -04:00
|
|
|
/// Removes all contours from this outline.
|
|
|
|
#[inline]
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.contours.clear();
|
|
|
|
self.bounds = RectF::default();
|
|
|
|
}
|
|
|
|
|
2019-05-03 15:35:19 -04:00
|
|
|
pub fn push_contour(&mut self, contour: Contour) {
|
|
|
|
if contour.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.contours.is_empty() {
|
|
|
|
self.bounds = contour.bounds;
|
|
|
|
} else {
|
|
|
|
self.bounds = self.bounds.union_rect(contour.bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.contours.push(contour);
|
|
|
|
}
|
|
|
|
|
2020-03-27 15:33:06 -04:00
|
|
|
pub fn pop_contour(&mut self) -> Option<Contour> {
|
|
|
|
let last_contour = self.contours.pop();
|
|
|
|
|
|
|
|
let mut new_bounds = None;
|
|
|
|
for contour in &mut self.contours {
|
|
|
|
contour.update_bounds(&mut new_bounds);
|
|
|
|
}
|
|
|
|
self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
|
|
|
|
|
|
|
|
last_contour
|
|
|
|
}
|
|
|
|
|
2019-07-11 17:35:06 -04:00
|
|
|
pub fn transform(&mut self, transform: &Transform2F) {
|
2019-05-13 21:40:25 -04:00
|
|
|
if transform.is_identity() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-01 20:57:26 -05:00
|
|
|
let mut new_bounds = None;
|
|
|
|
for contour in &mut self.contours {
|
|
|
|
contour.transform(transform);
|
|
|
|
contour.update_bounds(&mut new_bounds);
|
|
|
|
}
|
2019-05-29 22:13:42 -04:00
|
|
|
self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
|
2019-01-15 16:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-01-16 19:53:10 -05:00
|
|
|
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
2019-02-01 20:57:26 -05:00
|
|
|
let mut new_bounds = None;
|
|
|
|
for contour in &mut self.contours {
|
|
|
|
contour.apply_perspective(perspective);
|
|
|
|
contour.update_bounds(&mut new_bounds);
|
2019-01-15 16:49:26 -05:00
|
|
|
}
|
2019-05-29 22:13:42 -04:00
|
|
|
self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
|
2019-01-15 16:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn dilate(&mut self, amount: Vector2F) {
|
2019-02-06 16:12:53 -05:00
|
|
|
let orientation = Orientation::from_outline(self);
|
2019-04-29 19:46:35 -04:00
|
|
|
self.contours
|
|
|
|
.iter_mut()
|
|
|
|
.for_each(|contour| contour.dilate(amount, orientation));
|
2019-02-05 23:10:20 -05:00
|
|
|
self.bounds = self.bounds.dilate(amount);
|
|
|
|
}
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn is_outside_polygon(&self, clip_polygon: &[Vector2F]) -> bool {
|
2019-02-09 22:24:30 -05:00
|
|
|
clip::rect_is_outside_polygon(self.bounds, clip_polygon)
|
|
|
|
}
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
fn is_inside_polygon(&self, clip_polygon: &[Vector2F]) -> bool {
|
2019-02-09 22:24:30 -05:00
|
|
|
clip::rect_is_inside_polygon(self.bounds, clip_polygon)
|
|
|
|
}
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn clip_against_polygon(&mut self, clip_polygon: &[Vector2F]) {
|
2019-02-09 22:24:30 -05:00
|
|
|
// Quick check.
|
|
|
|
if self.is_inside_polygon(clip_polygon) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-25 20:07:37 -05:00
|
|
|
for contour in mem::replace(&mut self.contours, vec![]) {
|
2019-05-03 15:35:19 -04:00
|
|
|
self.push_contour(ContourPolygonClipper::new(clip_polygon, contour).clip());
|
2019-01-25 20:07:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 17:50:02 -04:00
|
|
|
#[inline]
|
|
|
|
pub fn close_all_contours(&mut self) {
|
|
|
|
self.contours.iter_mut().for_each(|contour| contour.close());
|
|
|
|
}
|
2020-05-19 11:26:17 -04:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.contours.iter().all(Contour::is_empty)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Appends the contours in another outline to this one.
|
|
|
|
pub fn push_outline(&mut self, other: Outline) {
|
|
|
|
if other.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.is_empty() {
|
|
|
|
self.bounds = other.bounds;
|
|
|
|
} else {
|
|
|
|
self.bounds = self.bounds.union_rect(other.bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.contours.extend(other.contours);
|
|
|
|
}
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
2019-01-29 17:50:15 -05:00
|
|
|
impl Debug for Outline {
|
|
|
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
|
|
|
for (contour_index, contour) in self.contours.iter().enumerate() {
|
|
|
|
if contour_index > 0 {
|
|
|
|
write!(formatter, " ")?;
|
|
|
|
}
|
|
|
|
contour.fmt(formatter)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
impl Contour {
|
|
|
|
#[inline]
|
|
|
|
pub fn new() -> Contour {
|
2019-04-29 19:46:35 -04:00
|
|
|
Contour {
|
|
|
|
points: vec![],
|
|
|
|
flags: vec![],
|
2019-05-29 22:13:42 -04:00
|
|
|
bounds: RectF::default(),
|
2019-04-29 19:46:35 -04:00
|
|
|
closed: false,
|
|
|
|
}
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
2020-03-27 13:06:56 -04:00
|
|
|
#[inline]
|
|
|
|
pub fn with_capacity(length: usize) -> Contour {
|
|
|
|
Contour {
|
|
|
|
points: Vec::with_capacity(length),
|
|
|
|
flags: Vec::with_capacity(length),
|
|
|
|
bounds: RectF::default(),
|
|
|
|
closed: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 01:03:42 -04:00
|
|
|
#[inline]
|
|
|
|
pub fn from_rect(rect: RectF) -> Contour {
|
|
|
|
let mut contour = Contour::new();
|
|
|
|
contour.push_point(rect.origin(), PointFlags::empty(), false);
|
|
|
|
contour.push_point(rect.upper_right(), PointFlags::empty(), false);
|
|
|
|
contour.push_point(rect.lower_right(), PointFlags::empty(), false);
|
|
|
|
contour.push_point(rect.lower_left(), PointFlags::empty(), false);
|
|
|
|
contour.close();
|
|
|
|
contour.bounds = rect;
|
|
|
|
contour
|
|
|
|
}
|
|
|
|
|
2019-02-01 22:10:42 -05:00
|
|
|
// Replaces this contour with a new one, with arrays preallocated to match `self`.
|
|
|
|
#[inline]
|
|
|
|
pub(crate) fn take(&mut self) -> Contour {
|
|
|
|
let length = self.len() as usize;
|
2019-04-29 19:46:35 -04:00
|
|
|
mem::replace(
|
|
|
|
self,
|
|
|
|
Contour {
|
|
|
|
points: Vec::with_capacity(length),
|
|
|
|
flags: Vec::with_capacity(length),
|
2019-05-29 22:13:42 -04:00
|
|
|
bounds: RectF::default(),
|
2019-04-29 19:46:35 -04:00
|
|
|
closed: false,
|
|
|
|
},
|
|
|
|
)
|
2019-02-01 22:10:42 -05:00
|
|
|
}
|
|
|
|
|
2020-03-28 12:22:39 -04:00
|
|
|
/// restore self to the state of Contour::new(), but keep the points buffer allocated
|
|
|
|
#[inline]
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.points.clear();
|
|
|
|
self.flags.clear();
|
|
|
|
self.bounds = RectF::default();
|
|
|
|
self.closed = false;
|
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
#[inline]
|
2020-03-27 18:40:27 -04:00
|
|
|
pub fn iter(&self, flags: ContourIterFlags) -> ContourIter {
|
2019-04-29 19:46:35 -04:00
|
|
|
ContourIter {
|
|
|
|
contour: self,
|
|
|
|
index: 1,
|
2020-03-27 18:40:27 -04:00
|
|
|
flags,
|
2019-04-29 19:46:35 -04:00
|
|
|
}
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.points.is_empty()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> u32 {
|
|
|
|
self.points.len() as u32
|
|
|
|
}
|
|
|
|
|
2019-01-15 16:49:26 -05:00
|
|
|
#[inline]
|
2019-05-29 22:13:42 -04:00
|
|
|
pub fn bounds(&self) -> RectF {
|
2019-02-05 13:03:20 -05:00
|
|
|
self.bounds
|
2019-01-15 16:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-02-20 19:32:40 -05:00
|
|
|
#[inline]
|
|
|
|
pub fn is_closed(&self) -> bool {
|
|
|
|
self.closed
|
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn position_of(&self, index: u32) -> Vector2F {
|
2019-01-14 13:59:48 -05:00
|
|
|
self.points[index as usize]
|
|
|
|
}
|
|
|
|
|
2019-05-16 13:43:43 -04:00
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn last_position(&self) -> Option<Vector2F> {
|
2019-05-31 19:01:53 -04:00
|
|
|
self.points.last().cloned()
|
2019-05-16 13:43:43 -04:00
|
|
|
}
|
|
|
|
|
2019-01-22 14:46:01 -05:00
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub(crate) fn position_of_last(&self, index: u32) -> Vector2F {
|
2019-05-31 19:01:53 -04:00
|
|
|
self.points[self.points.len() - index as usize]
|
2019-01-22 14:46:01 -05:00
|
|
|
}
|
|
|
|
|
2020-06-23 16:01:50 -04:00
|
|
|
#[inline]
|
|
|
|
pub fn flags_of(&self, index: u32) -> PointFlags {
|
|
|
|
self.flags[index as usize]
|
|
|
|
}
|
|
|
|
|
2019-02-20 19:32:40 -05:00
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn push_endpoint(&mut self, point: Vector2F) {
|
2019-05-03 15:35:19 -04:00
|
|
|
self.push_point(point, PointFlags::empty(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn push_quadratic(&mut self, ctrl: Vector2F, point: Vector2F) {
|
2019-05-03 15:35:19 -04:00
|
|
|
self.push_point(ctrl, PointFlags::CONTROL_POINT_0, true);
|
|
|
|
self.push_point(point, PointFlags::empty(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn push_cubic(&mut self, ctrl0: Vector2F, ctrl1: Vector2F, point: Vector2F) {
|
2019-05-03 15:35:19 -04:00
|
|
|
self.push_point(ctrl0, PointFlags::CONTROL_POINT_0, true);
|
|
|
|
self.push_point(ctrl1, PointFlags::CONTROL_POINT_1, true);
|
|
|
|
self.push_point(point, PointFlags::empty(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn close(&mut self) {
|
2019-02-20 19:32:40 -05:00
|
|
|
self.closed = true;
|
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
#[inline]
|
2019-05-13 15:17:49 -04:00
|
|
|
pub(crate) fn push_point(&mut self,
|
2019-06-03 15:39:29 -04:00
|
|
|
point: Vector2F,
|
2019-05-13 15:17:49 -04:00
|
|
|
flags: PointFlags,
|
|
|
|
update_bounds: bool) {
|
2019-02-20 19:21:58 -05:00
|
|
|
debug_assert!(!point.x().is_nan() && !point.y().is_nan());
|
|
|
|
|
2019-02-04 19:04:13 -05:00
|
|
|
if update_bounds {
|
|
|
|
let first = self.is_empty();
|
|
|
|
union_rect(&mut self.bounds, point, first);
|
|
|
|
}
|
2019-01-15 16:49:26 -05:00
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
self.points.push(point);
|
|
|
|
self.flags.push(flags);
|
|
|
|
}
|
|
|
|
|
2019-02-05 16:21:33 -05:00
|
|
|
#[inline]
|
2019-06-01 15:02:53 -04:00
|
|
|
pub(crate) fn push_segment(&mut self, segment: &Segment, flags: PushSegmentFlags) {
|
2019-01-15 17:21:37 -05:00
|
|
|
if segment.is_none() {
|
2019-04-29 19:46:35 -04:00
|
|
|
return;
|
2019-01-15 17:21:37 -05:00
|
|
|
}
|
|
|
|
|
2019-06-01 15:02:53 -04:00
|
|
|
let update_bounds = flags.contains(PushSegmentFlags::UPDATE_BOUNDS);
|
2019-02-20 19:21:58 -05:00
|
|
|
self.push_point(segment.baseline.from(), PointFlags::empty(), update_bounds);
|
|
|
|
|
|
|
|
if !segment.is_line() {
|
2019-04-29 19:46:35 -04:00
|
|
|
self.push_point(
|
|
|
|
segment.ctrl.from(),
|
|
|
|
PointFlags::CONTROL_POINT_0,
|
|
|
|
update_bounds,
|
|
|
|
);
|
2019-02-20 19:21:58 -05:00
|
|
|
if !segment.is_quadratic() {
|
2019-04-29 19:46:35 -04:00
|
|
|
self.push_point(
|
|
|
|
segment.ctrl.to(),
|
|
|
|
PointFlags::CONTROL_POINT_1,
|
|
|
|
update_bounds,
|
|
|
|
);
|
2019-02-20 19:21:58 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
|
|
|
|
}
|
|
|
|
|
2019-06-01 15:11:47 -04:00
|
|
|
pub fn push_arc(&mut self,
|
2019-07-11 17:35:06 -04:00
|
|
|
transform: &Transform2F,
|
2019-06-01 15:11:47 -04:00
|
|
|
start_angle: f32,
|
|
|
|
end_angle: f32,
|
|
|
|
direction: ArcDirection) {
|
2019-05-30 13:56:19 -04:00
|
|
|
if end_angle - start_angle >= PI * 2.0 {
|
2019-05-30 20:19:21 -04:00
|
|
|
self.push_ellipse(transform);
|
|
|
|
} else {
|
2020-03-31 14:28:34 -04:00
|
|
|
let start = vec2f(start_angle.cos(), start_angle.sin());
|
|
|
|
let end = vec2f(end_angle.cos(), end_angle.sin());
|
2019-06-03 15:39:29 -04:00
|
|
|
self.push_arc_from_unit_chord(transform, LineSegment2F::new(start, end), direction);
|
2019-05-30 13:56:19 -04:00
|
|
|
}
|
2019-05-29 15:12:12 -04:00
|
|
|
}
|
|
|
|
|
2019-06-01 15:11:47 -04:00
|
|
|
pub fn push_arc_from_unit_chord(&mut self,
|
2019-07-11 17:35:06 -04:00
|
|
|
transform: &Transform2F,
|
2019-06-03 15:39:29 -04:00
|
|
|
mut chord: LineSegment2F,
|
2019-06-01 15:11:47 -04:00
|
|
|
direction: ArcDirection) {
|
2019-07-11 17:35:06 -04:00
|
|
|
let mut direction_transform = Transform2F::default();
|
2019-06-01 15:11:47 -04:00
|
|
|
if direction == ArcDirection::CCW {
|
2020-04-01 20:20:32 -04:00
|
|
|
chord *= vec2f(1.0, -1.0);
|
2020-03-31 14:28:34 -04:00
|
|
|
direction_transform = Transform2F::from_scale(vec2f(1.0, -1.0));
|
2019-06-01 15:11:47 -04:00
|
|
|
}
|
|
|
|
|
2019-05-29 15:12:12 -04:00
|
|
|
let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to()));
|
2020-03-31 16:27:47 -04:00
|
|
|
for segment_index in 0..4 {
|
|
|
|
debug!("push_arc_from_unit_chord(): loop segment index {}", segment_index);
|
2019-05-30 20:19:21 -04:00
|
|
|
|
2019-05-29 15:12:12 -04:00
|
|
|
let mut sweep_vector = end_vector.rev_rotate_by(vector);
|
2019-05-30 13:56:19 -04:00
|
|
|
let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON;
|
2020-03-31 16:27:47 -04:00
|
|
|
debug!("... end_vector={:?} vector={:?} sweep_vector={:?} last={:?}",
|
|
|
|
end_vector,
|
|
|
|
vector,
|
|
|
|
sweep_vector,
|
|
|
|
last);
|
2019-05-31 00:20:32 -04:00
|
|
|
|
|
|
|
let mut segment;
|
2019-05-29 15:12:12 -04:00
|
|
|
if !last {
|
2020-03-31 14:28:34 -04:00
|
|
|
sweep_vector = UnitVector(vec2f(0.0, 1.0));
|
2019-05-31 00:20:32 -04:00
|
|
|
segment = Segment::quarter_circle_arc();
|
|
|
|
} else {
|
|
|
|
segment = Segment::arc_from_cos(sweep_vector.0.x());
|
2019-05-29 15:12:12 -04:00
|
|
|
}
|
2019-05-13 15:17:49 -04:00
|
|
|
|
2019-06-01 15:11:47 -04:00
|
|
|
let half_sweep_vector = sweep_vector.halve_angle();
|
2019-07-11 17:35:06 -04:00
|
|
|
let rotation = Transform2F::from_rotation_vector(half_sweep_vector.rotate_by(vector));
|
2020-03-31 20:59:41 -04:00
|
|
|
segment = segment.transform(&(*transform * direction_transform * rotation));
|
2019-05-13 15:17:49 -04:00
|
|
|
|
2019-06-01 15:02:53 -04:00
|
|
|
let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS;
|
2020-03-31 16:27:47 -04:00
|
|
|
if segment_index == 0 {
|
2019-06-01 15:02:53 -04:00
|
|
|
push_segment_flags.insert(PushSegmentFlags::INCLUDE_FROM_POINT);
|
2019-05-13 15:17:49 -04:00
|
|
|
}
|
2019-06-01 15:02:53 -04:00
|
|
|
self.push_segment(&segment, push_segment_flags);
|
2019-05-13 15:17:49 -04:00
|
|
|
|
2019-05-29 15:12:12 -04:00
|
|
|
if last {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-05-31 19:01:53 -04:00
|
|
|
vector = vector.rotate_by(sweep_vector);
|
2019-05-13 15:17:49 -04:00
|
|
|
}
|
2019-05-29 15:12:12 -04:00
|
|
|
|
|
|
|
const EPSILON: f32 = 0.001;
|
2019-05-13 15:17:49 -04:00
|
|
|
}
|
|
|
|
|
2019-07-11 17:35:06 -04:00
|
|
|
pub fn push_ellipse(&mut self, transform: &Transform2F) {
|
2019-05-31 00:20:32 -04:00
|
|
|
let segment = Segment::quarter_circle_arc();
|
|
|
|
let mut rotation;
|
2019-06-01 15:02:53 -04:00
|
|
|
self.push_segment(&segment.transform(transform),
|
|
|
|
PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT);
|
2020-03-31 14:28:34 -04:00
|
|
|
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, 1.0)));
|
2019-07-12 14:26:09 -04:00
|
|
|
self.push_segment(&segment.transform(&(*transform * rotation)),
|
2019-06-01 15:02:53 -04:00
|
|
|
PushSegmentFlags::UPDATE_BOUNDS);
|
2020-03-31 14:28:34 -04:00
|
|
|
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(-1.0, 0.0)));
|
2019-07-12 14:26:09 -04:00
|
|
|
self.push_segment(&segment.transform(&(*transform * rotation)),
|
2019-06-01 15:02:53 -04:00
|
|
|
PushSegmentFlags::UPDATE_BOUNDS);
|
2020-03-31 14:28:34 -04:00
|
|
|
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, -1.0)));
|
2019-07-12 14:26:09 -04:00
|
|
|
self.push_segment(&segment.transform(&(*transform * rotation)),
|
2019-06-01 15:02:53 -04:00
|
|
|
PushSegmentFlags::UPDATE_BOUNDS);
|
2019-05-30 13:56:19 -04:00
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
#[inline]
|
|
|
|
pub fn segment_after(&self, point_index: u32) -> Segment {
|
|
|
|
debug_assert!(self.point_is_endpoint(point_index));
|
|
|
|
|
|
|
|
let mut segment = Segment::none();
|
2019-06-25 17:43:13 -04:00
|
|
|
segment.baseline.set_from(self.position_of(point_index));
|
2019-01-14 13:59:48 -05:00
|
|
|
|
|
|
|
let point1_index = self.add_to_point_index(point_index, 1);
|
|
|
|
if self.point_is_endpoint(point1_index) {
|
2019-06-25 17:43:13 -04:00
|
|
|
segment.baseline.set_to(self.position_of(point1_index));
|
2019-01-14 13:59:48 -05:00
|
|
|
segment.kind = SegmentKind::Line;
|
|
|
|
} else {
|
2019-06-25 17:43:13 -04:00
|
|
|
segment.ctrl.set_from(self.position_of(point1_index));
|
2019-01-14 13:59:48 -05:00
|
|
|
|
|
|
|
let point2_index = self.add_to_point_index(point_index, 2);
|
|
|
|
if self.point_is_endpoint(point2_index) {
|
2019-06-25 17:43:13 -04:00
|
|
|
segment.baseline.set_to(self.position_of(point2_index));
|
2019-01-14 13:59:48 -05:00
|
|
|
segment.kind = SegmentKind::Quadratic;
|
|
|
|
} else {
|
2019-06-25 17:43:13 -04:00
|
|
|
segment.ctrl.set_to(self.position_of(point2_index));
|
2019-01-14 13:59:48 -05:00
|
|
|
segment.kind = SegmentKind::Cubic;
|
|
|
|
|
|
|
|
let point3_index = self.add_to_point_index(point_index, 3);
|
2019-06-25 17:43:13 -04:00
|
|
|
segment.baseline.set_to(self.position_of(point3_index));
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
segment
|
|
|
|
}
|
|
|
|
|
2019-02-20 15:41:43 -05:00
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegment2F {
|
2019-02-20 15:41:43 -05:00
|
|
|
let next_point_index = self.next_point_index_of(prev_point_index);
|
2019-06-03 15:39:29 -04:00
|
|
|
LineSegment2F::new(
|
2019-05-13 15:17:49 -04:00
|
|
|
self.points[prev_point_index as usize],
|
|
|
|
self.points[next_point_index as usize],
|
2019-04-29 19:46:35 -04:00
|
|
|
)
|
2019-02-20 15:41:43 -05:00
|
|
|
}
|
|
|
|
|
2019-01-14 13:59:48 -05:00
|
|
|
#[inline]
|
|
|
|
pub fn point_is_endpoint(&self, point_index: u32) -> bool {
|
|
|
|
!self.flags[point_index as usize]
|
2019-04-29 19:46:35 -04:00
|
|
|
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn add_to_point_index(&self, point_index: u32, addend: u32) -> u32 {
|
|
|
|
let (index, limit) = (point_index + addend, self.len());
|
|
|
|
if index >= limit {
|
|
|
|
index - limit
|
|
|
|
} else {
|
|
|
|
index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
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());
|
|
|
|
a_y < b_y || (a_y == b_y && a < b)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn prev_endpoint_index_of(&self, mut point_index: u32) -> u32 {
|
|
|
|
loop {
|
|
|
|
point_index = self.prev_point_index_of(point_index);
|
|
|
|
if self.point_is_endpoint(point_index) {
|
|
|
|
return point_index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn next_endpoint_index_of(&self, mut point_index: u32) -> u32 {
|
|
|
|
loop {
|
|
|
|
point_index = self.next_point_index_of(point_index);
|
|
|
|
if self.point_is_endpoint(point_index) {
|
|
|
|
return point_index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn prev_point_index_of(&self, point_index: u32) -> u32 {
|
|
|
|
if point_index == 0 {
|
|
|
|
self.len() - 1
|
|
|
|
} else {
|
|
|
|
point_index - 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn next_point_index_of(&self, point_index: u32) -> u32 {
|
|
|
|
if point_index == self.len() - 1 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
point_index + 1
|
|
|
|
}
|
|
|
|
}
|
2019-01-15 14:42:25 -05:00
|
|
|
|
2019-07-11 17:35:06 -04:00
|
|
|
pub fn transform(&mut self, transform: &Transform2F) {
|
2019-05-13 21:40:25 -04:00
|
|
|
if transform.is_identity() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-15 16:49:26 -05:00
|
|
|
for (point_index, point) in self.points.iter_mut().enumerate() {
|
2019-07-12 14:55:01 -04:00
|
|
|
*point = *transform * *point;
|
2019-01-15 16:49:26 -05:00
|
|
|
union_rect(&mut self.bounds, *point, point_index == 0);
|
2019-01-15 14:42:25 -05:00
|
|
|
}
|
2019-01-15 17:21:37 -05:00
|
|
|
}
|
|
|
|
|
2019-01-16 19:53:10 -05:00
|
|
|
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
|
|
|
for (point_index, point) in self.points.iter_mut().enumerate() {
|
2019-07-12 14:55:01 -04:00
|
|
|
*point = *perspective * *point;
|
2019-01-16 19:53:10 -05:00
|
|
|
union_rect(&mut self.bounds, *point, point_index == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
pub fn dilate(&mut self, amount: Vector2F, orientation: Orientation) {
|
2019-02-06 16:12:53 -05:00
|
|
|
ContourDilator::new(self, amount, orientation).dilate();
|
2019-02-05 23:10:20 -05:00
|
|
|
self.bounds = self.bounds.dilate(amount);
|
|
|
|
}
|
|
|
|
|
2019-05-03 15:35:19 -04:00
|
|
|
// Use this function to keep bounds up to date when mutating paths. See `Outline::transform()`
|
|
|
|
// for an example of use.
|
2019-05-29 22:13:42 -04:00
|
|
|
pub(crate) fn update_bounds(&self, bounds: &mut Option<RectF>) {
|
2019-02-01 20:57:26 -05:00
|
|
|
*bounds = Some(match *bounds {
|
|
|
|
None => self.bounds,
|
2019-02-05 13:03:20 -05:00
|
|
|
Some(bounds) => bounds.union_rect(self.bounds),
|
2019-02-01 20:57:26 -05:00
|
|
|
})
|
|
|
|
}
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for Contour {
|
|
|
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
2020-03-27 18:40:27 -04:00
|
|
|
for (segment_index, segment) in self.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT)
|
|
|
|
.enumerate() {
|
2019-01-29 17:50:15 -05:00
|
|
|
if segment_index == 0 {
|
2019-04-29 19:46:35 -04:00
|
|
|
write!(
|
|
|
|
formatter,
|
|
|
|
"M {} {}",
|
|
|
|
segment.baseline.from_x(),
|
|
|
|
segment.baseline.from_y()
|
|
|
|
)?;
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
2019-01-29 17:50:15 -05:00
|
|
|
|
|
|
|
match segment.kind {
|
|
|
|
SegmentKind::None => {}
|
|
|
|
SegmentKind::Line => {
|
2019-04-29 19:46:35 -04:00
|
|
|
write!(
|
|
|
|
formatter,
|
|
|
|
" L {} {}",
|
|
|
|
segment.baseline.to_x(),
|
|
|
|
segment.baseline.to_y()
|
|
|
|
)?;
|
2019-01-29 17:50:15 -05:00
|
|
|
}
|
|
|
|
SegmentKind::Quadratic => {
|
2019-04-29 19:46:35 -04:00
|
|
|
write!(
|
|
|
|
formatter,
|
|
|
|
" Q {} {} {} {}",
|
|
|
|
segment.ctrl.from_x(),
|
|
|
|
segment.ctrl.from_y(),
|
|
|
|
segment.baseline.to_x(),
|
|
|
|
segment.baseline.to_y()
|
|
|
|
)?;
|
2019-01-29 17:50:15 -05:00
|
|
|
}
|
|
|
|
SegmentKind::Cubic => {
|
2019-04-29 19:46:35 -04:00
|
|
|
write!(
|
|
|
|
formatter,
|
|
|
|
" C {} {} {} {} {} {}",
|
|
|
|
segment.ctrl.from_x(),
|
|
|
|
segment.ctrl.from_y(),
|
|
|
|
segment.ctrl.to_x(),
|
|
|
|
segment.ctrl.to_y(),
|
|
|
|
segment.baseline.to_x(),
|
|
|
|
segment.baseline.to_y()
|
|
|
|
)?;
|
2019-01-29 17:50:15 -05:00
|
|
|
}
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-20 19:32:40 -05:00
|
|
|
if self.closed {
|
|
|
|
write!(formatter, " z")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub struct PointIndex(u32);
|
|
|
|
|
|
|
|
impl PointIndex {
|
|
|
|
#[inline]
|
|
|
|
pub fn new(contour: u32, point: u32) -> PointIndex {
|
|
|
|
debug_assert!(contour <= 0xfff);
|
|
|
|
debug_assert!(point <= 0x000f_ffff);
|
|
|
|
PointIndex((contour << 20) | point)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn contour(self) -> u32 {
|
|
|
|
self.0 >> 20
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn point(self) -> u32 {
|
|
|
|
self.0 & 0x000f_ffff
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ContourIter<'a> {
|
|
|
|
contour: &'a Contour,
|
|
|
|
index: u32,
|
2020-03-27 18:40:27 -04:00
|
|
|
flags: ContourIterFlags,
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for ContourIter<'a> {
|
2019-01-15 17:21:37 -05:00
|
|
|
type Item = Segment;
|
2019-01-14 13:59:48 -05:00
|
|
|
|
|
|
|
#[inline]
|
2019-01-15 17:21:37 -05:00
|
|
|
fn next(&mut self) -> Option<Segment> {
|
2019-01-14 13:59:48 -05:00
|
|
|
let contour = self.contour;
|
2020-03-27 18:40:27 -04:00
|
|
|
|
|
|
|
let include_close_segment = self.contour.closed &&
|
|
|
|
!self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT);
|
|
|
|
if (self.index == contour.len() && !include_close_segment) ||
|
|
|
|
self.index == contour.len() + 1 {
|
2019-01-14 13:59:48 -05:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2019-01-15 17:21:37 -05:00
|
|
|
let point0_index = self.index - 1;
|
2019-01-14 13:59:48 -05:00
|
|
|
let point0 = contour.position_of(point0_index);
|
2019-01-15 17:21:37 -05:00
|
|
|
if self.index == contour.len() {
|
|
|
|
let point1 = contour.position_of(0);
|
|
|
|
self.index += 1;
|
2019-06-25 17:43:13 -04:00
|
|
|
return Some(Segment::line(LineSegment2F::new(point0, point1)));
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let point1_index = self.index;
|
|
|
|
self.index += 1;
|
2019-01-15 17:21:37 -05:00
|
|
|
let point1 = contour.position_of(point1_index);
|
2019-01-14 13:59:48 -05:00
|
|
|
if contour.point_is_endpoint(point1_index) {
|
2019-06-25 17:43:13 -04:00
|
|
|
return Some(Segment::line(LineSegment2F::new(point0, point1)));
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let point2_index = self.index;
|
|
|
|
let point2 = contour.position_of(point2_index);
|
|
|
|
self.index += 1;
|
2019-01-15 17:21:37 -05:00
|
|
|
if contour.point_is_endpoint(point2_index) {
|
2019-06-25 17:43:13 -04:00
|
|
|
return Some(Segment::quadratic(LineSegment2F::new(point0, point2), point1));
|
2019-01-15 17:21:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let point3_index = self.index;
|
|
|
|
let point3 = contour.position_of(point3_index);
|
|
|
|
self.index += 1;
|
|
|
|
debug_assert!(contour.point_is_endpoint(point3_index));
|
2019-04-29 19:46:35 -04:00
|
|
|
return Some(Segment::cubic(
|
2019-06-25 17:43:13 -04:00
|
|
|
LineSegment2F::new(point0, point3),
|
|
|
|
LineSegment2F::new(point1, point2),
|
2019-04-29 19:46:35 -04:00
|
|
|
));
|
2019-01-14 13:59:48 -05:00
|
|
|
}
|
|
|
|
}
|
2019-01-15 16:49:26 -05:00
|
|
|
|
2019-06-01 15:11:47 -04:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
pub enum ArcDirection {
|
|
|
|
CW,
|
|
|
|
CCW,
|
|
|
|
}
|
|
|
|
|
2020-03-27 18:40:27 -04:00
|
|
|
bitflags! {
|
|
|
|
pub struct ContourIterFlags: u8 {
|
|
|
|
const IGNORE_CLOSE_SEGMENT = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 16:49:26 -05:00
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub(crate) fn union_rect(bounds: &mut RectF, new_point: Vector2F, first: bool) {
|
2019-01-15 16:49:26 -05:00
|
|
|
if first {
|
2019-05-29 22:13:42 -04:00
|
|
|
*bounds = RectF::from_points(new_point, new_point);
|
2019-02-05 13:03:20 -05:00
|
|
|
} else {
|
|
|
|
*bounds = bounds.union_point(new_point)
|
2019-01-15 16:49:26 -05:00
|
|
|
}
|
|
|
|
}
|