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)",
|
||||
"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)",
|
||||
"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",
|
||||
"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)",
|
||||
|
@ -1024,8 +1022,6 @@ dependencies = [
|
|||
"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)",
|
||||
"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_renderer 0.1.0",
|
||||
"pathfinder_svg 0.1.0",
|
||||
|
|
|
@ -8,8 +8,6 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
|||
arrayvec = "0.4"
|
||||
bitflags = "1.0"
|
||||
euclid = "0.19"
|
||||
lyon_geom = "0.12"
|
||||
lyon_path = "0.12"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
smallvec = "0.6"
|
||||
|
|
|
@ -14,8 +14,6 @@ use crate::basic::point::Point2DF32;
|
|||
use crate::basic::rect::RectF32;
|
||||
use crate::basic::transform3d::Transform3DF32;
|
||||
use crate::segment::Segment;
|
||||
use euclid::Transform2D;
|
||||
use lyon_path::PathEvent;
|
||||
use pathfinder_simd::default::F32x4;
|
||||
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::util::lerp;
|
||||
use arrayvec::ArrayVec;
|
||||
use lyon_path::PathEvent;
|
||||
use smallvec::SmallVec;
|
||||
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)]
|
||||
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)]
|
||||
enum AxisAlignedEdge {
|
||||
Left(f32),
|
||||
|
|
|
@ -21,7 +21,6 @@ pub mod monotonic;
|
|||
pub mod orientation;
|
||||
pub mod outline;
|
||||
pub mod segment;
|
||||
pub mod segments;
|
||||
pub mod stroke;
|
||||
pub mod util;
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
use crate::basic::line_segment::LineSegmentF32;
|
||||
use crate::basic::point::Point2DF32;
|
||||
use lyon_path::PathEvent;
|
||||
use pathfinder_simd::default::F32x4;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
|
@ -316,156 +315,3 @@ impl<'s> CubicSegment<'s> {
|
|||
#[inline]
|
||||
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::outline::{Contour, Outline};
|
||||
use crate::segment::Segment as SegmentPF3;
|
||||
use crate::segments::{Segment, SegmentIter};
|
||||
use lyon_path::PathEvent;
|
||||
use lyon_path::iterator::PathIterator;
|
||||
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 outline: Outline,
|
||||
pub radius: f32,
|
||||
|
|
|
@ -10,14 +10,12 @@
|
|||
|
||||
//! 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::point::Point2DF32;
|
||||
use pathfinder_geometry::basic::rect::RectF32;
|
||||
use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32PathIter};
|
||||
use pathfinder_geometry::outline::Outline;
|
||||
use pathfinder_geometry::segment::{PathEventsToSegments, Segment};
|
||||
use pathfinder_geometry::segment::{SegmentFlags, SegmentsToPathEvents};
|
||||
use pathfinder_geometry::segment::{Segment, SegmentFlags};
|
||||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
||||
use pathfinder_renderer::paint::{ColorU, Paint};
|
||||
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);
|
||||
|
||||
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 outline = Outline::from_segments(path);
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ euclid = "0.19"
|
|||
fixedbitset = "0.1"
|
||||
hashbrown = "0.1"
|
||||
jemallocator = "0.1"
|
||||
lyon_geom = "0.12"
|
||||
lyon_path = "0.12"
|
||||
rayon = "1.0"
|
||||
svgtypes = "0.3"
|
||||
usvg = "0.4"
|
||||
|
|
Loading…
Reference in New Issue