use crate::geom::Point;
use crate::predicates::{cmp_dir_halfplane, orient, sub};
use core::cmp::Ordering;
use core::ops::Range;
#[inline]
pub(crate) fn cmp_sweep_edges(a: (Point, Point), b: (Point, Point)) -> Ordering {
a.0.cmp(&b.0)
.then_with(|| cmp_dir_halfplane(sub(a.1, a.0), sub(b.1, b.0)))
}
struct Status {
blocks: Vec<Vec<Entry>>,
last: Vec<Entry>,
}
#[derive(Clone, Copy)]
struct Entry {
lo: Point,
hi: Point,
id: u32,
}
impl Entry {
#[inline]
fn new(edges: &[(Point, Point)], id: u32) -> Self {
let (lo, hi) = edges[id as usize];
Entry { lo, hi, id }
}
}
const BLOCK: usize = 128;
impl Status {
fn from_sorted(edges: &[(Point, Point)], ids: &[u32]) -> Self {
let mut st = Status {
blocks: Vec::new(),
last: Vec::new(),
};
for c in ids.chunks(BLOCK) {
let mut b = Vec::with_capacity(2 * BLOCK);
b.extend(c.iter().map(|&e| Entry::new(edges, e)));
st.last.push(b[b.len() - 1]);
st.blocks.push(b);
}
st
}
fn into_vec(self) -> Vec<u32> {
self.blocks.iter().flatten().map(|e| e.id).collect()
}
#[inline]
fn find(&self, below: impl Fn(Point, Point) -> bool) -> (usize, usize) {
let b = self.last.partition_point(|e| below(e.lo, e.hi));
if b == self.blocks.len() {
return match self.blocks.last() {
Some(l) => (b - 1, l.len()),
None => (0, 0),
};
}
(b, self.blocks[b].partition_point(|e| below(e.lo, e.hi)))
}
#[inline]
fn before(&self, b: usize, i: usize) -> Option<u32> {
if i > 0 {
Some(self.blocks[b][i - 1].id)
} else if b > 0 {
Some(self.last[b - 1].id)
} else {
None
}
}
fn after(&self, mut b: usize, mut i: usize, mut skip: usize) -> Option<u32> {
while b < self.blocks.len() {
let len = self.blocks[b].len();
if i + skip < len {
return Some(self.blocks[b][i + skip].id);
}
skip -= len.saturating_sub(i).min(skip);
b += 1;
i = 0;
}
None
}
fn collect(&self, mut b: usize, mut i: usize, mut count: usize, out: &mut Vec<u32>) {
out.clear();
while count > 0 && b < self.blocks.len() {
let blk = &self.blocks[b];
let k = count.min(blk.len() - i.min(blk.len()));
out.extend(blk[i..i + k].iter().map(|e| e.id));
count -= k;
b += 1;
i = 0;
}
}
fn splice(
&mut self,
edges: &[(Point, Point)],
b: usize,
i: usize,
mut remove: usize,
insert: impl ExactSizeIterator<Item = u32>,
) {
let mut insert = insert.map(|e| Entry::new(edges, e));
if self.blocks.is_empty() {
if insert.len() == 0 {
return;
}
self.blocks.push(Vec::with_capacity(2 * BLOCK));
self.last.push(Entry {
lo: Point::default(),
hi: Point::default(),
id: 0,
});
}
let mut i = i;
let blk = &mut self.blocks[b];
while remove > 0 && i < blk.len() {
let Some(e) = insert.next() else { break };
blk[i] = e;
i += 1;
remove -= 1;
}
let mut bb = b;
let mut ii = i;
while remove > 0 && bb < self.blocks.len() {
let blk = &mut self.blocks[bb];
let k = remove.min(blk.len() - ii);
blk.drain(ii..ii + k);
remove -= k;
bb += 1;
ii = 0;
}
let blk = &mut self.blocks[b];
let i = i.min(blk.len());
blk.splice(i..i, insert);
if blk.len() > 2 * BLOCK {
let tail = blk.split_off(BLOCK);
let l = tail[tail.len() - 1];
self.blocks.insert(b + 1, tail);
self.last.insert(b + 1, l);
}
let end = bb.max(b + 2).min(self.blocks.len());
let mut k = b;
let mut end = end;
while k < end {
match self.blocks[k].last() {
None => {
self.blocks.remove(k);
self.last.remove(k);
end -= 1;
}
Some(&l) => {
self.last[k] = l;
k += 1;
}
}
}
}
}
pub(crate) fn sweep(edges: &[(Point, Point)], mut on_insert: impl FnMut(u32, Option<u32>)) {
sweep_events(edges, |below, _, _, starting| {
for k in starting.clone() {
let bl = if k == starting.start {
below
} else {
Some(k - 1)
};
on_insert(k, bl);
}
});
}
pub(crate) fn sweep_events(
edges: &[(Point, Point)],
on_event: impl FnMut(Option<u32>, Option<u32>, &[u32], Range<u32>),
) {
let mut his: Vec<Point> = edges.iter().map(|e| e.1).collect();
his.sort_unstable();
sweep_band(edges, &his, 0..edges.len(), &[], on_event);
}
pub(crate) fn sweep_band(
edges: &[(Point, Point)],
his: &[Point],
starting: Range<usize>,
initial: &[u32],
mut on_event: impl FnMut(Option<u32>, Option<u32>, &[u32], Range<u32>),
) -> Vec<u32> {
let mut status = Status::from_sorted(edges, initial);
let mut ending_buf: Vec<u32> = Vec::new();
let n = starting.end;
let mut si = starting.start;
let mut hi = 0usize;
loop {
let v = match (edges[..n].get(si), his.get(hi)) {
(Some(e), Some(&h)) => e.0.min(h),
(Some(e), None) => e.0,
(None, Some(&h)) => h,
(None, None) => break,
};
let mut ending = 0usize;
while hi < his.len() && his[hi] == v {
ending += 1;
hi += 1;
}
let s0 = si;
while si < n && edges[si].0 == v {
si += 1;
}
let (b, i) = status.find(|lo, h| h != v && orient(lo, h, v) > 0);
let below = status.before(b, i);
status.collect(b, i, ending, &mut ending_buf);
status.splice(edges, b, i, ending, (s0..si).map(|e| e as u32));
let above = status.after(b, i, si - s0);
on_event(below, above, &ending_buf, s0 as u32..si as u32);
}
status.into_vec()
}
pub(crate) fn cmp_status(edges: &[(Point, Point)], a: u32, b: u32) -> Ordering {
let (ea, eb) = (edges[a as usize], edges[b as usize]);
if ea.0 == eb.0 {
return a.cmp(&b);
}
if ea.0 > eb.0 {
orient(eb.0, eb.1, ea.0).cmp(&0).then(a.cmp(&b))
} else {
0.cmp(&orient(ea.0, ea.1, eb.0)).then(a.cmp(&b))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nested_squares() {
let p = Point::new;
let mut e = vec![
(p(0, 0), p(10, 0)),
(p(0, 0), p(0, 10)),
(p(0, 10), p(10, 10)),
(p(10, 0), p(10, 10)),
(p(2, 2), p(8, 2)),
(p(2, 2), p(2, 8)),
(p(2, 8), p(8, 8)),
(p(8, 2), p(8, 8)),
];
e.sort_by(|a, b| cmp_sweep_edges(*a, *b));
let mut below = vec![None; e.len()];
sweep(&e, |i, b| below[i as usize] = b.map(|b| e[b as usize]));
let idx = |x: (Point, Point)| e.iter().position(|&y| y == x).unwrap();
assert_eq!(below[idx((p(0, 0), p(10, 0)))], None);
assert_eq!(below[idx((p(0, 0), p(0, 10)))], Some((p(0, 0), p(10, 0))));
assert_eq!(below[idx((p(2, 2), p(8, 2)))], Some((p(0, 0), p(10, 0))));
assert_eq!(below[idx((p(2, 8), p(8, 8)))], Some((p(2, 2), p(8, 2))));
assert_eq!(below[idx((p(10, 0), p(10, 10)))], None);
}
}