Add a fast path for monotonic conversion
This commit is contained in:
parent
70e615205e
commit
f19757e4cf
|
@ -287,62 +287,67 @@ trait ContourClipper where Self::Edge: TEdge {
|
||||||
}
|
}
|
||||||
|
|
||||||
let input = self.contour_mut().take();
|
let input = self.contour_mut().take();
|
||||||
for mut segment in input.iter() {
|
for segment in input.iter() {
|
||||||
// Easy cases.
|
self.clip_segment_against(segment, &edge);
|
||||||
match edge.trivially_test_segment(&segment) {
|
}
|
||||||
EdgeRelativeLocation::Outside => continue,
|
}
|
||||||
EdgeRelativeLocation::Inside => {
|
|
||||||
//println!("trivial test inside, pushing segment");
|
fn clip_segment_against(&mut self, mut segment: Segment, edge: &Self::Edge) {
|
||||||
push_segment(self.contour_mut(), &segment);
|
// Easy cases.
|
||||||
continue;
|
match edge.trivially_test_segment(&segment) {
|
||||||
}
|
EdgeRelativeLocation::Outside => return,
|
||||||
EdgeRelativeLocation::Intersecting => {}
|
EdgeRelativeLocation::Inside => {
|
||||||
|
//println!("trivial test inside, pushing segment");
|
||||||
|
self.push_segment(&segment);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
EdgeRelativeLocation::Intersecting => {}
|
||||||
|
}
|
||||||
|
|
||||||
// We have a potential intersection.
|
// We have a potential intersection.
|
||||||
//println!("potential intersection: {:?} edge: {:?}", segment, edge);
|
//println!("potential intersection: {:?} edge: {:?}", segment, edge);
|
||||||
let mut starts_inside = edge.point_is_inside(&segment.baseline.from());
|
let mut starts_inside = edge.point_is_inside(&segment.baseline.from());
|
||||||
let intersection_ts = edge.intersect_segment(&segment);
|
let intersection_ts = edge.intersect_segment(&segment);
|
||||||
let mut last_t = 0.0;
|
let mut last_t = 0.0;
|
||||||
//println!("... intersections: {:?}", intersection_ts);
|
//println!("... intersections: {:?}", intersection_ts);
|
||||||
for t in intersection_ts {
|
for t in intersection_ts {
|
||||||
let (before_split, after_split) = segment.split((t - last_t) / (1.0 - last_t));
|
let (before_split, after_split) = segment.split((t - last_t) / (1.0 - last_t));
|
||||||
|
|
||||||
// Push the split segment if appropriate.
|
// Push the split segment if appropriate.
|
||||||
/*println!("... ... edge={:?} before_split={:?} t={:?} starts_inside={:?}",
|
/*println!("... ... edge={:?} before_split={:?} t={:?} starts_inside={:?}",
|
||||||
edge.0,
|
edge.0,
|
||||||
before_split,
|
before_split,
|
||||||
t,
|
t,
|
||||||
starts_inside);*/
|
starts_inside);*/
|
||||||
if starts_inside {
|
|
||||||
//println!("... split segment case, pushing segment");
|
|
||||||
push_segment(self.contour_mut(), &before_split);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We've now transitioned from inside to outside or vice versa.
|
|
||||||
starts_inside = !starts_inside;
|
|
||||||
last_t = t;
|
|
||||||
segment = after_split;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No more intersections. Push the last segment if applicable.
|
|
||||||
if starts_inside {
|
if starts_inside {
|
||||||
//println!("... last segment case, pushing segment");
|
//println!("... split segment case, pushing segment");
|
||||||
push_segment(self.contour_mut(), &segment);
|
self.push_segment(&before_split);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've now transitioned from inside to outside or vice versa.
|
||||||
|
starts_inside = !starts_inside;
|
||||||
|
last_t = t;
|
||||||
|
segment = after_split;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No more intersections. Push the last segment if applicable.
|
||||||
|
if starts_inside {
|
||||||
|
//println!("... last segment case, pushing segment");
|
||||||
|
self.push_segment(&segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_segment(&mut self, segment: &Segment) {
|
||||||
|
//println!("... push_segment({:?}, edge={:?}", segment, edge);
|
||||||
|
let contour = self.contour_mut();
|
||||||
|
if let Some(last_position) = contour.last_position() {
|
||||||
|
if last_position != segment.baseline.from() {
|
||||||
|
// Add a line to join up segments.
|
||||||
|
contour.push_point(segment.baseline.from(), PointFlags::empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_segment(contour: &mut Contour, segment: &Segment) {
|
contour.push_segment(*segment);
|
||||||
//println!("... push_segment({:?}, edge={:?}", segment, edge);
|
|
||||||
if let Some(last_position) = contour.last_position() {
|
|
||||||
if last_position != segment.baseline.from() {
|
|
||||||
// Add a line to join up segments.
|
|
||||||
contour.push_point(segment.baseline.from(), PointFlags::empty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contour.push_segment(*segment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult {
|
fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult {
|
||||||
|
|
|
@ -8,12 +8,11 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! Converts paths to monotonically increasing/decreasing segments.
|
//! Converts paths to monotonically increasing/decreasing segments in Y.
|
||||||
|
|
||||||
use crate::segment::{Segment, SegmentKind};
|
use crate::segment::{Segment, SegmentKind};
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
|
|
||||||
// TODO(pcwalton): I think we only need to be monotonic in Y, maybe?
|
|
||||||
pub struct MonotonicConversionIter<I>
|
pub struct MonotonicConversionIter<I>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Segment>,
|
I: Iterator<Item = Segment>,
|
||||||
|
|
|
@ -356,7 +356,12 @@ impl Contour {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn make_monotonic(&mut self) {
|
pub fn make_monotonic(&mut self) {
|
||||||
// TODO(pcwalton): Make monotonic in place?
|
// Fast path.
|
||||||
|
if self.iter().all(|segment| segment.is_monotonic()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path.
|
||||||
let contour = self.take();
|
let contour = self.take();
|
||||||
for segment in MonotonicConversionIter::new(contour.iter()) {
|
for segment in MonotonicConversionIter::new(contour.iter()) {
|
||||||
self.push_segment(segment);
|
self.push_segment(segment);
|
||||||
|
|
|
@ -112,6 +112,16 @@ impl Segment {
|
||||||
new_segment
|
new_segment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_monotonic(&self) -> bool {
|
||||||
|
// FIXME(pcwalton): Don't degree elevate!
|
||||||
|
match self.kind {
|
||||||
|
SegmentKind::None | SegmentKind::Line => true,
|
||||||
|
SegmentKind::Quadratic => self.to_cubic().as_cubic_segment().is_monotonic(),
|
||||||
|
SegmentKind::Cubic => self.as_cubic_segment().is_monotonic(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reversed(&self) -> Segment {
|
pub fn reversed(&self) -> Segment {
|
||||||
Segment {
|
Segment {
|
||||||
|
@ -265,19 +275,25 @@ impl<'s> CubicSegment<'s> {
|
||||||
self.split(t).0.baseline.to()
|
self.split(t).0.baseline.to()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_monotonic(self) -> bool {
|
||||||
|
// TODO(pcwalton): Optimize this.
|
||||||
|
let (p0, p3) = (self.0.baseline.from_y(), self.0.baseline.to_y());
|
||||||
|
let (p1, p2) = (self.0.ctrl.from_y(), self.0.ctrl.to_y());
|
||||||
|
(p0 <= p1 && p1 <= p2 && p2 <= p3) || (p0 >= p1 && p1 >= p2 && p2 >= p3)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn y_extrema(self) -> (Option<f32>, Option<f32>) {
|
pub fn y_extrema(self) -> (Option<f32>, Option<f32>) {
|
||||||
|
if self.is_monotonic() {
|
||||||
|
return (None, None)
|
||||||
|
}
|
||||||
|
|
||||||
let p0p1p2p3 = F32x4::new(self.0.baseline.from_y(),
|
let p0p1p2p3 = F32x4::new(self.0.baseline.from_y(),
|
||||||
self.0.ctrl.from_y(),
|
self.0.ctrl.from_y(),
|
||||||
self.0.ctrl.to_y(),
|
self.0.ctrl.to_y(),
|
||||||
self.0.baseline.to_y());
|
self.0.baseline.to_y());
|
||||||
|
|
||||||
// TODO(pcwalton): Optimize this.
|
|
||||||
if p0p1p2p3[0] <= p0p1p2p3[1] && p0p1p2p3[0] <= p0p1p2p3[2] &&
|
|
||||||
p0p1p2p3[1] <= p0p1p2p3[3] && p0p1p2p3[2] <= p0p1p2p3[3] {
|
|
||||||
return (None, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let pxp0p1p2 = p0p1p2p3.wxyz();
|
let pxp0p1p2 = p0p1p2p3.wxyz();
|
||||||
let pxv0v1v2 = p0p1p2p3 - pxp0p1p2;
|
let pxv0v1v2 = p0p1p2p3 - pxp0p1p2;
|
||||||
let (v0, v1, v2) = (pxv0v1v2[1], pxv0v1v2[2], pxv0v1v2[3]);
|
let (v0, v1, v2) = (pxv0v1v2[1], pxv0v1v2[2], pxv0v1v2[3]);
|
||||||
|
|
Loading…
Reference in New Issue