// pathfinder/geometry/src/monotonic.rs // // Copyright © 2019 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Converts paths to monotonically increasing/decreasing segments. use crate::segment::{Segment, SegmentKind}; use arrayvec::ArrayVec; // TODO(pcwalton): I think we only need to be monotonic in Y, maybe? pub struct MonotonicConversionIter where I: Iterator, { iter: I, buffer: ArrayVec<[Segment; 2]>, } impl Iterator for MonotonicConversionIter where I: Iterator, { type Item = Segment; #[inline] fn next(&mut self) -> Option { if let Some(segment) = self.buffer.pop() { return Some(segment); } let segment = self.iter.next()?; match segment.kind { SegmentKind::None => self.next(), SegmentKind::Line => Some(segment), SegmentKind::Cubic => self.handle_cubic(&segment), SegmentKind::Quadratic => { // TODO(pcwalton): Don't degree elevate! self.handle_cubic(&segment.to_cubic()) } } } } impl MonotonicConversionIter where I: Iterator, { #[inline] pub fn new(iter: I) -> MonotonicConversionIter { MonotonicConversionIter { iter, buffer: ArrayVec::new(), } } pub fn handle_cubic(&mut self, segment: &Segment) -> Option { match segment.as_cubic_segment().y_extrema() { (Some(t0), Some(t1)) => { let (segments_01, segment_2) = segment.as_cubic_segment().split(t1); self.buffer.push(segment_2); let (segment_0, segment_1) = segments_01.as_cubic_segment().split(t0 / t1); self.buffer.push(segment_1); Some(segment_0) } (Some(t0), None) | (None, Some(t0)) => { let (segment_0, segment_1) = segment.as_cubic_segment().split(t0); self.buffer.push(segment_1); Some(segment_0) } (None, None) => Some(*segment), } } }