makepad_vector/trapezoidator/
mod.rs

1use crate::geometry::{LineSegment, Point, Trapezoid};
2use crate::internal_iter::InternalIterator;
3use crate::path::{LinePathCommand, LinePathIterator};
4use std::cmp::Ordering;
5use std::collections::BinaryHeap;
6use std::mem;
7use std::ops::Range;
8
9/// Converts a sequence of line path commands to a sequence of trapezoids. The line path commands
10/// should define a set of closed contours.
11#[derive(Clone, Debug, Default)]
12pub struct Trapezoidator {
13    event_queue: BinaryHeap<Event>,
14    active_segments: Vec<ActiveSegment>,
15}
16
17impl Trapezoidator {
18    /// Creates a new trapezoidator.
19    pub fn new() -> Trapezoidator {
20        Trapezoidator::default()
21    }
22
23    /// Returns an iterator over trapezoids corresponding to the given iterator over line path
24    /// commands.
25    pub fn trapezoidate<P: LinePathIterator>(&mut self, path: P)->Option<Trapezoidate>{
26        let mut initial_point = None;
27        let mut current_point = None;
28        if !path.for_each(&mut |command| {
29            match command {
30                LinePathCommand::MoveTo(p) => {
31                    //assert!(initial_point == current_point);
32                    initial_point = Some(p);
33                    current_point = Some(p);
34                }
35                LinePathCommand::LineTo(p) => {
36                    let p0 = current_point.replace(p).unwrap();
37                    if self.push_events_for_segment(LineSegment::new(p0, p)){
38                        return false
39                    }
40                }
41                LinePathCommand::Close => {
42                    let p = initial_point.take().unwrap();
43                    let p0 = current_point.replace(p).unwrap();
44                    if self.push_events_for_segment(LineSegment::new(p0, p)){
45                        return false
46                    }
47                }
48            }
49            true
50        }){
51            return None
52        };
53        Some(Trapezoidate {
54            trapezoidator: self,
55        })
56    }
57
58    fn push_events_for_segment(&mut self, segment: LineSegment)->bool {
59        let (winding, p0, p1) = match segment.p0.partial_cmp(&segment.p1) {
60            None => return true,
61            Some(Ordering::Less) => (1, segment.p0, segment.p1),
62            Some(Ordering::Equal) => return false,
63            Some(Ordering::Greater) => (-1, segment.p1, segment.p0),
64        };
65        self.event_queue.push(Event {
66            point: p0,
67            pending_segment: Some(PendingSegment { winding, p1 }),
68        });
69        self.event_queue.push(Event {
70            point: p1,
71            pending_segment: None,
72        });
73        false
74    }
75
76    fn pop_events_for_point(
77        &mut self,
78        pending_segments: &mut Vec<PendingSegment>,
79    ) -> Option<Point> {
80        self.event_queue.pop().map(|event| {
81            if let Some(pending_segment) = event.pending_segment {
82                pending_segments.push(pending_segment)
83            }
84            while let Some(&next_event) = self.event_queue.peek() {
85                if next_event != event {
86                    break;
87                }
88                self.event_queue.pop();
89                if let Some(pending_segment) = next_event.pending_segment {
90                    pending_segments.push(pending_segment);
91                }
92            }
93            event.point
94        })
95    }
96
97    fn handle_events_for_point<F>(
98        &mut self,
99        point: Point,
100        right_segments: &mut Vec<PendingSegment>,
101        trapezoid_segments: &mut Vec<ActiveSegment>,
102        f: &mut F,
103    ) -> bool
104    where
105        F: FnMut(Trapezoid) -> bool,
106    {
107        let mut incident_segment_range = self.find_incident_segment_range(point);
108        if let Some(trapezoid_segment) =
109            self.find_lower_trapezoid_segment(point, incident_segment_range.start)
110        {
111            trapezoid_segments.push(trapezoid_segment);
112        }
113        self.remove_incident_segments(
114            point,
115            &mut incident_segment_range,
116            right_segments,
117            trapezoid_segments,
118        );
119        self.sort_right_segments(point, right_segments);
120        self.insert_right_segments(point, &mut incident_segment_range, right_segments);
121        if let Some(trapezoid_segment) =
122            self.find_upper_trapezoid_segment(point, incident_segment_range.end)
123        {
124            trapezoid_segments.push(trapezoid_segment);
125        }
126        self.generate_trapezoids(trapezoid_segments, f)
127    }
128
129    fn find_incident_segment_range(&self, point: Point) -> Range<usize> {
130        Range {
131            start: self
132                .active_segments
133                .iter()
134                .position(|active_segment| {
135                    active_segment.segment.compare_to_point(point).unwrap() != Ordering::Less
136                })
137                .unwrap_or(self.active_segments.len()),
138            end: self
139                .active_segments
140                .iter()
141                .rposition(|active_segment| {
142                    active_segment.segment.compare_to_point(point).unwrap() != Ordering::Greater
143                })
144                .map_or(0, |index| index + 1),
145        }
146    }
147
148    fn find_lower_trapezoid_segment(
149        &mut self,
150        point: Point,
151        incident_segment_start: usize,
152    ) -> Option<ActiveSegment> {
153        if 0 == incident_segment_start
154            || !self.active_segments[incident_segment_start - 1]
155                .upper_region
156                .is_inside
157        {
158            return None;
159        }
160        let intersection = self.active_segments[incident_segment_start - 1]
161            .segment
162            .intersect_with_vertical_line(point.x)
163            .unwrap();
164        self.active_segments[incident_segment_start - 1].split_front_mut(intersection)
165    }
166
167    fn remove_incident_segments(
168        &mut self,
169        point: Point,
170        incident_segment_range: &mut Range<usize>,
171        pending_segments: &mut Vec<PendingSegment>,
172        trapezoid_segments: &mut Vec<ActiveSegment>,
173    ) {
174        trapezoid_segments.extend(
175            Iterator::map(
176                self.active_segments.drain(incident_segment_range.clone()),
177                |mut active_segment| {
178                    if let Some(pending_segment) = active_segment.split_back_mut(point) {
179                        pending_segments.push(pending_segment);
180                    }
181                    active_segment
182                },
183            )
184            .filter(|active_segment| active_segment.segment.p0.x != active_segment.segment.p1.x),
185        );
186        incident_segment_range.end = incident_segment_range.start;
187    }
188
189    fn sort_right_segments(&mut self, point: Point, right_segments: &mut Vec<PendingSegment>) {
190        right_segments.sort_by(|&right_segment_0, &right_segment_1| {
191            right_segment_0.compare(right_segment_1, point).unwrap()
192        });
193        let mut index_0 = 0;
194        for index_1 in 1..right_segments.len() {
195            let right_segment_1 = right_segments[index_1];
196            let right_segment_0 = &mut right_segments[index_0];
197            if right_segment_0.overlaps(right_segment_1, point) {
198                if let Some(event) = right_segment_0.splice_mut(right_segment_1) {
199                    self.event_queue.push(event);
200                }
201            } else {
202                index_0 += 1;
203                right_segments[index_0] = right_segment_1;
204            }
205        }
206        right_segments.truncate(index_0 + 1);
207    }
208
209    fn insert_right_segments(
210        &mut self,
211        point: Point,
212        incident_segment_range: &mut Range<usize>,
213        right_segments: &[PendingSegment],
214    ) {
215        let mut lower_region = if incident_segment_range.end == 0 {
216            Region {
217                is_inside: false,
218                winding: 0,
219            }
220        } else {
221            self.active_segments[incident_segment_range.end - 1].upper_region
222        };
223        self.active_segments.splice(
224            incident_segment_range.end..incident_segment_range.end,
225            Iterator::map(right_segments.iter(), |right_segment| {
226                let upper_region = {
227                    let winding = lower_region.winding + right_segment.winding;
228                    Region {
229                        is_inside: winding != 0,
230                        winding,
231                    }
232                };
233                let right_segment = ActiveSegment {
234                    winding: right_segment.winding,
235                    segment: LineSegment::new(point, right_segment.p1),
236                    upper_region,
237                };
238                lower_region = upper_region;
239                right_segment
240            }),
241        );
242        incident_segment_range.end += right_segments.len();
243    }
244
245    fn find_upper_trapezoid_segment(
246        &mut self,
247        point: Point,
248        incident_segment_end: usize,
249    ) -> Option<ActiveSegment> {
250        if 0 == incident_segment_end
251            || !self.active_segments[incident_segment_end - 1]
252                .upper_region
253                .is_inside
254        {
255            return None;
256        }
257        let intersection = self.active_segments[incident_segment_end]
258            .segment
259            .intersect_with_vertical_line(point.x)
260            .unwrap();
261        if let Some(pending_segment) =
262            self.active_segments[incident_segment_end].split_back_mut(intersection)
263        {
264            self.event_queue.push(Event {
265                point: intersection,
266                pending_segment: Some(pending_segment),
267            });
268        }
269        Some(self.active_segments[incident_segment_end])
270    }
271
272    fn generate_trapezoids<F>(&self, trapezoid_segments: &[ActiveSegment], f: &mut F) -> bool
273    where
274        F: FnMut(Trapezoid) -> bool,
275    {
276        for trapezoid_segment_pair in trapezoid_segments.windows(2) {
277            if !trapezoid_segment_pair[0].upper_region.is_inside {
278                continue;
279            }
280            let lower_segment = trapezoid_segment_pair[0].segment;
281            let upper_segment = trapezoid_segment_pair[1].segment;
282            if !f(Trapezoid {
283                xs: [lower_segment.p0.x as f32, lower_segment.p1.x as f32],
284                ys: [
285                    lower_segment.p0.y as f32,
286                    lower_segment.p1.y as f32,
287                    upper_segment.p0.y as f32,
288                    upper_segment.p1.y as f32,
289                ],
290            }) {
291                return false;
292            }
293        }
294        true
295    }
296}
297
298/// An iterator over trapezoids corresponding to the given iterator over line path commands.
299#[derive(Debug)]
300pub struct Trapezoidate<'a> {
301    trapezoidator: &'a mut Trapezoidator,
302}
303
304impl<'a> InternalIterator for Trapezoidate<'a> {
305    type Item = Trapezoid;
306
307    fn for_each<F>(self, f: &mut F) -> bool
308    where
309        F: FnMut(Trapezoid) -> bool,
310    {
311        let mut right_segments = Vec::new();
312        let mut trapezoid_segments = Vec::new();
313        while let Some(point) = self.trapezoidator.pop_events_for_point(&mut right_segments) {
314            let ok = self.trapezoidator.handle_events_for_point(
315                point,
316                &mut right_segments,
317                &mut trapezoid_segments,
318                f,
319            );
320            right_segments.clear();
321            trapezoid_segments.clear();
322            if !ok {
323                return false;
324            }
325        }
326        true
327    }
328}
329
330#[derive(Clone, Copy, Debug)]
331struct Event {
332    point: Point,
333    pending_segment: Option<PendingSegment>,
334}
335
336impl Eq for Event {}
337
338impl Ord for Event {
339    fn cmp(&self, other: &Event) -> Ordering {
340        self.point.partial_cmp(&other.point).unwrap().reverse()
341    }
342}
343
344impl PartialEq for Event {
345    fn eq(&self, other: &Event) -> bool {
346        self.cmp(other) == Ordering::Equal
347    }
348}
349
350impl PartialOrd for Event {
351    fn partial_cmp(&self, other: &Event) -> Option<Ordering> {
352        Some(self.cmp(other))
353    }
354}
355
356#[derive(Clone, Copy, Debug, PartialEq)]
357struct PendingSegment {
358    winding: i32,
359    p1: Point,
360}
361
362impl PendingSegment {
363    fn to_segment(self, p0: Point) -> LineSegment {
364        LineSegment::new(p0, self.p1)
365    }
366
367    fn overlaps(self, other: PendingSegment, p0: Point) -> bool {
368        self.compare(other, p0) == Some(Ordering::Equal)
369    }
370
371    fn compare(self, other: PendingSegment, p0: Point) -> Option<Ordering> {
372        if self.p1 <= other.p1 {
373            other
374                .to_segment(p0)
375                .compare_to_point(self.p1)
376                .map(|ordering| ordering.reverse())
377        } else {
378            self.to_segment(p0).compare_to_point(other.p1)
379        }
380    }
381
382    fn splice_mut(&mut self, mut other: Self) -> Option<Event> {
383        if other.p1 < self.p1 {
384            mem::swap(self, &mut other);
385        }
386        self.winding += other.winding;
387        if self.p1 == other.p1 {
388            return None;
389        }
390        Some(Event {
391            point: self.p1,
392            pending_segment: Some(other),
393        })
394    }
395}
396
397#[derive(Clone, Copy, Debug, PartialEq)]
398struct ActiveSegment {
399    winding: i32,
400    segment: LineSegment,
401    upper_region: Region,
402}
403
404impl ActiveSegment {
405    fn split_front_mut(&mut self, p: Point) -> Option<ActiveSegment> {
406        let p0 = self.segment.p0;
407        if p == p0 {
408            return None;
409        }
410        self.segment.p0 = p;
411        Some(ActiveSegment {
412            winding: self.winding,
413            segment: LineSegment::new(p0, p),
414            upper_region: self.upper_region,
415        })
416    }
417
418    fn split_back_mut(&mut self, p: Point) -> Option<PendingSegment> {
419        let p1 = self.segment.p1;
420        if p == p1 {
421            return None;
422        }
423        self.segment.p1 = p;
424        Some(PendingSegment {
425            winding: self.winding,
426            p1,
427        })
428    }
429}
430
431#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
432struct Region {
433    is_inside: bool,
434    winding: i32,
435}
436/*
437#[cfg(test)]
438mod tests {
439    use super::*;
440    use path::{Path, PathIterator};
441
442    #[test]
443    fn test() {
444        let mut path = Path::new();
445        path.move_to(Point::new(1.0, 0.0));
446        path.quadratic_to(Point::new(1.0, 1.0), Point::new(0.0, 1.0));
447        path.quadratic_to(Point::new(-1.0, 1.0), Point::new(-1.0, 0.0));
448        path.quadratic_to(Point::new(-1.0, -1.0), Point::new(0.0, -1.0));
449        path.quadratic_to(Point::new(-1.0, -1.0), Point::new(0.0, -1.0));
450        path.close();
451        assert_eq!(
452            Trapezoidator::new()
453                .trapezoidate(path.commands().linearize(0.1))
454                .collect::<Vec<_>>(),
455            [
456                Trapezoid {
457                    xs: [-1.0, -0.9375],
458                    ys: [0.0, -0.4375, 0.0, 0.4375]
459                },
460                Trapezoid {
461                    xs: [-0.9375, -0.75],
462                    ys: [-0.4375, -0.75, 0.4375, 0.75]
463                },
464                Trapezoid {
465                    xs: [-0.75, -0.4375],
466                    ys: [-0.75, -0.9375, 0.75, 0.9375]
467                },
468                Trapezoid {
469                    xs: [-0.4375, 0.0],
470                    ys: [-0.9375, -1.0, 0.9375, 1.0]
471                },
472                Trapezoid {
473                    xs: [0.0, 0.4375],
474                    ys: [-1.0, -0.5625, 1.0, 0.9375]
475                },
476                Trapezoid {
477                    xs: [0.4375, 0.75],
478                    ys: [-0.5625, -0.25, 0.9375, 0.75]
479                },
480                Trapezoid {
481                    xs: [0.75, 0.9375],
482                    ys: [-0.25, -0.0625, 0.75, 0.4375]
483                },
484                Trapezoid {
485                    xs: [0.9375, 1.0],
486                    ys: [-0.0625, 0.0, 0.4375, 0.0]
487                }
488            ]
489        );
490    }
491}*/