Skip to main content

pathfinder_content/
outline.rs

1// pathfinder/content/src/outline.rs
2//
3// Copyright © 2019 The Pathfinder Project Developers.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! A compressed in-memory representation of paths.
12
13use crate::clip::{self, ContourPolygonClipper, ContourRectClipper};
14use crate::dilation::ContourDilator;
15use crate::orientation::Orientation;
16use crate::segment::{Segment, SegmentFlags, SegmentKind};
17use pathfinder_geometry::line_segment::LineSegment2F;
18use pathfinder_geometry::rect::RectF;
19use pathfinder_geometry::transform2d::Transform2F;
20use pathfinder_geometry::transform3d::Perspective;
21use pathfinder_geometry::unit_vector::UnitVector;
22use pathfinder_geometry::vector::{Vector2F, vec2f};
23use std::f32::consts::PI;
24use std::fmt::{self, Debug, Formatter};
25use std::mem;
26
27#[derive(Clone)]
28pub struct Outline {
29    pub(crate) contours: Vec<Contour>,
30    pub(crate) bounds: RectF,
31}
32
33#[derive(Clone)]
34pub struct Contour {
35    pub(crate) points: Vec<Vector2F>,
36    pub(crate) flags: Vec<PointFlags>,
37    pub(crate) bounds: RectF,
38    pub(crate) closed: bool,
39}
40
41bitflags! {
42    pub struct PointFlags: u8 {
43        const CONTROL_POINT_0 = 0x01;
44        const CONTROL_POINT_1 = 0x02;
45    }
46}
47
48bitflags! {
49    pub struct PushSegmentFlags: u8 {
50        const UPDATE_BOUNDS = 0x01;
51        const INCLUDE_FROM_POINT = 0x02;
52    }
53}
54
55impl Outline {
56    #[inline]
57    pub fn new() -> Outline {
58        Outline {
59            contours: vec![],
60            bounds: RectF::default(),
61        }
62    }
63
64    #[inline]
65    pub fn from_segments<I>(segments: I) -> Outline
66    where
67        I: Iterator<Item = Segment>,
68    {
69        let mut outline = Outline::new();
70        let mut current_contour = Contour::new();
71
72        for segment in segments {
73            if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) {
74                if !current_contour.is_empty() {
75                    outline
76                        .contours
77                        .push(mem::replace(&mut current_contour, Contour::new()));
78                }
79                current_contour.push_point(segment.baseline.from(), PointFlags::empty(), true);
80            }
81
82            if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
83                if !current_contour.is_empty() {
84                    current_contour.close();
85                    let contour = mem::replace(&mut current_contour, Contour::new());
86                    outline.push_contour(contour);
87                }
88                continue;
89            }
90
91            if segment.is_none() {
92                continue;
93            }
94
95            if !segment.is_line() {
96                current_contour.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0, true);
97                if !segment.is_quadratic() {
98                    current_contour.push_point(
99                        segment.ctrl.to(),
100                        PointFlags::CONTROL_POINT_1,
101                        true,
102                    );
103                }
104            }
105
106            current_contour.push_point(segment.baseline.to(), PointFlags::empty(), true);
107        }
108
109        outline.push_contour(current_contour);
110        outline
111    }
112
113    #[inline]
114    pub fn from_rect(rect: RectF) -> Outline {
115        let mut outline = Outline::new();
116        outline.push_contour(Contour::from_rect(rect));
117        outline
118    }
119
120    #[inline]
121    pub fn bounds(&self) -> RectF {
122        self.bounds
123    }
124
125    #[inline]
126    pub fn contours(&self) -> &[Contour] {
127        &self.contours
128    }
129
130    #[inline]
131    pub fn into_contours(self) -> Vec<Contour> {
132        self.contours
133    }
134
135    /// Removes all contours from this outline.
136    #[inline]
137    pub fn clear(&mut self) {
138        self.contours.clear();
139        self.bounds = RectF::default();
140    }
141
142    pub fn push_contour(&mut self, contour: Contour) {
143        if contour.is_empty() {
144            return;
145        }
146
147        if self.contours.is_empty() {
148            self.bounds = contour.bounds;
149        } else {
150            self.bounds = self.bounds.union_rect(contour.bounds);
151        }
152
153        self.contours.push(contour);
154    }
155
156    pub fn pop_contour(&mut self) -> Option<Contour> {
157        let last_contour = self.contours.pop();
158
159        let mut new_bounds = None;
160        for contour in &mut self.contours {
161            contour.update_bounds(&mut new_bounds);
162        }
163        self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
164
165        last_contour
166    }
167
168    pub fn transform(&mut self, transform: &Transform2F) {
169        if transform.is_identity() {
170            return;
171        }
172
173        let mut new_bounds = None;
174        for contour in &mut self.contours {
175            contour.transform(transform);
176            contour.update_bounds(&mut new_bounds);
177        }
178        self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
179    }
180
181    pub fn apply_perspective(&mut self, perspective: &Perspective) {
182        let mut new_bounds = None;
183        for contour in &mut self.contours {
184            contour.apply_perspective(perspective);
185            contour.update_bounds(&mut new_bounds);
186        }
187        self.bounds = new_bounds.unwrap_or_else(|| RectF::default());
188    }
189
190    pub fn dilate(&mut self, amount: Vector2F) {
191        let orientation = Orientation::from_outline(self);
192        self.contours
193            .iter_mut()
194            .for_each(|contour| contour.dilate(amount, orientation));
195        self.bounds = self.bounds.dilate(amount);
196    }
197
198    pub fn prepare_for_tiling(&mut self, view_box: RectF) {
199        self.contours
200            .iter_mut()
201            .for_each(|contour| contour.prepare_for_tiling(view_box));
202        self.bounds = self
203            .bounds
204            .intersection(view_box)
205            .unwrap_or_else(|| RectF::default());
206    }
207
208    pub fn is_outside_polygon(&self, clip_polygon: &[Vector2F]) -> bool {
209        clip::rect_is_outside_polygon(self.bounds, clip_polygon)
210    }
211
212    fn is_inside_polygon(&self, clip_polygon: &[Vector2F]) -> bool {
213        clip::rect_is_inside_polygon(self.bounds, clip_polygon)
214    }
215
216    pub fn clip_against_polygon(&mut self, clip_polygon: &[Vector2F]) {
217        // Quick check.
218        if self.is_inside_polygon(clip_polygon) {
219            return;
220        }
221
222        for contour in mem::replace(&mut self.contours, vec![]) {
223            self.push_contour(ContourPolygonClipper::new(clip_polygon, contour).clip());
224        }
225    }
226
227    pub fn clip_against_rect(&mut self, clip_rect: RectF) {
228        if clip_rect.contains_rect(self.bounds) {
229            return;
230        }
231
232        for contour in mem::replace(&mut self.contours, vec![]) {
233            self.push_contour(ContourRectClipper::new(clip_rect, contour).clip());
234        }
235    }
236
237    #[inline]
238    pub fn close_all_contours(&mut self) {
239        self.contours.iter_mut().for_each(|contour| contour.close());
240    }
241}
242
243impl Debug for Outline {
244    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
245        for (contour_index, contour) in self.contours.iter().enumerate() {
246            if contour_index > 0 {
247                write!(formatter, " ")?;
248            }
249            contour.fmt(formatter)?;
250        }
251        Ok(())
252    }
253}
254
255impl Contour {
256    #[inline]
257    pub fn new() -> Contour {
258        Contour {
259            points: vec![],
260            flags: vec![],
261            bounds: RectF::default(),
262            closed: false,
263        }
264    }
265
266    #[inline]
267    pub fn with_capacity(length: usize) -> Contour {
268        Contour {
269            points: Vec::with_capacity(length),
270            flags: Vec::with_capacity(length),
271            bounds: RectF::default(),
272            closed: false,
273        }
274    }
275
276    #[inline]
277    pub fn from_rect(rect: RectF) -> Contour {
278        let mut contour = Contour::new();
279        contour.push_point(rect.origin(), PointFlags::empty(), false);
280        contour.push_point(rect.upper_right(), PointFlags::empty(), false);
281        contour.push_point(rect.lower_right(), PointFlags::empty(), false);
282        contour.push_point(rect.lower_left(), PointFlags::empty(), false);
283        contour.close();
284        contour.bounds = rect;
285        contour
286    }
287
288    // Replaces this contour with a new one, with arrays preallocated to match `self`.
289    #[inline]
290    pub(crate) fn take(&mut self) -> Contour {
291        let length = self.len() as usize;
292        mem::replace(
293            self,
294            Contour {
295                points: Vec::with_capacity(length),
296                flags: Vec::with_capacity(length),
297                bounds: RectF::default(),
298                closed: false,
299            },
300        )
301    }
302
303    /// restore self to the state of Contour::new(), but keep the points buffer allocated
304    #[inline]
305    pub fn clear(&mut self) {
306        self.points.clear();
307        self.flags.clear();
308        self.bounds = RectF::default();
309        self.closed = false;
310    }
311
312    #[inline]
313    pub fn iter(&self, flags: ContourIterFlags) -> ContourIter {
314        ContourIter {
315            contour: self,
316            index: 1,
317            flags,
318        }
319    }
320
321    #[inline]
322    pub fn is_empty(&self) -> bool {
323        self.points.is_empty()
324    }
325
326    #[inline]
327    pub fn len(&self) -> u32 {
328        self.points.len() as u32
329    }
330
331    #[inline]
332    pub fn bounds(&self) -> RectF {
333        self.bounds
334    }
335
336    #[inline]
337    pub fn is_closed(&self) -> bool {
338        self.closed
339    }
340
341    #[inline]
342    pub fn position_of(&self, index: u32) -> Vector2F {
343        self.points[index as usize]
344    }
345
346    #[inline]
347    pub fn last_position(&self) -> Option<Vector2F> {
348        self.points.last().cloned()
349    }
350
351    #[inline]
352    pub(crate) fn position_of_last(&self, index: u32) -> Vector2F {
353        self.points[self.points.len() - index as usize]
354    }
355
356    #[inline]
357    pub fn push_endpoint(&mut self, point: Vector2F) {
358        self.push_point(point, PointFlags::empty(), true);
359    }
360
361    #[inline]
362    pub fn push_quadratic(&mut self, ctrl: Vector2F, point: Vector2F) {
363        self.push_point(ctrl, PointFlags::CONTROL_POINT_0, true);
364        self.push_point(point, PointFlags::empty(), true);
365    }
366
367    #[inline]
368    pub fn push_cubic(&mut self, ctrl0: Vector2F, ctrl1: Vector2F, point: Vector2F) {
369        self.push_point(ctrl0, PointFlags::CONTROL_POINT_0, true);
370        self.push_point(ctrl1, PointFlags::CONTROL_POINT_1, true);
371        self.push_point(point, PointFlags::empty(), true);
372    }
373
374    #[inline]
375    pub fn close(&mut self) {
376        self.closed = true;
377    }
378
379    #[inline]
380    pub(crate) fn push_point(&mut self,
381                             point: Vector2F,
382                             flags: PointFlags,
383                             update_bounds: bool) {
384        debug_assert!(!point.x().is_nan() && !point.y().is_nan());
385
386        if update_bounds {
387            let first = self.is_empty();
388            union_rect(&mut self.bounds, point, first);
389        }
390
391        self.points.push(point);
392        self.flags.push(flags);
393    }
394
395    #[inline]
396    pub(crate) fn push_segment(&mut self, segment: &Segment, flags: PushSegmentFlags) {
397        if segment.is_none() {
398            return;
399        }
400
401        let update_bounds = flags.contains(PushSegmentFlags::UPDATE_BOUNDS);
402        self.push_point(segment.baseline.from(), PointFlags::empty(), update_bounds);
403
404        if !segment.is_line() {
405            self.push_point(
406                segment.ctrl.from(),
407                PointFlags::CONTROL_POINT_0,
408                update_bounds,
409            );
410            if !segment.is_quadratic() {
411                self.push_point(
412                    segment.ctrl.to(),
413                    PointFlags::CONTROL_POINT_1,
414                    update_bounds,
415                );
416            }
417        }
418
419        self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
420    }
421
422    pub fn push_arc(&mut self,
423                    transform: &Transform2F,
424                    start_angle: f32,
425                    end_angle: f32,
426                    direction: ArcDirection) {
427        if end_angle - start_angle >= PI * 2.0 {
428            self.push_ellipse(transform);
429        } else {
430            let start = vec2f(start_angle.cos(), start_angle.sin());
431            let end   = vec2f(end_angle.cos(),   end_angle.sin());
432            self.push_arc_from_unit_chord(transform, LineSegment2F::new(start, end), direction);
433        }
434    }
435
436    pub fn push_arc_from_unit_chord(&mut self,
437                                    transform: &Transform2F,
438                                    mut chord: LineSegment2F,
439                                    direction: ArcDirection) {
440        let mut direction_transform = Transform2F::default();
441        if direction == ArcDirection::CCW {
442            chord *= vec2f(1.0, -1.0);
443            direction_transform = Transform2F::from_scale(vec2f(1.0, -1.0));
444        }
445
446        let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to()));
447        for segment_index in 0..4 {
448            debug!("push_arc_from_unit_chord(): loop segment index {}", segment_index);
449
450            let mut sweep_vector = end_vector.rev_rotate_by(vector);
451            let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON;
452            debug!("... end_vector={:?} vector={:?} sweep_vector={:?} last={:?}",
453                   end_vector,
454                   vector,
455                   sweep_vector,
456                   last);
457
458            let mut segment;
459            if !last {
460                sweep_vector = UnitVector(vec2f(0.0, 1.0));
461                segment = Segment::quarter_circle_arc();
462            } else {
463                segment = Segment::arc_from_cos(sweep_vector.0.x());
464            }
465
466            let half_sweep_vector = sweep_vector.halve_angle();
467            let rotation = Transform2F::from_rotation_vector(half_sweep_vector.rotate_by(vector));
468            segment = segment.transform(&(*transform * direction_transform * rotation));
469
470            let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS;
471            if segment_index == 0 {
472                push_segment_flags.insert(PushSegmentFlags::INCLUDE_FROM_POINT);
473            }
474            self.push_segment(&segment, push_segment_flags);
475
476            if last {
477                break;
478            }
479
480            vector = vector.rotate_by(sweep_vector);
481        }
482
483        const EPSILON: f32 = 0.001;
484    }
485
486    pub fn push_ellipse(&mut self, transform: &Transform2F) {
487        let segment = Segment::quarter_circle_arc();
488        let mut rotation;
489        self.push_segment(&segment.transform(transform),
490                          PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT);
491        rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0,  1.0)));
492        self.push_segment(&segment.transform(&(*transform * rotation)),
493                          PushSegmentFlags::UPDATE_BOUNDS);
494        rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(-1.0,  0.0)));
495        self.push_segment(&segment.transform(&(*transform * rotation)),
496                          PushSegmentFlags::UPDATE_BOUNDS);
497        rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, -1.0)));
498        self.push_segment(&segment.transform(&(*transform * rotation)),
499                          PushSegmentFlags::UPDATE_BOUNDS);
500    }
501
502    #[inline]
503    pub fn segment_after(&self, point_index: u32) -> Segment {
504        debug_assert!(self.point_is_endpoint(point_index));
505
506        let mut segment = Segment::none();
507        segment.baseline.set_from(self.position_of(point_index));
508
509        let point1_index = self.add_to_point_index(point_index, 1);
510        if self.point_is_endpoint(point1_index) {
511            segment.baseline.set_to(self.position_of(point1_index));
512            segment.kind = SegmentKind::Line;
513        } else {
514            segment.ctrl.set_from(self.position_of(point1_index));
515
516            let point2_index = self.add_to_point_index(point_index, 2);
517            if self.point_is_endpoint(point2_index) {
518                segment.baseline.set_to(self.position_of(point2_index));
519                segment.kind = SegmentKind::Quadratic;
520            } else {
521                segment.ctrl.set_to(self.position_of(point2_index));
522                segment.kind = SegmentKind::Cubic;
523
524                let point3_index = self.add_to_point_index(point_index, 3);
525                segment.baseline.set_to(self.position_of(point3_index));
526            }
527        }
528
529        segment
530    }
531
532    #[inline]
533    pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegment2F {
534        let next_point_index = self.next_point_index_of(prev_point_index);
535        LineSegment2F::new(
536            self.points[prev_point_index as usize],
537            self.points[next_point_index as usize],
538        )
539    }
540
541    #[inline]
542    pub fn point_is_endpoint(&self, point_index: u32) -> bool {
543        !self.flags[point_index as usize]
544            .intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
545    }
546
547    #[inline]
548    pub fn add_to_point_index(&self, point_index: u32, addend: u32) -> u32 {
549        let (index, limit) = (point_index + addend, self.len());
550        if index >= limit {
551            index - limit
552        } else {
553            index
554        }
555    }
556
557    #[inline]
558    pub fn point_is_logically_above(&self, a: u32, b: u32) -> bool {
559        let (a_y, b_y) = (self.points[a as usize].y(), self.points[b as usize].y());
560        a_y < b_y || (a_y == b_y && a < b)
561    }
562
563    #[inline]
564    pub fn prev_endpoint_index_of(&self, mut point_index: u32) -> u32 {
565        loop {
566            point_index = self.prev_point_index_of(point_index);
567            if self.point_is_endpoint(point_index) {
568                return point_index;
569            }
570        }
571    }
572
573    #[inline]
574    pub fn next_endpoint_index_of(&self, mut point_index: u32) -> u32 {
575        loop {
576            point_index = self.next_point_index_of(point_index);
577            if self.point_is_endpoint(point_index) {
578                return point_index;
579            }
580        }
581    }
582
583    #[inline]
584    pub fn prev_point_index_of(&self, point_index: u32) -> u32 {
585        if point_index == 0 {
586            self.len() - 1
587        } else {
588            point_index - 1
589        }
590    }
591
592    #[inline]
593    pub fn next_point_index_of(&self, point_index: u32) -> u32 {
594        if point_index == self.len() - 1 {
595            0
596        } else {
597            point_index + 1
598        }
599    }
600
601    pub fn transform(&mut self, transform: &Transform2F) {
602        if transform.is_identity() {
603            return;
604        }
605
606        for (point_index, point) in self.points.iter_mut().enumerate() {
607            *point = *transform * *point;
608            union_rect(&mut self.bounds, *point, point_index == 0);
609        }
610    }
611
612    pub fn apply_perspective(&mut self, perspective: &Perspective) {
613        for (point_index, point) in self.points.iter_mut().enumerate() {
614            *point = *perspective * *point;
615            union_rect(&mut self.bounds, *point, point_index == 0);
616        }
617    }
618
619    pub fn dilate(&mut self, amount: Vector2F, orientation: Orientation) {
620        ContourDilator::new(self, amount, orientation).dilate();
621        self.bounds = self.bounds.dilate(amount);
622    }
623
624    fn prepare_for_tiling(&mut self, view_box: RectF) {
625        // Snap points to the view box bounds. This mops up floating point error from the clipping
626        // process.
627        let (mut last_endpoint_index, mut contour_is_monotonic) = (None, true);
628        for point_index in 0..(self.points.len() as u32) {
629            if contour_is_monotonic {
630                if self.point_is_endpoint(point_index) {
631                    if let Some(last_endpoint_index) = last_endpoint_index {
632                        if !self.curve_with_endpoints_is_monotonic(last_endpoint_index,
633                                                                   point_index) {
634                            contour_is_monotonic = false;
635                        }
636                    }
637                    last_endpoint_index = Some(point_index);
638                }
639            }
640        }
641
642        // Convert to monotonic, if necessary.
643        if !contour_is_monotonic {
644            self.make_monotonic();
645        }
646
647        // Update bounds.
648        self.bounds = self
649            .bounds
650            .intersection(view_box)
651            .unwrap_or_else(|| RectF::default());
652    }
653
654    fn make_monotonic(&mut self) {
655        debug!("--- make_monotonic() ---");
656
657        let contour = self.take();
658        self.bounds = contour.bounds;
659
660        let mut last_endpoint_index = None;
661        let input_point_count = contour.points.len() as u32;
662        for point_index in 0..(input_point_count + 1) {
663            if point_index < input_point_count && !contour.point_is_endpoint(point_index) {
664                continue;
665            }
666
667            if let Some(last_endpoint_index) = last_endpoint_index {
668                let position_index = if point_index == input_point_count {
669                    0
670                } else {
671                    point_index
672                };
673                let baseline = LineSegment2F::new(
674                    contour.points[last_endpoint_index as usize],
675                    contour.points[position_index as usize],
676                );
677                let point_count = point_index - last_endpoint_index + 1;
678                if point_count == 3 {
679                    let ctrl_point_index = last_endpoint_index as usize + 1;
680                    let ctrl_position = &contour.points[ctrl_point_index];
681                    handle_cubic(
682                        self,
683                        &Segment::quadratic(baseline, *ctrl_position).to_cubic(),
684                    );
685                } else if point_count == 4 {
686                    let first_ctrl_point_index = last_endpoint_index as usize + 1;
687                    let ctrl_position_0 = &contour.points[first_ctrl_point_index + 0];
688                    let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1];
689                    let ctrl = LineSegment2F::new(*ctrl_position_0, *ctrl_position_1);
690                    handle_cubic(self, &Segment::cubic(baseline, ctrl));
691                }
692
693                self.push_point(
694                    contour.points[position_index as usize],
695                    PointFlags::empty(),
696                    false,
697                );
698            }
699
700            last_endpoint_index = Some(point_index);
701        }
702
703        fn handle_cubic(contour: &mut Contour, segment: &Segment) {
704            debug!("handle_cubic({:?})", segment);
705
706            match segment.as_cubic_segment().y_extrema() {
707                (Some(t0), Some(t1)) => {
708                    let (segments_01, segment_2) = segment.as_cubic_segment().split(t1);
709                    let (segment_0, segment_1) = segments_01.as_cubic_segment().split(t0 / t1);
710                    contour.push_segment(&segment_0, PushSegmentFlags::empty());
711                    contour.push_segment(&segment_1, PushSegmentFlags::empty());
712                    contour.push_segment(&segment_2, PushSegmentFlags::empty());
713                }
714                (Some(t0), None) | (None, Some(t0)) => {
715                    let (segment_0, segment_1) = segment.as_cubic_segment().split(t0);
716                    contour.push_segment(&segment_0, PushSegmentFlags::empty());
717                    contour.push_segment(&segment_1, PushSegmentFlags::empty());
718                }
719                (None, None) => contour.push_segment(segment, PushSegmentFlags::empty()),
720            }
721        }
722    }
723
724    fn curve_with_endpoints_is_monotonic(
725        &self,
726        start_endpoint_index: u32,
727        end_endpoint_index: u32,
728    ) -> bool {
729        let start_position = self.points[start_endpoint_index as usize];
730        let end_position = self.points[end_endpoint_index as usize];
731
732        if start_position.x() <= end_position.x() {
733            for point_index in start_endpoint_index..end_endpoint_index {
734                if self.points[point_index as usize].x() > self.points[point_index as usize + 1].x()
735                {
736                    return false;
737                }
738            }
739        } else {
740            for point_index in start_endpoint_index..end_endpoint_index {
741                if self.points[point_index as usize].x() < self.points[point_index as usize + 1].x()
742                {
743                    return false;
744                }
745            }
746        }
747
748        if start_position.y() <= end_position.y() {
749            for point_index in start_endpoint_index..end_endpoint_index {
750                if self.points[point_index as usize].y() > self.points[point_index as usize + 1].y()
751                {
752                    return false;
753                }
754            }
755        } else {
756            for point_index in start_endpoint_index..end_endpoint_index {
757                if self.points[point_index as usize].y() < self.points[point_index as usize + 1].y()
758                {
759                    return false;
760                }
761            }
762        }
763
764        true
765    }
766
767    // Use this function to keep bounds up to date when mutating paths. See `Outline::transform()`
768    // for an example of use.
769    pub(crate) fn update_bounds(&self, bounds: &mut Option<RectF>) {
770        *bounds = Some(match *bounds {
771            None => self.bounds,
772            Some(bounds) => bounds.union_rect(self.bounds),
773        })
774    }
775}
776
777impl Debug for Contour {
778    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
779        for (segment_index, segment) in self.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT)
780                                            .enumerate() {
781            if segment_index == 0 {
782                write!(
783                    formatter,
784                    "M {} {}",
785                    segment.baseline.from_x(),
786                    segment.baseline.from_y()
787                )?;
788            }
789
790            match segment.kind {
791                SegmentKind::None => {}
792                SegmentKind::Line => {
793                    write!(
794                        formatter,
795                        " L {} {}",
796                        segment.baseline.to_x(),
797                        segment.baseline.to_y()
798                    )?;
799                }
800                SegmentKind::Quadratic => {
801                    write!(
802                        formatter,
803                        " Q {} {} {} {}",
804                        segment.ctrl.from_x(),
805                        segment.ctrl.from_y(),
806                        segment.baseline.to_x(),
807                        segment.baseline.to_y()
808                    )?;
809                }
810                SegmentKind::Cubic => {
811                    write!(
812                        formatter,
813                        " C {} {} {} {} {} {}",
814                        segment.ctrl.from_x(),
815                        segment.ctrl.from_y(),
816                        segment.ctrl.to_x(),
817                        segment.ctrl.to_y(),
818                        segment.baseline.to_x(),
819                        segment.baseline.to_y()
820                    )?;
821                }
822            }
823        }
824
825        if self.closed {
826            write!(formatter, " z")?;
827        }
828
829        Ok(())
830    }
831}
832
833#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
834pub struct PointIndex(u32);
835
836impl PointIndex {
837    #[inline]
838    pub fn new(contour: u32, point: u32) -> PointIndex {
839        debug_assert!(contour <= 0xfff);
840        debug_assert!(point <= 0x000f_ffff);
841        PointIndex((contour << 20) | point)
842    }
843
844    #[inline]
845    pub fn contour(self) -> u32 {
846        self.0 >> 20
847    }
848
849    #[inline]
850    pub fn point(self) -> u32 {
851        self.0 & 0x000f_ffff
852    }
853}
854
855pub struct ContourIter<'a> {
856    contour: &'a Contour,
857    index: u32,
858    flags: ContourIterFlags,
859}
860
861impl<'a> Iterator for ContourIter<'a> {
862    type Item = Segment;
863
864    #[inline]
865    fn next(&mut self) -> Option<Segment> {
866        let contour = self.contour;
867
868        let include_close_segment = self.contour.closed &&
869            !self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT);
870        if (self.index == contour.len() && !include_close_segment) ||
871                self.index == contour.len() + 1 {
872            return None;
873        }
874
875        let point0_index = self.index - 1;
876        let point0 = contour.position_of(point0_index);
877        if self.index == contour.len() {
878            let point1 = contour.position_of(0);
879            self.index += 1;
880            return Some(Segment::line(LineSegment2F::new(point0, point1)));
881        }
882
883        let point1_index = self.index;
884        self.index += 1;
885        let point1 = contour.position_of(point1_index);
886        if contour.point_is_endpoint(point1_index) {
887            return Some(Segment::line(LineSegment2F::new(point0, point1)));
888        }
889
890        let point2_index = self.index;
891        let point2 = contour.position_of(point2_index);
892        self.index += 1;
893        if contour.point_is_endpoint(point2_index) {
894            return Some(Segment::quadratic(LineSegment2F::new(point0, point2), point1));
895        }
896
897        let point3_index = self.index;
898        let point3 = contour.position_of(point3_index);
899        self.index += 1;
900        debug_assert!(contour.point_is_endpoint(point3_index));
901        return Some(Segment::cubic(
902            LineSegment2F::new(point0, point3),
903            LineSegment2F::new(point1, point2),
904        ));
905    }
906}
907
908#[derive(Clone, Copy, Debug, PartialEq)]
909pub enum ArcDirection {
910    CW,
911    CCW,
912}
913
914bitflags! {
915    pub struct ContourIterFlags: u8 {
916        const IGNORE_CLOSE_SEGMENT = 1;
917    }
918}
919
920#[inline]
921pub(crate) fn union_rect(bounds: &mut RectF, new_point: Vector2F, first: bool) {
922    if first {
923        *bounds = RectF::from_points(new_point, new_point);
924    } else {
925        *bounds = bounds.union_point(new_point)
926    }
927}