Remove all Lyon dependencies.
We may want to re-merge at a later date, but for now all relevant algorithms have been appropriately SIMD-ified.
This commit is contained in:
parent
23943c7428
commit
3a8478a048
|
@ -535,8 +535,6 @@ dependencies = [
|
||||||
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lyon_geom 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"pathfinder_simd 0.3.0",
|
"pathfinder_simd 0.3.0",
|
||||||
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1024,8 +1022,6 @@ dependencies = [
|
||||||
"fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lyon_geom 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"pathfinder_geometry 0.3.0",
|
"pathfinder_geometry 0.3.0",
|
||||||
"pathfinder_renderer 0.1.0",
|
"pathfinder_renderer 0.1.0",
|
||||||
"pathfinder_svg 0.1.0",
|
"pathfinder_svg 0.1.0",
|
||||||
|
|
|
@ -8,8 +8,6 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
arrayvec = "0.4"
|
arrayvec = "0.4"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
lyon_geom = "0.12"
|
|
||||||
lyon_path = "0.12"
|
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
smallvec = "0.6"
|
smallvec = "0.6"
|
||||||
|
|
|
@ -14,8 +14,6 @@ use crate::basic::point::Point2DF32;
|
||||||
use crate::basic::rect::RectF32;
|
use crate::basic::rect::RectF32;
|
||||||
use crate::basic::transform3d::Transform3DF32;
|
use crate::basic::transform3d::Transform3DF32;
|
||||||
use crate::segment::Segment;
|
use crate::segment::Segment;
|
||||||
use euclid::Transform2D;
|
|
||||||
use lyon_path::PathEvent;
|
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
|
|
||||||
|
@ -302,50 +300,3 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms a path with a Euclid 2D transform.
|
|
||||||
pub struct Transform2DPathIter<I> where I: Iterator<Item = PathEvent> {
|
|
||||||
inner: I,
|
|
||||||
transform: Transform2D<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Transform2DPathIter<I> where I: Iterator<Item = PathEvent> {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(inner: I, transform: &Transform2D<f32>) -> Transform2DPathIter<I> {
|
|
||||||
Transform2DPathIter {
|
|
||||||
inner: inner,
|
|
||||||
transform: *transform,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for Transform2DPathIter<I> where I: Iterator<Item = PathEvent> {
|
|
||||||
type Item = PathEvent;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<PathEvent> {
|
|
||||||
match self.inner.next() {
|
|
||||||
Some(PathEvent::MoveTo(to)) => {
|
|
||||||
Some(PathEvent::MoveTo(self.transform.transform_point(&to)))
|
|
||||||
}
|
|
||||||
Some(PathEvent::LineTo(to)) => {
|
|
||||||
Some(PathEvent::LineTo(self.transform.transform_point(&to)))
|
|
||||||
}
|
|
||||||
Some(PathEvent::QuadraticTo(ctrl, to)) => {
|
|
||||||
Some(PathEvent::QuadraticTo(self.transform.transform_point(&ctrl),
|
|
||||||
self.transform.transform_point(&to)))
|
|
||||||
}
|
|
||||||
Some(PathEvent::CubicTo(ctrl1, ctrl2, to)) => {
|
|
||||||
Some(PathEvent::CubicTo(self.transform.transform_point(&ctrl1),
|
|
||||||
self.transform.transform_point(&ctrl2),
|
|
||||||
self.transform.transform_point(&to)))
|
|
||||||
}
|
|
||||||
Some(PathEvent::Arc(center, radius, start, end)) => {
|
|
||||||
Some(PathEvent::Arc(self.transform.transform_point(¢er),
|
|
||||||
self.transform.transform_vector(&radius),
|
|
||||||
start,
|
|
||||||
end))
|
|
||||||
}
|
|
||||||
event => event,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,90 +15,9 @@ use crate::outline::{Contour, PointFlags};
|
||||||
use crate::segment::{CubicSegment, Segment};
|
use crate::segment::{CubicSegment, Segment};
|
||||||
use crate::util::lerp;
|
use crate::util::lerp;
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use lyon_path::PathEvent;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
pub struct RectClipper<'a> {
|
|
||||||
clip_rect: RectF32,
|
|
||||||
subject: &'a [PathEvent],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RectClipper<'a> {
|
|
||||||
pub fn new<'aa>(clip_rect: RectF32, subject: &'aa [PathEvent]) -> RectClipper<'aa> {
|
|
||||||
RectClipper { clip_rect, subject }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clip(&self) -> Vec<PathEvent> {
|
|
||||||
let mut output = self.subject.to_vec();
|
|
||||||
self.clip_against(Edge::left(self.clip_rect), &mut output);
|
|
||||||
self.clip_against(Edge::top(self.clip_rect), &mut output);
|
|
||||||
self.clip_against(Edge::right(self.clip_rect), &mut output);
|
|
||||||
self.clip_against(Edge::bottom(self.clip_rect), &mut output);
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clip_against(&self, edge: Edge, output: &mut Vec<PathEvent>) {
|
|
||||||
let (mut from, mut path_start, mut first_point) = (Point2DF32::default(), None, false);
|
|
||||||
let input = mem::replace(output, vec![]);
|
|
||||||
for event in input {
|
|
||||||
let to = match event {
|
|
||||||
PathEvent::MoveTo(to) => {
|
|
||||||
let to = Point2DF32::from_euclid(to);
|
|
||||||
path_start = Some(to);
|
|
||||||
from = to;
|
|
||||||
first_point = true;
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
PathEvent::Close => {
|
|
||||||
match path_start {
|
|
||||||
None => continue,
|
|
||||||
Some(path_start) => path_start,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PathEvent::LineTo(to) |
|
|
||||||
PathEvent::QuadraticTo(_, to) |
|
|
||||||
PathEvent::CubicTo(_, _, to) => Point2DF32::from_euclid(to),
|
|
||||||
PathEvent::Arc(..) => panic!("Arcs unsupported!"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if edge.point_is_inside(&to) {
|
|
||||||
if !edge.point_is_inside(&from) {
|
|
||||||
let line_segment = LineSegmentF32::new(&from, &to);
|
|
||||||
for t in edge.intersect_line_segment(&line_segment) {
|
|
||||||
let intersection = line_segment.sample(t);
|
|
||||||
add_line(&intersection, output, &mut first_point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
add_line(&to, output, &mut first_point);
|
|
||||||
} else if edge.point_is_inside(&from) {
|
|
||||||
let line_segment = LineSegmentF32::new(&from, &to);
|
|
||||||
for t in edge.intersect_line_segment(&line_segment) {
|
|
||||||
let intersection = line_segment.sample(t);
|
|
||||||
add_line(&intersection, output, &mut first_point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
from = to;
|
|
||||||
|
|
||||||
if let PathEvent::Close = event {
|
|
||||||
output.push(PathEvent::Close);
|
|
||||||
path_start = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_line(to: &Point2DF32, output: &mut Vec<PathEvent>, first_point: &mut bool) {
|
|
||||||
let to = to.as_euclid();
|
|
||||||
if *first_point {
|
|
||||||
output.push(PathEvent::MoveTo(to));
|
|
||||||
*first_point = false;
|
|
||||||
} else {
|
|
||||||
output.push(PathEvent::LineTo(to));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct Edge(LineSegmentF32);
|
struct Edge(LineSegmentF32);
|
||||||
|
|
||||||
|
@ -121,28 +40,6 @@ impl TEdge for Edge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edge {
|
|
||||||
#[inline]
|
|
||||||
fn left(rect: RectF32) -> Edge {
|
|
||||||
Edge(LineSegmentF32::new(&rect.lower_left(), &rect.origin()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn top(rect: RectF32) -> Edge {
|
|
||||||
Edge(LineSegmentF32::new(&rect.origin(), &rect.upper_right()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn right(rect: RectF32) -> Edge {
|
|
||||||
Edge(LineSegmentF32::new(&rect.upper_right(), &rect.lower_right()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn bottom(rect: RectF32) -> Edge {
|
|
||||||
Edge(LineSegmentF32::new(&rect.lower_right(), &rect.lower_left()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum AxisAlignedEdge {
|
enum AxisAlignedEdge {
|
||||||
Left(f32),
|
Left(f32),
|
||||||
|
|
|
@ -21,7 +21,6 @@ pub mod monotonic;
|
||||||
pub mod orientation;
|
pub mod orientation;
|
||||||
pub mod outline;
|
pub mod outline;
|
||||||
pub mod segment;
|
pub mod segment;
|
||||||
pub mod segments;
|
|
||||||
pub mod stroke;
|
pub mod stroke;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
use crate::basic::line_segment::LineSegmentF32;
|
use crate::basic::line_segment::LineSegmentF32;
|
||||||
use crate::basic::point::Point2DF32;
|
use crate::basic::point::Point2DF32;
|
||||||
use lyon_path::PathEvent;
|
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
@ -316,156 +315,3 @@ impl<'s> CubicSegment<'s> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn max_y(&self) -> f32 { f32::max(self.0.baseline.max_y(), self.0.ctrl.max_y()) }
|
pub fn max_y(&self) -> f32 { f32::max(self.0.baseline.max_y(), self.0.ctrl.max_y()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lyon interoperability
|
|
||||||
|
|
||||||
pub struct PathEventsToSegments<I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = PathEvent>,
|
|
||||||
{
|
|
||||||
iter: I,
|
|
||||||
first_subpath_point: Point2DF32,
|
|
||||||
last_subpath_point: Point2DF32,
|
|
||||||
just_moved: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> PathEventsToSegments<I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = PathEvent>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn new(iter: I) -> PathEventsToSegments<I> {
|
|
||||||
PathEventsToSegments {
|
|
||||||
iter,
|
|
||||||
first_subpath_point: Point2DF32::default(),
|
|
||||||
last_subpath_point: Point2DF32::default(),
|
|
||||||
just_moved: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for PathEventsToSegments<I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = PathEvent>,
|
|
||||||
{
|
|
||||||
type Item = Segment;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Segment> {
|
|
||||||
match self.iter.next()? {
|
|
||||||
PathEvent::MoveTo(to) => {
|
|
||||||
let to = Point2DF32::from_euclid(to);
|
|
||||||
self.first_subpath_point = to;
|
|
||||||
self.last_subpath_point = to;
|
|
||||||
self.just_moved = true;
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
PathEvent::LineTo(to) => {
|
|
||||||
let to = Point2DF32::from_euclid(to);
|
|
||||||
let mut segment =
|
|
||||||
Segment::line(&LineSegmentF32::new(&self.last_subpath_point, &to));
|
|
||||||
if self.just_moved {
|
|
||||||
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
|
|
||||||
}
|
|
||||||
self.last_subpath_point = to;
|
|
||||||
self.just_moved = false;
|
|
||||||
Some(segment)
|
|
||||||
}
|
|
||||||
PathEvent::QuadraticTo(ctrl, to) => {
|
|
||||||
let (ctrl, to) = (Point2DF32::from_euclid(ctrl), Point2DF32::from_euclid(to));
|
|
||||||
let mut segment =
|
|
||||||
Segment::quadratic(&LineSegmentF32::new(&self.last_subpath_point, &to), &ctrl);
|
|
||||||
if self.just_moved {
|
|
||||||
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
|
|
||||||
}
|
|
||||||
self.last_subpath_point = to;
|
|
||||||
self.just_moved = false;
|
|
||||||
Some(segment)
|
|
||||||
}
|
|
||||||
PathEvent::CubicTo(ctrl0, ctrl1, to) => {
|
|
||||||
let ctrl0 = Point2DF32::from_euclid(ctrl0);
|
|
||||||
let ctrl1 = Point2DF32::from_euclid(ctrl1);
|
|
||||||
let to = Point2DF32::from_euclid(to);
|
|
||||||
let mut segment = Segment::cubic(
|
|
||||||
&LineSegmentF32::new(&self.last_subpath_point, &to),
|
|
||||||
&LineSegmentF32::new(&ctrl0, &ctrl1),
|
|
||||||
);
|
|
||||||
if self.just_moved {
|
|
||||||
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
|
|
||||||
}
|
|
||||||
self.last_subpath_point = to;
|
|
||||||
self.just_moved = false;
|
|
||||||
Some(segment)
|
|
||||||
}
|
|
||||||
PathEvent::Close => {
|
|
||||||
let mut segment = Segment::line(&LineSegmentF32::new(
|
|
||||||
&self.last_subpath_point,
|
|
||||||
&self.first_subpath_point,
|
|
||||||
));
|
|
||||||
segment.flags.insert(SegmentFlags::CLOSES_SUBPATH);
|
|
||||||
self.just_moved = false;
|
|
||||||
self.last_subpath_point = self.first_subpath_point;
|
|
||||||
Some(segment)
|
|
||||||
}
|
|
||||||
PathEvent::Arc(..) => panic!("TODO: arcs"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SegmentsToPathEvents<I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = Segment>,
|
|
||||||
{
|
|
||||||
iter: I,
|
|
||||||
buffer: Option<PathEvent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> SegmentsToPathEvents<I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = Segment>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn new(iter: I) -> SegmentsToPathEvents<I> {
|
|
||||||
SegmentsToPathEvents { iter, buffer: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for SegmentsToPathEvents<I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = Segment>,
|
|
||||||
{
|
|
||||||
type Item = PathEvent;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<PathEvent> {
|
|
||||||
if let Some(event) = self.buffer.take() {
|
|
||||||
return Some(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
let segment = self.iter.next()?;
|
|
||||||
if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
|
|
||||||
return Some(PathEvent::Close);
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = match segment.kind {
|
|
||||||
SegmentKind::None => return self.next(),
|
|
||||||
SegmentKind::Line => PathEvent::LineTo(segment.baseline.to().as_euclid()),
|
|
||||||
SegmentKind::Quadratic => PathEvent::QuadraticTo(
|
|
||||||
segment.ctrl.from().as_euclid(),
|
|
||||||
segment.baseline.to().as_euclid(),
|
|
||||||
),
|
|
||||||
SegmentKind::Cubic => PathEvent::CubicTo(
|
|
||||||
segment.ctrl.from().as_euclid(),
|
|
||||||
segment.ctrl.to().as_euclid(),
|
|
||||||
segment.baseline.to().as_euclid(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) {
|
|
||||||
self.buffer = Some(event);
|
|
||||||
Some(PathEvent::MoveTo(segment.baseline.from().as_euclid()))
|
|
||||||
} else {
|
|
||||||
Some(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,262 +0,0 @@
|
||||||
// pathfinder/geometry/src/segments.rs
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
//! Returns each segment of a path.
|
|
||||||
|
|
||||||
use euclid::approxeq::ApproxEq;
|
|
||||||
use euclid::{Point2D, Vector2D};
|
|
||||||
use lyon_geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment};
|
|
||||||
use lyon_path::iterator::{PathIter, PathIterator};
|
|
||||||
use lyon_path::PathEvent;
|
|
||||||
|
|
||||||
pub struct SegmentIter<I> where I: Iterator<Item = PathEvent> {
|
|
||||||
inner: PathIter<I>,
|
|
||||||
stack: Vec<Segment>,
|
|
||||||
was_just_closed: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> SegmentIter<I> where I: Iterator<Item = PathEvent> {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(inner: I) -> SegmentIter<I> {
|
|
||||||
SegmentIter {
|
|
||||||
inner: PathIter::new(inner),
|
|
||||||
stack: vec![],
|
|
||||||
was_just_closed: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for SegmentIter<I> where I: Iterator<Item = PathEvent> {
|
|
||||||
type Item = Segment;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Segment> {
|
|
||||||
if let Some(segment) = self.stack.pop() {
|
|
||||||
return Some(segment)
|
|
||||||
}
|
|
||||||
|
|
||||||
let current_point = self.inner.get_state().current;
|
|
||||||
|
|
||||||
match self.inner.next() {
|
|
||||||
None => None,
|
|
||||||
Some(PathEvent::Close) => {
|
|
||||||
self.was_just_closed = true;
|
|
||||||
let state = self.inner.get_state();
|
|
||||||
self.stack.push(Segment::EndSubpath(true));
|
|
||||||
Some(Segment::Line(LineSegment {
|
|
||||||
from: current_point,
|
|
||||||
to: state.first,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Some(PathEvent::MoveTo(_)) => {
|
|
||||||
if self.was_just_closed {
|
|
||||||
self.was_just_closed = false;
|
|
||||||
return self.next();
|
|
||||||
}
|
|
||||||
Some(Segment::EndSubpath(false))
|
|
||||||
}
|
|
||||||
Some(PathEvent::LineTo(to)) => {
|
|
||||||
Some(Segment::Line(LineSegment {
|
|
||||||
from: current_point,
|
|
||||||
to: to,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Some(PathEvent::QuadraticTo(ctrl, to)) => {
|
|
||||||
Some(Segment::Quadratic(QuadraticBezierSegment {
|
|
||||||
from: current_point,
|
|
||||||
ctrl: ctrl,
|
|
||||||
to: to,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Some(PathEvent::CubicTo(ctrl1, ctrl2, to)) => {
|
|
||||||
Some(Segment::Cubic(CubicBezierSegment {
|
|
||||||
from: current_point,
|
|
||||||
ctrl1: ctrl1,
|
|
||||||
ctrl2: ctrl2,
|
|
||||||
to: to,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Some(PathEvent::Arc(..)) => {
|
|
||||||
panic!("SegmentIter doesn't support cubics and arcs yet!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Segment {
|
|
||||||
Line(LineSegment<f32>),
|
|
||||||
Quadratic(QuadraticBezierSegment<f32>),
|
|
||||||
Cubic(CubicBezierSegment<f32>),
|
|
||||||
/// True if the subpath is closed.
|
|
||||||
EndSubpath(bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Segment {
|
|
||||||
pub fn flip(&self) -> Segment {
|
|
||||||
match *self {
|
|
||||||
Segment::EndSubpath(closed) => Segment::EndSubpath(closed),
|
|
||||||
Segment::Line(line_segment) => Segment::Line(line_segment.flip()),
|
|
||||||
Segment::Quadratic(quadratic_segment) => Segment::Quadratic(quadratic_segment.flip()),
|
|
||||||
Segment::Cubic(cubic_segment) => Segment::Cubic(cubic_segment.flip()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn offset<F>(&self, distance: f32, mut sink: F) where F: FnMut(&Segment) {
|
|
||||||
match *self {
|
|
||||||
Segment::EndSubpath(_) => {}
|
|
||||||
Segment::Line(ref segment) => {
|
|
||||||
sink(&Segment::Line(offset_line_segment(segment, distance)))
|
|
||||||
}
|
|
||||||
|
|
||||||
Segment::Quadratic(ref quadratic_segment) => {
|
|
||||||
// This is the Tiller & Hanson 1984 algorithm for approximate Bézier offset curves.
|
|
||||||
// We take the cage (i.e. convex hull) and push its edges out along their normals,
|
|
||||||
// then recompute the control point with a miter join.
|
|
||||||
let line_segments = (LineSegment {
|
|
||||||
from: quadratic_segment.from,
|
|
||||||
to: quadratic_segment.ctrl,
|
|
||||||
}, LineSegment {
|
|
||||||
from: quadratic_segment.ctrl,
|
|
||||||
to: quadratic_segment.to,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Miter join.
|
|
||||||
let (from, intersection, to) = match offset_and_join_line_segments(line_segments.0,
|
|
||||||
line_segments.1,
|
|
||||||
distance) {
|
|
||||||
None => return sink(self),
|
|
||||||
Some(intersection) => intersection,
|
|
||||||
};
|
|
||||||
|
|
||||||
sink(&Segment::Quadratic(QuadraticBezierSegment {
|
|
||||||
from: from,
|
|
||||||
ctrl: intersection,
|
|
||||||
to: to,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
Segment::Cubic(ref cubic_segment) if points_overlap(&cubic_segment.from,
|
|
||||||
&cubic_segment.ctrl1) => {
|
|
||||||
// As above.
|
|
||||||
let line_segments = (LineSegment {
|
|
||||||
from: cubic_segment.from,
|
|
||||||
to: cubic_segment.ctrl2,
|
|
||||||
}, LineSegment {
|
|
||||||
from: cubic_segment.ctrl2,
|
|
||||||
to: cubic_segment.to,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Miter join.
|
|
||||||
let (from, intersection, to) = match offset_and_join_line_segments(line_segments.0,
|
|
||||||
line_segments.1,
|
|
||||||
distance) {
|
|
||||||
None => return sink(self),
|
|
||||||
Some(intersection) => intersection,
|
|
||||||
};
|
|
||||||
|
|
||||||
sink(&Segment::Cubic(CubicBezierSegment {
|
|
||||||
from: from,
|
|
||||||
ctrl1: from,
|
|
||||||
ctrl2: intersection,
|
|
||||||
to: to,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
Segment::Cubic(ref cubic_segment) if points_overlap(&cubic_segment.ctrl2,
|
|
||||||
&cubic_segment.to) => {
|
|
||||||
// As above.
|
|
||||||
let line_segments = (LineSegment {
|
|
||||||
from: cubic_segment.from,
|
|
||||||
to: cubic_segment.ctrl1,
|
|
||||||
}, LineSegment {
|
|
||||||
from: cubic_segment.ctrl1,
|
|
||||||
to: cubic_segment.to,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Miter join.
|
|
||||||
let (from, intersection, to) = match offset_and_join_line_segments(line_segments.0,
|
|
||||||
line_segments.1,
|
|
||||||
distance) {
|
|
||||||
None => return sink(self),
|
|
||||||
Some(intersection) => intersection,
|
|
||||||
};
|
|
||||||
|
|
||||||
sink(&Segment::Cubic(CubicBezierSegment {
|
|
||||||
from: from,
|
|
||||||
ctrl1: intersection,
|
|
||||||
ctrl2: to,
|
|
||||||
to: to,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
Segment::Cubic(ref cubic_segment) => {
|
|
||||||
// As above.
|
|
||||||
let line_segments = (LineSegment {
|
|
||||||
from: cubic_segment.from,
|
|
||||||
to: cubic_segment.ctrl1,
|
|
||||||
}, LineSegment {
|
|
||||||
from: cubic_segment.ctrl1,
|
|
||||||
to: cubic_segment.ctrl2,
|
|
||||||
}, LineSegment {
|
|
||||||
from: cubic_segment.ctrl2,
|
|
||||||
to: cubic_segment.to,
|
|
||||||
});
|
|
||||||
|
|
||||||
let (from, intersection_0, _) =
|
|
||||||
match offset_and_join_line_segments(line_segments.0,
|
|
||||||
line_segments.1,
|
|
||||||
distance) {
|
|
||||||
None => return sink(self),
|
|
||||||
Some(intersection) => intersection,
|
|
||||||
};
|
|
||||||
let (_, intersection_1, to) = match offset_and_join_line_segments(line_segments.1,
|
|
||||||
line_segments.2,
|
|
||||||
distance) {
|
|
||||||
None => return sink(self),
|
|
||||||
Some(intersection) => intersection,
|
|
||||||
};
|
|
||||||
|
|
||||||
sink(&Segment::Cubic(CubicBezierSegment {
|
|
||||||
from: from,
|
|
||||||
ctrl1: intersection_0,
|
|
||||||
ctrl2: intersection_1,
|
|
||||||
to: to,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn offset_line_segment(segment: &LineSegment<f32>, distance: f32) -> LineSegment<f32> {
|
|
||||||
let mut segment = *segment;
|
|
||||||
let vector = segment.to_vector();
|
|
||||||
if vector.square_length() < f32::approx_epsilon() {
|
|
||||||
return segment
|
|
||||||
}
|
|
||||||
let tangent = vector.normalize() * distance;
|
|
||||||
segment.translate(Vector2D::new(-tangent.y, tangent.x))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Performs a miter join.
|
|
||||||
fn offset_and_join_line_segments(mut line_segment_0: LineSegment<f32>,
|
|
||||||
mut line_segment_1: LineSegment<f32>,
|
|
||||||
distance: f32)
|
|
||||||
-> Option<(Point2D<f32>, Point2D<f32>, Point2D<f32>)> {
|
|
||||||
line_segment_0 = offset_line_segment(&line_segment_0, distance);
|
|
||||||
line_segment_1 = offset_line_segment(&line_segment_1, distance);
|
|
||||||
match line_segment_0.to_line().intersection(&line_segment_1.to_line()) {
|
|
||||||
None => None,
|
|
||||||
Some(intersection) => Some((line_segment_0.from, intersection, line_segment_1.to)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn points_overlap(a: &Point2D<f32>, b: &Point2D<f32>) -> bool {
|
|
||||||
a.x.approx_eq(&b.x) && a.y.approx_eq(&b.y)
|
|
||||||
}
|
|
|
@ -14,144 +14,8 @@ use crate::basic::line_segment::LineSegmentF32;
|
||||||
use crate::basic::rect::RectF32;
|
use crate::basic::rect::RectF32;
|
||||||
use crate::outline::{Contour, Outline};
|
use crate::outline::{Contour, Outline};
|
||||||
use crate::segment::Segment as SegmentPF3;
|
use crate::segment::Segment as SegmentPF3;
|
||||||
use crate::segments::{Segment, SegmentIter};
|
|
||||||
use lyon_path::PathEvent;
|
|
||||||
use lyon_path::iterator::PathIterator;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct StrokeStyle {
|
|
||||||
pub width: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StrokeStyle {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(width: f32) -> StrokeStyle {
|
|
||||||
StrokeStyle {
|
|
||||||
width: width,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StrokeToFillIter<I> where I: PathIterator {
|
|
||||||
inner: SegmentIter<I>,
|
|
||||||
subpath: Vec<Segment>,
|
|
||||||
stack: Vec<PathEvent>,
|
|
||||||
state: StrokeToFillState,
|
|
||||||
style: StrokeStyle,
|
|
||||||
first_point_in_subpath: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> StrokeToFillIter<I> where I: PathIterator {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(inner: I, style: StrokeStyle) -> StrokeToFillIter<I> {
|
|
||||||
StrokeToFillIter {
|
|
||||||
inner: SegmentIter::new(inner),
|
|
||||||
subpath: vec![],
|
|
||||||
stack: vec![],
|
|
||||||
state: StrokeToFillState::Forward,
|
|
||||||
style: style,
|
|
||||||
first_point_in_subpath: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for StrokeToFillIter<I> where I: PathIterator {
|
|
||||||
type Item = PathEvent;
|
|
||||||
|
|
||||||
// TODO(pcwalton): Support miter and round joins. This will probably require the inner iterator
|
|
||||||
// to be `Peekable`, I guess.
|
|
||||||
fn next(&mut self) -> Option<PathEvent> {
|
|
||||||
// If we have path events queued, return the latest.
|
|
||||||
if let Some(path_event) = self.stack.pop() {
|
|
||||||
return Some(path_event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch the next segment.
|
|
||||||
let next_segment = match self.state {
|
|
||||||
StrokeToFillState::Forward => {
|
|
||||||
match self.inner.next() {
|
|
||||||
None | Some(Segment::EndSubpath(false)) => {
|
|
||||||
if self.subpath.is_empty() {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
self.state = StrokeToFillState::Backward;
|
|
||||||
return self.next()
|
|
||||||
}
|
|
||||||
Some(Segment::EndSubpath(true)) => {
|
|
||||||
if self.subpath.is_empty() {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
self.state = StrokeToFillState::Backward;
|
|
||||||
self.first_point_in_subpath = true;
|
|
||||||
return Some(PathEvent::Close)
|
|
||||||
}
|
|
||||||
Some(segment) => {
|
|
||||||
self.subpath.push(segment);
|
|
||||||
segment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StrokeToFillState::Backward => {
|
|
||||||
match self.subpath.pop() {
|
|
||||||
None | Some(Segment::EndSubpath(_)) => {
|
|
||||||
self.state = StrokeToFillState::Forward;
|
|
||||||
self.first_point_in_subpath = true;
|
|
||||||
return Some(PathEvent::Close)
|
|
||||||
}
|
|
||||||
Some(segment) => segment.flip(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
next_segment.offset(self.style.width * 0.5, |offset_segment| {
|
|
||||||
match *offset_segment {
|
|
||||||
Segment::EndSubpath(_) => unreachable!(),
|
|
||||||
Segment::Line(ref offset_segment) => {
|
|
||||||
if self.first_point_in_subpath {
|
|
||||||
self.first_point_in_subpath = false;
|
|
||||||
self.stack.push(PathEvent::MoveTo(offset_segment.from))
|
|
||||||
} else if self.stack.is_empty() {
|
|
||||||
self.stack.push(PathEvent::LineTo(offset_segment.from))
|
|
||||||
}
|
|
||||||
self.stack.push(PathEvent::LineTo(offset_segment.to))
|
|
||||||
}
|
|
||||||
Segment::Quadratic(ref offset_segment) => {
|
|
||||||
if self.first_point_in_subpath {
|
|
||||||
self.first_point_in_subpath = false;
|
|
||||||
self.stack.push(PathEvent::MoveTo(offset_segment.from))
|
|
||||||
} else if self.stack.is_empty() {
|
|
||||||
self.stack.push(PathEvent::LineTo(offset_segment.from))
|
|
||||||
}
|
|
||||||
self.stack.push(PathEvent::QuadraticTo(offset_segment.ctrl, offset_segment.to))
|
|
||||||
}
|
|
||||||
Segment::Cubic(ref offset_segment) => {
|
|
||||||
if self.first_point_in_subpath {
|
|
||||||
self.first_point_in_subpath = false;
|
|
||||||
self.stack.push(PathEvent::MoveTo(offset_segment.from))
|
|
||||||
} else if self.stack.is_empty() {
|
|
||||||
self.stack.push(PathEvent::LineTo(offset_segment.from))
|
|
||||||
}
|
|
||||||
self.stack.push(PathEvent::CubicTo(offset_segment.ctrl1,
|
|
||||||
offset_segment.ctrl2,
|
|
||||||
offset_segment.to))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.stack.reverse();
|
|
||||||
return self.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
enum StrokeToFillState {
|
|
||||||
Forward,
|
|
||||||
Backward,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pathfinder 3
|
|
||||||
|
|
||||||
pub struct OutlineStrokeToFill {
|
pub struct OutlineStrokeToFill {
|
||||||
pub outline: Outline,
|
pub outline: Outline,
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
|
|
|
@ -10,14 +10,12 @@
|
||||||
|
|
||||||
//! Converts a subset of SVG to a Pathfinder scene.
|
//! Converts a subset of SVG to a Pathfinder scene.
|
||||||
|
|
||||||
use lyon_path::iterator::PathIter;
|
|
||||||
use pathfinder_geometry::basic::line_segment::LineSegmentF32;
|
use pathfinder_geometry::basic::line_segment::LineSegmentF32;
|
||||||
use pathfinder_geometry::basic::point::Point2DF32;
|
use pathfinder_geometry::basic::point::Point2DF32;
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32PathIter};
|
use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32PathIter};
|
||||||
use pathfinder_geometry::outline::Outline;
|
use pathfinder_geometry::outline::Outline;
|
||||||
use pathfinder_geometry::segment::{PathEventsToSegments, Segment};
|
use pathfinder_geometry::segment::{Segment, SegmentFlags};
|
||||||
use pathfinder_geometry::segment::{SegmentFlags, SegmentsToPathEvents};
|
|
||||||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
||||||
use pathfinder_renderer::paint::{ColorU, Paint};
|
use pathfinder_renderer::paint::{ColorU, Paint};
|
||||||
use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene};
|
use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene};
|
||||||
|
@ -91,10 +89,6 @@ fn process_node(scene: &mut Scene, node: &Node, transform: &Transform2DF32) {
|
||||||
f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH);
|
f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH);
|
||||||
|
|
||||||
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
||||||
/*let path = SegmentsToPathEvents::new(path);
|
|
||||||
let path = PathIter::new(path);
|
|
||||||
let path = StrokeToFillIter::new(path, StrokeStyle::new(stroke_width));
|
|
||||||
let path = PathEventsToSegments::new(path);*/
|
|
||||||
let path = Transform2DF32PathIter::new(path, &transform);
|
let path = Transform2DF32PathIter::new(path, &transform);
|
||||||
let outline = Outline::from_segments(path);
|
let outline = Outline::from_segments(path);
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,6 @@ euclid = "0.19"
|
||||||
fixedbitset = "0.1"
|
fixedbitset = "0.1"
|
||||||
hashbrown = "0.1"
|
hashbrown = "0.1"
|
||||||
jemallocator = "0.1"
|
jemallocator = "0.1"
|
||||||
lyon_geom = "0.12"
|
|
||||||
lyon_path = "0.12"
|
|
||||||
rayon = "1.0"
|
rayon = "1.0"
|
||||||
svgtypes = "0.3"
|
svgtypes = "0.3"
|
||||||
usvg = "0.4"
|
usvg = "0.4"
|
||||||
|
|
Loading…
Reference in New Issue