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                    initial_point = Some(p);
32                    current_point = Some(p);
33                }
34                LinePathCommand::LineTo(p) => {
35                    let p0 = current_point.replace(p).unwrap();
36                    if !self.push_events_for_segment(LineSegment::new(p0, p)) {
37                        return false;
38                    }
39                }
40                LinePathCommand::Close => {
41                    let p = initial_point.take().unwrap();
42                    let p0 = current_point.replace(p).unwrap();
43                    if !self.push_events_for_segment(LineSegment::new(p0, p)) {
44                        return false;
45                    }
46                }
47            }
48            true
49        }){
50            return None
51        };
52        Some(Trapezoidate {
53            trapezoidator: self,
54        })
55    }
56
57    /// Adds events for the given segment to the event queue.
58    fn push_events_for_segment(&mut self, segment: LineSegment) -> bool {
59        // Determine the winding, the leftmost point, and the rightmost point of the segment.
60        //
61        // The winding is used to determine which regions are considered inside, and which are
62        // considered outside. A region is considered inside if its winding is non-zero.
63        // Conceptually, the winding of a region is determined by casting an imaginary ray from
64        // any point inside the region to infinity in any direction, and adding the windings of
65        // all segments that are intersected by the ray. The winding of a segment is +1 if it
66        // intersects the ray from left to right, and -1 if it intersects the ray from right to
67        // left.
68        let (winding, p0, p1) = match segment.p0.partial_cmp(&segment.p1) {
69            None => {
70                // The endpoints of the segment cannot be compared, so the segment is invalid.
71                // This can happen if the semgent has NaN coordinates. In this case, the input
72                // as a whole is invalid, so we bail out early.
73                return false;
74            },
75            Some(Ordering::Less) => (1, segment.p0, segment.p1),
76            Some(Ordering::Equal) => {
77                // The endpoints of the segment are equal, so the segment is empty. Empty segments
78                // do not affect the output of the trapezoidation algorithm, so they can safely be
79                // ignored.
80                return true;
81            }
82            Some(Ordering::Greater) => (-1, segment.p1, segment.p0),
83        };
84        // Add an event to the event queue for the leftmost point of the segment. This is where
85        // the segment starts intersecting the sweepline.
86        self.event_queue.push(Event {
87            point: p0,
88            pending_segment: Some(PendingSegment { winding, p1 }),
89        });
90        // Add an event to the event queue for the rightmost point of the segment. This is where
91        // the segment stops intersecting the sweepline.
92        self.event_queue.push(Event {
93            point: p1,
94            pending_segment: None,
95        });
96        true
97    }
98
99    /// Removes all events at the next point where an event occurs from the event queue.
100    /// 
101    /// Returns the point at which the events occur, or `None` if the event queue is empty.
102    /// Appends the pending segments that start intersecting the sweepline at this point to
103    /// `pending_segments`.
104    fn pop_events_for_next_point(
105        &mut self,
106        pending_segments: &mut Vec<PendingSegment>,
107    ) -> Option<Point> {
108        // Pop an event from the event queue. This will be the first event at the next point.
109        self.event_queue.pop().map(|event| {
110            // If there is a segment that starts intersecting the sweepline at this point, add it
111            // to `pending_segments`.
112            if let Some(pending_segment) = event.pending_segment {
113                pending_segments.push(pending_segment)
114            }
115            // Keep popping events while they occur at the same point as the first one.
116            while let Some(&next_event) = self.event_queue.peek() {
117                if next_event != event {
118                    break;
119                }
120                self.event_queue.pop();
121                // If there is a segment that starts intersecting the sweepline at this point, add
122                // it to `pending_segments`.
123                if let Some(pending_segment) = next_event.pending_segment {
124                    pending_segments.push(pending_segment);
125                }
126            }
127            event.point
128        })
129    }
130
131    /// Handle all events that occur at the given point. `right_segments` is a list of segments that
132    /// start intersecting the sweepline at this point. `trapezoid_segments` is scratch space for a
133    /// list of segments for which we potentially have to generate trapezoids.
134    fn handle_events_for_point<F>(
135        &mut self,
136        point: Point,
137        right_segments: &mut Vec<PendingSegment>,
138        trapezoid_segments: &mut Vec<ActiveSegment>,
139        f: &mut F,
140    ) -> bool
141    where
142        F: FnMut(Trapezoid) -> bool,
143    {
144        // Find the range of active segments that are incident with the given point.
145        let mut incident_segment_range = self.find_incident_segment_range(point);
146        // If there is an active segment that lies below the current point, and the region below it
147        // is considered outside, then this segment is the lower boundary of a trapezoid. We split
148        // the segment where it intersects the sweepline, adding the part on the left to the list of
149        // trapezoid segments, while keeping the part on the right in the list of active segments.
150        if let Some(trapezoid_segment) =
151            self.find_trapezoid_segment_below(point, incident_segment_range.start)
152        {
153            trapezoid_segments.push(trapezoid_segment);
154        }
155        // If there are any active segments that are incident with the given point, we remove them
156        // from the list of active segments, and then split each segment where it intersects the
157        // sweepline, adding the part on the left to the list of trapezoid segments, while adding
158        // the part on the right to the list of right segments.
159        self.remove_incident_segments(
160            point,
161            &mut incident_segment_range,
162            right_segments,
163            trapezoid_segments,
164        );
165        // Sort the right segments by their slope.
166        self.sort_right_segments(point, right_segments);
167        // Insert the right segments into the list of active segments, updating the range of
168        // active segments that are incident with the given point accordingly.
169        self.insert_right_segments(point, &mut incident_segment_range, right_segments);
170        // If there is an active segment that lies above the current point, and the region below it
171        // is considered inside, then this segment is the upper boundary of a trapezoid. We split the
172        // the segment where it intersects the sweepline, adding the part on the left to the list of
173        // trapezoid segments, while generating an event for the part on the right.
174        if let Some(trapezoid_segment) =
175            self.find_trapezoid_segment_above(point, incident_segment_range.end)
176        {
177            trapezoid_segments.push(trapezoid_segment);
178        }
179        // At this point, `trapezoid_segments` contains a list of segments that stop intersecting the
180        // sweepline at the current point, and that potentially form trapezoid boundaries. We generate
181        // trapezoids for these segments, and pass them to the given closure.
182        self.generate_trapezoids(trapezoid_segments, f)
183    }
184
185    /// Finds the range of active segments that are incident with the given point.
186    fn find_incident_segment_range(&self, point: Point) -> Range<usize> {
187        Range {
188            // Find the index of the first active segment that does not lie below the given point.
189            start: self
190                .active_segments
191                .iter()
192                .position(|active_segment| {
193                    active_segment.segment.compare_to_point(point).unwrap() != Ordering::Less
194                })
195                .unwrap_or(self.active_segments.len()),
196            // Find the index of the first active segment that lies above the given point.
197            end: self
198                .active_segments
199                .iter()
200                .rposition(|active_segment| {
201                    active_segment.segment.compare_to_point(point).unwrap() != Ordering::Greater
202                })
203                .map_or(0, |index| index + 1),
204        }
205    }
206
207    // Finds the first active segment that lies below the given point. If such a segment exists,
208    // and the region below it is considered outside, then this segment is the lower boundary of a
209    // trapezoid. We split the segment where it intersects the sweepline, keeping the part on the
210    // right in the list of active segments, and returning the part on the left.
211    fn find_trapezoid_segment_below(
212        &mut self,
213        point: Point,
214        incident_segment_start: usize,
215    ) -> Option<ActiveSegment> {
216        if incident_segment_start == 0
217            || !self.active_segments[incident_segment_start - 1].region_above.is_inside {
218            return None;
219        }
220        let intersection = self.active_segments[incident_segment_start - 1]
221            .segment
222            .intersect_with_vertical_line(point.x)
223            .unwrap_or(point);
224        self.active_segments[incident_segment_start - 1].split_left_mut(intersection)
225    }
226
227    // Removes all active segments that are incident with the given point from the list of active
228    // segments, and then splits each segment where it intersects the sweepline, adding the part
229    // on the left to the list of trapezoid segments, while adding the part on the right to the
230    // list of right segments.
231    fn remove_incident_segments(
232        &mut self,
233        point: Point,
234        incident_segment_range: &mut Range<usize>,
235        right_segments: &mut Vec<PendingSegment>,
236        trapezoid_segments: &mut Vec<ActiveSegment>,
237    ) {
238        trapezoid_segments.extend(
239            Iterator::map(
240                self.active_segments.drain(incident_segment_range.clone()),
241                |mut active_segment| {
242                    if let Some(pending_segment) = active_segment.split_right_mut(point) {
243                        right_segments.push(pending_segment);
244                    }
245                    active_segment
246                },
247            )
248            .filter(|active_segment| active_segment.segment.p0.x != active_segment.segment.p1.x),
249        );
250        incident_segment_range.end = incident_segment_range.start;
251    }
252
253    /// Sorts the given list of right segments by their slope, using the given point as the leftmost
254    /// endpoint.
255    fn sort_right_segments(&mut self, point: Point, right_segments: &mut Vec<PendingSegment>) {
256        right_segments.sort_by(|&right_segment_0, &right_segment_1| {
257            right_segment_0.compare(right_segment_1, point).unwrap()
258        });
259        let mut index_0 = 0;
260        for index_1 in 1..right_segments.len() {
261            let right_segment_1 = right_segments[index_1];
262            let right_segment_0 = &mut right_segments[index_0];
263            if right_segment_0.overlaps(right_segment_1, point) {
264                if let Some(event) = right_segment_0.splice_mut(right_segment_1) {
265                    self.event_queue.push(event);
266                }
267            } else {
268                index_0 += 1;
269                right_segments[index_0] = right_segment_1;
270            }
271        }
272        right_segments.truncate(index_0 + 1);
273    }
274
275    // Inserts the given right segments into the list of active segments, updating the range of
276    // active segments that are incident with the given point accordingly.
277    fn insert_right_segments(
278        &mut self,
279        point: Point,
280        incident_segment_range: &mut Range<usize>,
281        right_segments: &[PendingSegment],
282    ) {
283        let mut lower_region = if incident_segment_range.end == 0 {
284            Region {
285                is_inside: false,
286                winding: 0,
287            }
288        } else {
289            self.active_segments[incident_segment_range.end - 1].region_above
290        };
291        self.active_segments.splice(
292            incident_segment_range.end..incident_segment_range.end,
293            Iterator::map(right_segments.iter(), |right_segment| {
294                let upper_region = {
295                    let winding = lower_region.winding + right_segment.winding;
296                    Region {
297                        is_inside: winding != 0,
298                        winding,
299                    }
300                };
301                let right_segment = ActiveSegment {
302                    winding: right_segment.winding,
303                    segment: LineSegment::new(point, right_segment.p1),
304                    region_above: upper_region,
305                };
306                lower_region = upper_region;
307                right_segment
308            }),
309        );
310        incident_segment_range.end += right_segments.len();
311    }
312
313    // Finds the first active segment that lies above the given point. If such a segment exists,
314    // and the region below it is considered inside, then this segment is the upper boundary of a
315    // trapezoid. We split the segment where it intersects the sweepline, generating an event for
316    // the part on the right, and returning the part on the left.
317    fn find_trapezoid_segment_above(
318        &mut self,
319        point: Point,
320        incident_segment_end: usize,
321    ) -> Option<ActiveSegment> {
322        if incident_segment_end == self.active_segments.len()
323            || incident_segment_end == 0 
324            || !self.active_segments[incident_segment_end - 1].region_above.is_inside
325        {
326            return None;
327        }
328        let intersection = self.active_segments[incident_segment_end]
329            .segment
330            .intersect_with_vertical_line(point.x)
331            .unwrap();
332        if let Some(pending_segment) =
333            self.active_segments[incident_segment_end].split_right_mut(intersection)
334        {
335            self.event_queue.push(Event {
336                point: intersection,
337                pending_segment: Some(pending_segment),
338            });
339        }
340        Some(self.active_segments[incident_segment_end])
341    }
342
343    fn generate_trapezoids<F>(&self, trapezoid_segments: &[ActiveSegment], f: &mut F) -> bool
344    where
345        F: FnMut(Trapezoid) -> bool,
346    {
347        for trapezoid_segment_pair in trapezoid_segments.windows(2) {
348            if !trapezoid_segment_pair[0].region_above.is_inside {
349                continue;
350            }
351            let lower_segment = trapezoid_segment_pair[0].segment;
352            let upper_segment = trapezoid_segment_pair[1].segment;
353            if !f(Trapezoid {
354                xs: [lower_segment.p0.x as f32, lower_segment.p1.x as f32],
355                ys: [
356                    lower_segment.p0.y as f32,
357                    lower_segment.p1.y as f32,
358                    upper_segment.p0.y as f32,
359                    upper_segment.p1.y as f32,
360                ],
361            }) {
362                return false;
363            }
364        }
365        true
366    }
367}
368
369/// An iterator over trapezoids corresponding to the given iterator over line path commands.
370#[derive(Debug)]
371pub struct Trapezoidate<'a> {
372    trapezoidator: &'a mut Trapezoidator,
373}
374
375impl<'a> InternalIterator for Trapezoidate<'a> {
376    type Item = Trapezoid;
377
378    fn for_each<F>(self, f: &mut F) -> bool
379    where
380        F: FnMut(Trapezoid) -> bool,
381    {
382        let mut right_segments = Vec::new();
383        let mut trapezoid_segments = Vec::new();
384        while let Some(point) = self.trapezoidator.pop_events_for_next_point(&mut right_segments) {
385            let ok = self.trapezoidator.handle_events_for_point(
386                point,
387                &mut right_segments,
388                &mut trapezoid_segments,
389                f,
390            );
391            right_segments.clear();
392            trapezoid_segments.clear();
393            if !ok {
394                return false;
395            }
396        }
397        true
398    }
399}
400
401// An event in the event queue.
402#[derive(Clone, Copy, Debug)]
403struct Event {
404    // The point at which the event occurs.
405    point: Point,
406    // The pending segment that starts intersecting the sweepline at this point, if any.
407    pending_segment: Option<PendingSegment>,
408}
409
410impl Eq for Event {}
411
412impl Ord for Event {
413    fn cmp(&self, other: &Event) -> Ordering {
414        self.point.partial_cmp(&other.point).unwrap().reverse()
415    }
416}
417
418impl PartialEq for Event {
419    fn eq(&self, other: &Event) -> bool {
420        self.cmp(other) == Ordering::Equal
421    }
422}
423
424impl PartialOrd for Event {
425    fn partial_cmp(&self, other: &Event) -> Option<Ordering> {
426        Some(self.cmp(other))
427    }
428}
429
430/// A segment that is pending insertion into the list of active segments.
431///
432/// We only store the rightmost endpoint for these segments, since the leftmost endpoint is
433/// determined implicitly by the point at which the event that inserts the segment occurs.
434#[derive(Clone, Copy, Debug, PartialEq)]
435struct PendingSegment {
436    // The winding of the segment.
437    winding: i32,
438    // The rightmost endpoint of the segment.
439    p1: Point,
440}
441
442impl PendingSegment {
443    fn to_segment(self, p0: Point) -> LineSegment {
444        LineSegment::new(p0, self.p1)
445    }
446
447    fn overlaps(self, other: PendingSegment, p0: Point) -> bool {
448        self.compare(other, p0) == Some(Ordering::Equal)
449    }
450
451    fn compare(self, other: PendingSegment, p0: Point) -> Option<Ordering> {
452        if self.p1 <= other.p1 {
453            other
454                .to_segment(p0)
455                .compare_to_point(self.p1)
456                .map(|ordering| ordering.reverse())
457        } else {
458            self.to_segment(p0).compare_to_point(other.p1)
459        }
460    }
461
462    fn splice_mut(&mut self, mut other: Self) -> Option<Event> {
463        if other.p1 < self.p1 {
464            mem::swap(self, &mut other);
465        }
466        self.winding += other.winding;
467        if self.p1 == other.p1 {
468            return None;
469        }
470        Some(Event {
471            point: self.p1,
472            pending_segment: Some(other),
473        })
474    }
475}
476
477/// A segment that currently intersects the sweepline,
478#[derive(Clone, Copy, Debug, PartialEq)]
479struct ActiveSegment {
480    winding: i32,
481    segment: LineSegment,
482    region_above: Region,
483}
484
485impl ActiveSegment {
486    // Splits this segment at the given point, returning the part on the left.
487    fn split_left_mut(&mut self, p: Point) -> Option<ActiveSegment> {
488        let p0 = self.segment.p0;
489        if p == p0 {
490            return None;
491        }
492        self.segment.p0 = p;
493        Some(ActiveSegment {
494            winding: self.winding,
495            segment: LineSegment::new(p0, p),
496            region_above: self.region_above,
497        })
498    }
499
500    // Splits this segment at the given point, returning the part on the right.
501    fn split_right_mut(&mut self, p: Point) -> Option<PendingSegment> {
502        let p1 = self.segment.p1;
503        if p == p1 {
504            return None;
505        }
506        self.segment.p1 = p;
507        Some(PendingSegment {
508            winding: self.winding,
509            p1,
510        })
511    }
512}
513
514#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
515struct Region {
516    is_inside: bool,
517    winding: i32,
518}
519
520#[cfg(test)]
521mod tests {
522    use super::*;
523    use crate::path::{Path, PathIterator};
524
525    #[test]
526    fn test_square() {
527        let mut path = Path::new();
528        path.move_to(Point::new(0.0, 0.0));
529        path.line_to(Point::new(1.0, 0.0));
530        path.line_to(Point::new(1.0, 1.0));
531        path.line_to(Point::new(0.0, 1.0));
532        path.close();
533        let mut trapezoidator = Trapezoidator::new();
534        let trapezoids: Vec<_> = trapezoidator
535            .trapezoidate(path.commands().linearize(0.1))
536            .unwrap()
537            .collect();
538        assert_eq!(trapezoids, [
539            Trapezoid { xs: [0.0, 1.0], ys: [0.0, 0.0, 1.0, 1.0] }
540        ]);
541    }
542}