use crate::prelude::{Rect, REdge};
use itertools::Itertools;
use num_traits::{Bounded, Zero};
pub fn decompose_rectangles<T, I>(redges: I) -> Vec<Rect<T>>
where T: Ord + Bounded + Copy + Zero,
I: Iterator<Item=REdge<T>> {
{
let vertical_edges: Vec<_> = redges
.filter(|e| e.is_vertical() && e.start != e.end)
.map(|e| {
let is_left = e.start > e.end;
if is_left {
(e.reversed(), true)
} else {
(e, false)
}
})
.sorted_by_key(|(e, _is_left)| (e.offset, e.start, e.end))
.collect();
let mut open_rects: Vec<(T, T, isize, T)> = vec![(T::min_value(), T::max_value(), 0, T::min_value())];
let mut results = Vec::new();
fn split_intervals<T: PartialOrd + Copy>(intervals: &mut Vec<(T, T, isize, T)>, split_location: T) -> usize {
let (pos, &(a, b, value, x)) = intervals.iter()
.enumerate()
.find(|(_pos, &(a, b, _val, _x))| a <= split_location && split_location < b)
.expect("Intervals must span the whole value range without gap.");
if split_location == a || split_location == b {
pos
} else {
let i1 = (a, split_location, value, x);
let i2 = (split_location, b, value, x);
intervals[pos] = i2;
intervals.insert(pos, i1);
pos + 1
}
}
fn merge_intervals<T: PartialEq + Copy>(intervals: &mut Vec<(T, T, isize, T)>) {
debug_assert!(intervals.len() >= 1);
let mut write_index = 0;
let mut value = (intervals[0].2, intervals[0].3);
for i in 1..intervals.len() {
let (start, end, count, x) = intervals[i];
let current_value = (count, x);
intervals[write_index].1 = end;
if current_value != value {
intervals[write_index].1 = start;
write_index += 1;
intervals[write_index] = intervals[i];
value = current_value;
} else {
}
}
intervals.truncate(write_index + 1);
}
for (current_edge, is_left) in vertical_edges {
debug_assert!(current_edge.start < current_edge.end);
{
let pos = split_intervals(&mut open_rects, current_edge.start);
split_intervals(&mut open_rects, current_edge.end);
open_rects.iter_mut()
.skip(pos) .take_while(|(_a, b, _value, _x)| b <= ¤t_edge.end) .filter(|(a, _b, value, _x)| a >= ¤t_edge.start && *value == 0)
.for_each(|(_a, _b, _value, x)| {
*x = current_edge.offset;
});
}
let increment = if is_left {
1
} else {
-1
};
{
let increment_inv = -increment;
let closed_rects = open_rects.iter()
.take_while(|(_a, b, _value, _x)| b <= ¤t_edge.end)
.filter(|(a, _b, value, _x)| a >= ¤t_edge.start && value == &increment_inv)
.map(|&(a, b, _value, x_start)| {
let y_start = a.max(current_edge.start);
let y_end = b.min(current_edge.end);
let x_end = current_edge.offset;
debug_assert!(x_start != T::min_value());
Rect::new((x_start, y_start), (x_end, y_end))
});
results.extend(closed_rects);
}
open_rects.iter_mut()
.take_while(|(_a, b, _value, _x)| b <= ¤t_edge.end)
.filter(|(a, _b, _value, _x)| a >= ¤t_edge.start)
.for_each(|(_, _, count, x)| {
*count += increment;
if *count == 0 {
*x = T::min_value();
}
});
merge_intervals(&mut open_rects);
}
results
}
}
#[test]
fn test_decompose_rectangles_trivial() {
use crate::prelude::Point;
use crate::prelude::SimpleRPolygon;
let empty: SimpleRPolygon<i32> = SimpleRPolygon::empty();
let rects = decompose_rectangles(empty.edges());
assert_eq!(rects, vec![]);
let rect = SimpleRPolygon::try_new(vec![
(0, 0), (2, 0), (2, 2), (0, 2)
].iter().map(|t| Point::from(t)).collect()).unwrap();
let rects = decompose_rectangles(rect.reversed().edges());
assert_eq!(rects, vec![Rect::new((0, 0), (2, 2))]);
}
#[test]
fn test_decompose_rectangles() {
use crate::prelude::Point;
use crate::prelude::SimpleRPolygon;
let poly = SimpleRPolygon::try_new(vec![
(0, 0), (2, 0), (2, 2), (1, 2), (1, 1), (0, 1)
].iter().map(|t| Point::from(t)).collect()).unwrap();
let rects = decompose_rectangles(poly.edges());
assert_eq!(rects, vec![Rect::new((0, 0), (2, 1)), Rect::new((1, 1), (2, 2))]);
let rects = decompose_rectangles(poly.reversed().edges());
assert_eq!(rects, vec![Rect::new((0, 0), (2, 1)), Rect::new((1, 1), (2, 2))]);
}
#[test]
fn test_decompose_rectangles_overlapping() {
use crate::prelude::IntoEdges;
let rects = vec![
Rect::new((0i32, 0), (10, 10)),
Rect::new((0, 0), (5, 5)),
];
let decomposed = decompose_rectangles(rects.iter().flat_map(|r| r.into_edges()));
assert_eq!(decomposed, vec![Rect::new((0, 0), (10, 10))]);
}