iron_shapes_algorithms/
rectangle_decomposition.rs

1// SPDX-FileCopyrightText: 2022 Thomas Kramer
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5//! Decompose rectilinear polygons into axis-aligned rectangles.
6
7use iron_shapes::prelude::{Angle, CoordinateType, REdge, Rect, RotateOrtho};
8use itertools::Itertools;
9use num_traits::{Bounded, Zero};
10
11/// Decompose a set of manhattanized polygons into non-overlapping horizontal rectangles.
12/// The polygons in form of an iterator over all the edges.
13/// A point is considered inside the polygon when the polygon wraps around it a non-zero number of times.
14///
15/// # Example
16/// ```
17///     use iron_shapes::prelude::*;
18///     use iron_shapes_algorithms::rectangle_decomposition::decompose_rectangles;
19///     // Use a polygon like the following.
20///     //    +--+
21///     //    |  |
22///     // +--+  |
23///     // |     |
24///     // +-----+
25///     let poly = SimpleRPolygon::try_new([
26///         (0, 0), (2, 0), (2, 2), (1, 2), (1, 1), (0, 1)
27///     ]).unwrap();
28///
29///     // Decompose the polygon into non-overlapping horizontal rectangles.
30///     let rects = decompose_rectangles(poly.edges());
31///     assert_eq!(rects, vec![Rect::new((0, 0), (2, 1)), Rect::new((1, 1), (2, 2))]);
32/// ```
33pub fn decompose_rectangles<T, I>(redges: I) -> Vec<Rect<T>>
34where
35    T: Ord + Bounded + Copy + Zero,
36    I: Iterator<Item = REdge<T>>,
37{
38    decompose_disjoint_and_maximal_rectangles(redges, true, false)
39}
40
41/// Same as `decompose_rectangles` but create vertical slices.
42/// Rotate the edges before and after decomposition.
43pub fn decompose_rectangles_vertical<T, I>(redges: I) -> Vec<Rect<T>>
44where
45    T: CoordinateType + Ord + Bounded + Copy + Zero,
46    I: Iterator<Item = REdge<T>>,
47{
48    let redges = redges.map(|e| e.rotate_ortho(Angle::R90));
49
50    let sliced = decompose_rectangles(redges);
51
52    sliced
53        .into_iter()
54        .map(|r| r.rotate_ortho(Angle::R270))
55        .collect()
56}
57
58/// Decompose a set of manhattanized polygons into maximal rectangles.
59/// The polygons in form of an iterator over all the edges.
60/// A point is considered inside the polygon when the polygon wraps around it a non-zero number of times.
61///
62/// # Example
63/// ```
64///     use iron_shapes::prelude::*;
65///     use iron_shapes_algorithms::rectangle_decomposition::decompose_maximal_rectangles;
66///     // Use a polygon like the following.
67///     //    +--+
68///     //    |  |
69///     // +--+  |
70///     // |     |
71///     // +-----+
72///     let poly = SimpleRPolygon::try_new([
73///         (0, 0), (2, 0), (2, 2), (1, 2), (1, 1), (0, 1)
74///     ]).unwrap();
75///
76///     // Decompose the polygon into non-overlapping horizontal rectangles.
77///     let rects = decompose_maximal_rectangles(poly.edges());
78///     assert_eq!(rects, vec![Rect::new((0, 0), (2, 1)), Rect::new((1, 0), (2, 2))]);
79/// ```
80pub fn decompose_maximal_rectangles<T, I>(redges: I) -> Vec<Rect<T>>
81where
82    T: Ord + Bounded + Copy + Zero,
83    I: Iterator<Item = REdge<T>>,
84{
85    decompose_disjoint_and_maximal_rectangles(redges, false, true)
86}
87
88/// Decompose a set of manhattanized polygons into rectangles.
89/// The polygons in form of an iterator over all the edges.
90/// A point is considered inside the polygon when the polygon wraps around it a non-zero number of times.
91///
92/// # Parameters
93/// * `find_disjoint_rectangles`: If true, output horizontal slices which are not overlapping.
94/// * `find_maximal_rectangles`: If true, also output all maximal axis-aligned rectangles which fit in the polygon. They are overlapping.
95fn decompose_disjoint_and_maximal_rectangles<T, I>(
96    redges: I,
97    find_disjoint_rectangles: bool,
98    find_maximal_rectangles: bool,
99) -> Vec<Rect<T>>
100where
101    T: Ord + Bounded + Copy + Zero,
102    I: Iterator<Item = REdge<T>>,
103{
104    // Sweep through the vertical edges from left to right. Keep track of 'open' rectangles.
105    // A rectangle is opened when a left boundary is encountered and closed when a right boundary is encountered.
106    // Construct a rectangle once a right boundary is encountered.
107
108    {
109        // Extract the vertical edges.
110        let vertical_edges: Vec<_> = redges
111            .filter(|e| e.is_vertical() && e.start != e.end)
112            .map(|e| {
113                let is_left = e.start > e.end;
114                if is_left {
115                    (e.reversed(), true)
116                } else {
117                    (e, false)
118                }
119            })
120            // Sort edges from left to right, then opening (left) edges before closing (right) edges, then by the y-coordinate of start and end point.
121            .sorted_by_key(|(e, is_left)| (e.offset, !is_left, e.start, e.end))
122            .collect();
123
124        // Store the open rectangle as a tuple (y-start, y-end, inside count, x-position of opening vertical edge).
125        let mut open_rects: Vec<(T, T, isize, T)> =
126            vec![(T::min_value(), T::max_value(), 0, T::min_value())];
127        let mut results = Vec::new();
128
129        // Return the position of the new entry.
130        fn split_intervals<T: PartialOrd + Copy>(
131            intervals: &mut Vec<(T, T, isize, T)>,
132            split_location: T,
133        ) -> usize {
134            // Find the interval which contains the split location.
135            let (pos, &(a, b, value, x)) = intervals
136                .iter()
137                .enumerate()
138                .find(|(_pos, &(a, b, _val, _x))| a <= split_location && split_location < b)
139                .expect("Intervals must span the whole value range without gap.");
140
141            if split_location == a || split_location == b {
142                // Split location is equal to an end-point of the interval. No need to split there.
143                pos
144            } else {
145                // Split location is inside the interval. Split the interval.
146                let i1 = (a, split_location, value, x);
147                let i2 = (split_location, b, value, x);
148                intervals[pos] = i2;
149                intervals.insert(pos, i1);
150                pos + 1
151            }
152        }
153
154        // Merge neighbouring intervals with the same value.
155        fn merge_intervals<T: PartialEq + Copy>(intervals: &mut Vec<(T, T, isize, T)>) {
156            debug_assert!(intervals.len() >= 1);
157            let mut write_index = 0;
158            let mut value = (intervals[0].2, intervals[0].3);
159            for i in 1..intervals.len() {
160                let (start, end, count, x) = intervals[i];
161                let current_value = (count, x);
162                intervals[write_index].1 = end;
163
164                if current_value != value {
165                    intervals[write_index].1 = start;
166                    write_index += 1;
167                    intervals[write_index] = intervals[i];
168                    value = current_value;
169                } else {
170                    // Merge the intervals.
171                }
172            }
173
174            // Crop the vector to the right size.
175            intervals.truncate(write_index + 1);
176        }
177
178        // Process all vertical edges.
179        for (current_edge, is_left) in vertical_edges {
180            debug_assert!(current_edge.start < current_edge.end);
181
182            {
183                let pos = split_intervals(&mut open_rects, current_edge.start);
184                split_intervals(&mut open_rects, current_edge.end);
185
186                // Find all newly opened rectangles and store the x position of their left edge.
187                open_rects
188                    .iter_mut()
189                    .skip(pos) // Skip intervals which come before the current interval.
190                    .take_while(|(_a, b, _value, _x)| b <= &current_edge.end) // Find intervals which overlap with the current interval.
191                    .filter(|(a, _b, value, _x)| a >= &current_edge.start && *value == 0)
192                    .for_each(|(_a, _b, _value, x)| {
193                        *x = current_edge.offset;
194                    });
195            }
196
197            let increment = if is_left {
198                // Enter rectangle.
199                1
200            } else {
201                // Exit rectangle.
202                -1
203            };
204
205            if find_disjoint_rectangles {
206                // Create non-overlapping rectangles.
207                // Find rectangles that are closed by this boundary.
208                // Find this by computing the intersection of the y-interval of the right boundary
209                // with all open intervals that are about to be closed (which have value = -increment).
210
211                let increment_inv = -increment;
212                let closed_rects = open_rects
213                    .iter()
214                    .take_while(|(_a, b, _value, _x)| b <= &current_edge.end)
215                    .filter(|(a, _b, value, _x)| {
216                        a >= &current_edge.start && value == &increment_inv
217                    })
218                    .map(|&(a, b, _value, x_start)| {
219                        // Compute the intersection of the intervals [a, b] and [e.start, e.end].
220                        let y_start = a.max(current_edge.start);
221                        let y_end = b.min(current_edge.end);
222                        let x_end = current_edge.offset;
223                        debug_assert!(x_start != T::min_value());
224                        Rect::new((x_start, y_start), (x_end, y_end))
225                    });
226
227                results.extend(closed_rects);
228            }
229
230            if find_maximal_rectangles {
231                // Create maximal rectangles which likely overlap.
232
233                let increment_inv = -increment;
234                let closed_rects: Vec<_> = open_rects
235                    .iter()
236                    .take_while(|(_a, b, _value, _x)| b <= &current_edge.end)
237                    .filter(|(a, _b, value, _x)| {
238                        a >= &current_edge.start && value == &increment_inv
239                    })
240                    .collect();
241
242                // Find x coordinates of all left edges.
243                let mut left_edge_xs: Vec<_> =
244                    closed_rects.iter().map(|(_a, _b, _value, x)| *x).collect();
245
246                left_edge_xs.sort_unstable();
247                left_edge_xs.dedup();
248
249                let x_end = current_edge.offset;
250
251                for x_start in left_edge_xs {
252                    // Find maximum open rectangles which are on the right of `x_start`.
253
254                    let y_intervals = open_rects
255                        .iter()
256                        .filter(|(_a, _b, value, _x)| value == &increment_inv)
257                        .filter(|(_, _, _, x)| x <= &x_start)
258                        .map(|&(a, b, _, _)| (a, b));
259
260                    // Merge the intervals.
261                    let max_y_spans = {
262                        let mut max_y_spans = Vec::new();
263                        let mut open_interval = None;
264                        for (start2, end2) in y_intervals {
265                            if let Some((start1, end1)) = open_interval {
266                                if start2 == end1 {
267                                    // Extend open interval.
268                                    open_interval = Some((start1, end2))
269                                } else {
270                                    // Close interval and start new interval.
271                                    max_y_spans.push((start1, end1));
272                                    open_interval = Some((start2, end2));
273                                }
274                            } else {
275                                open_interval = Some((start2, end2))
276                            }
277                        }
278                        // Add last interval.
279                        if let Some(i) = open_interval {
280                            max_y_spans.push(i);
281                        }
282                        max_y_spans
283                    };
284
285                    // Create maximal rectangles.
286                    for (y_start, y_end) in max_y_spans {
287                        if y_end <= current_edge.end && y_start >= current_edge.start {
288                            // The open interval intersects with the closing edge.
289                            let max_rectangle = Rect::new((x_start, y_start), (x_end, y_end));
290                            results.push(max_rectangle)
291                        } else {
292                            // The open interval does not interact with the closing edge. This is not a maximal rectangle.
293                        }
294                    }
295                }
296            }
297
298            // Update the inside-count for open rectangles that interact with the current edge.
299            open_rects
300                .iter_mut()
301                .take_while(|(_a, b, _value, _x)| b <= &current_edge.end)
302                .filter(|(a, _b, _value, _x)| a >= &current_edge.start)
303                .for_each(|(_, _, count, x)| {
304                    *count += increment;
305
306                    if *count == 0 {
307                        // Reset the x-coordinate of the left boundary (there's none now).
308                        *x = T::min_value();
309                    }
310                });
311
312            // Simplify the intervals.
313            merge_intervals(&mut open_rects);
314        }
315
316        results
317    }
318}
319
320#[test]
321fn test_decompose_rectangles_trivial() {
322    // Test trivial cases: Empty polygon and rectangle.
323    use iron_shapes::prelude::*;
324
325    let empty: SimpleRPolygon<i32> = SimpleRPolygon::empty();
326    let rects = decompose_rectangles(empty.edges());
327    assert_eq!(rects, vec![]);
328
329    // Test with reversed polygon.
330    let rect = SimpleRPolygon::try_new([(0, 0), (2, 0), (2, 2), (0, 2)]).unwrap();
331    let rects = decompose_rectangles(rect.reversed().edges());
332    assert_eq!(rects, vec![Rect::new((0, 0), (2, 2))]);
333}
334
335#[test]
336fn test_decompose_rectangles() {
337    use iron_shapes::prelude::*;
338    //    +--+
339    //    |  |
340    // +--+  |
341    // |     |
342    // +-----+
343    let poly = SimpleRPolygon::try_new([(0, 0), (2, 0), (2, 2), (1, 2), (1, 1), (0, 1)]).unwrap();
344    let rects = decompose_rectangles(poly.edges());
345    assert_eq!(
346        rects,
347        vec![Rect::new((0, 0), (2, 1)), Rect::new((1, 1), (2, 2))]
348    );
349
350    // Test with reversed polygon.
351    let rects = decompose_rectangles(poly.reversed().edges());
352    assert_eq!(
353        rects,
354        vec![Rect::new((0, 0), (2, 1)), Rect::new((1, 1), (2, 2))]
355    );
356}
357
358#[test]
359fn test_decompose_rectangles_overlapping() {
360    use iron_shapes::prelude::IntoEdges;
361
362    let rects = vec![Rect::new((0i32, 0), (10, 10)), Rect::new((0, 0), (5, 5))];
363
364    let decomposed = decompose_rectangles(rects.iter().flat_map(|r| r.into_edges()));
365    assert_eq!(decomposed, vec![Rect::new((0, 0), (10, 10))]);
366}
367
368#[test]
369fn test_decompose_rectangles_touching() {
370    use iron_shapes::prelude::IntoEdges;
371
372    let rects = vec![Rect::new((0i32, 0), (10, 10)), Rect::new((10, 0), (20, 10))];
373
374    let decomposed = decompose_rectangles(rects.iter().flat_map(|r| r.into_edges()));
375    assert_eq!(decomposed, vec![Rect::new((0, 0), (20, 10))]);
376}
377
378#[test]
379fn test_decompose_rectangles_vertical_touching() {
380    use iron_shapes::prelude::IntoEdges;
381
382    let rects = vec![
383        Rect::new((0i32, 0), (10, 10)),
384        Rect::new((0, 10), (10, 20)),
385        Rect::new((0, 20), (10, 30)),
386    ];
387
388    let decomposed = decompose_rectangles_vertical(rects.iter().flat_map(|r| r.into_edges()));
389    assert_eq!(decomposed, vec![Rect::new((0, 0), (10, 30))]);
390}
391
392#[test]
393fn test_decompose_maximal_rectangles() {
394    use iron_shapes::prelude::*;
395
396    //    +--+
397    //    |  |
398    // +--+  |
399    // |     |
400    // +-----+
401    let poly = SimpleRPolygon::try_new([(0, 0), (2, 0), (2, 2), (1, 2), (1, 1), (0, 1)]).unwrap();
402
403    let expected_rects = vec![Rect::new((0, 0), (2, 1)), Rect::new((1, 0), (2, 2))];
404
405    let rects = decompose_maximal_rectangles(poly.edges());
406    assert_eq!(rects, expected_rects);
407
408    // Test with reversed polygon.
409    let rects = decompose_maximal_rectangles(poly.reversed().edges());
410    assert_eq!(rects, expected_rects);
411}
412
413#[test]
414fn test_decompose_maximal_rectangles_2() {
415    use iron_shapes::prelude::*;
416    // +-----+
417    // |     |
418    // +--+  |
419    //    |  |
420    // +--+  |
421    // |     |
422    // +-----+
423    let poly = SimpleRPolygon::try_new([
424        (0, 0),
425        (2, 0),
426        (2, 3),
427        (0, 3),
428        (0, 2),
429        (1, 2),
430        (1, 1),
431        (0, 1),
432    ])
433    .unwrap();
434
435    let expected_rects = vec![
436        Rect::new((0, 0), (2, 1)),
437        Rect::new((0, 2), (2, 3)),
438        Rect::new((1, 0), (2, 3)),
439    ];
440
441    let rects = decompose_maximal_rectangles(poly.edges());
442    assert_eq!(rects, expected_rects);
443
444    // Test with reversed polygon.
445    let rects = decompose_maximal_rectangles(poly.reversed().edges());
446    assert_eq!(rects, expected_rects);
447}