use crate::{
indexes::{PointVec, PointIndex, HullVec, HullIndex, EdgeIndex, EMPTY_HULL},
};
const N: usize = 1 << 10;
#[derive(Clone, Copy, Debug)]
struct Node {
angle: f64,
edge: EdgeIndex,
left: HullIndex,
right: HullIndex,
}
#[derive(Debug)]
pub struct Hull {
buckets: [HullIndex; N],
data: HullVec<Node>,
points: PointVec<HullIndex>,
empty: Vec<HullIndex>,
}
impl Hull {
pub fn new(num_points: usize, constrained: bool) -> Hull {
Hull {
data: HullVec::new(),
buckets: [EMPTY_HULL; N],
points: if constrained {
PointVec::of(vec![EMPTY_HULL; num_points])
} else {
PointVec::new()
},
empty: Vec::new(),
}
}
pub fn initialize(&mut self, p: PointIndex, angle: f64, edge: EdgeIndex) {
let h = self.data.push(Node {
angle,
left: self.data.next_index(),
right: self.data.next_index(),
edge,
});
if !self.points.is_empty() {
self.points[p] = h;
}
let b = self.bucket(angle);
assert!(self.buckets[b] == EMPTY_HULL);
self.buckets[b] = h;
}
pub fn update(&mut self, h: HullIndex, e: EdgeIndex) {
self.data[h].edge = e;
}
pub fn get(&self, angle: f64) -> HullIndex {
let b = self.bucket(angle);
let mut h = self.buckets[b];
if h == EMPTY_HULL {
let mut t = b;
while self.buckets[t] == EMPTY_HULL {
t = (t + 1) % N;
}
h = self.buckets[t];
} else {
let start = h;
while self.data[h].angle < angle && self.bucket_h(h) == b {
h = self.data[h].right;
if h == start {
break;
}
}
}
assert!(h != EMPTY_HULL);
self.data[h].left
}
pub fn start(&self) -> HullIndex {
self.buckets.iter()
.filter(|b| **b != EMPTY_HULL)
.copied()
.next()
.unwrap()
}
pub fn check(&self) {
let point = self.buckets.iter()
.filter(|b| **b != EMPTY_HULL)
.copied()
.next();
assert!(point.is_some());
let start = point.unwrap();
assert!(self.buckets[self.bucket_h(start)] == start);
let mut index = start;
loop {
let next = self.data[index].right;
assert!(index == self.data[next].left);
let my_bucket = self.bucket_h(index);
let next_bucket = self.bucket_h(next);
if next_bucket != my_bucket {
assert!(self.buckets[next_bucket] == next);
}
if next == start {
break;
} else {
let my_position = self.data[index].angle;
let next_position = self.data[next].angle;
assert!(next_position >= my_position);
index = next;
}
}
}
pub fn left_hull(&self, h: HullIndex) -> HullIndex {
self.data[h].left
}
pub fn right_hull(&self, h: HullIndex) -> HullIndex {
self.data[h].right
}
pub fn edge(&self, h: HullIndex) -> EdgeIndex {
self.data[h].edge
}
pub fn index_of(&self, p: PointIndex) -> HullIndex {
assert!(!self.points.is_empty());
let h = self.points[p];
assert!(h != EMPTY_HULL);
assert!(self.data[h].left != EMPTY_HULL ||
self.data[h].right != EMPTY_HULL);
h
}
pub fn move_point(&mut self, old: PointIndex, new: PointIndex) {
if !self.points.is_empty() {
self.points[new] = self.points[old];
self.points[old] = EMPTY_HULL;
}
}
pub fn insert_bare(&mut self, angle: f64, point: PointIndex, e: EdgeIndex)
-> HullIndex
{
self.insert(self.get(angle), angle, point, e)
}
pub fn insert(&mut self, left: HullIndex, angle: f64,
point: PointIndex, edge: EdgeIndex) -> HullIndex {
let right = self.right_hull(left);
let h = if let Some(h) = self.empty.pop() {
self.data[h] = Node {
angle, edge, left, right
};
h
} else {
self.data.push(Node{
angle, edge, left, right
})
};
let b = self.bucket(angle);
if self.buckets[b] == EMPTY_HULL || (self.buckets[b] == right &&
angle < self.data[right].angle)
{
self.buckets[b] = h;
}
self.data[right].left = h;
self.data[left].right = h;
if !self.points.is_empty() {
self.points[point] = h;
}
h
}
pub fn erase(&mut self, h: HullIndex) {
let next = self.data[h].right;
let prev = self.data[h].left;
self.data[next].left = prev;
self.data[prev].right = next;
self.data[h].right = EMPTY_HULL;
self.data[h].left = EMPTY_HULL;
let b = self.bucket_h(h);
if self.buckets[b] == h {
if self.bucket_h(next) == b {
self.buckets[b] = next;
} else {
self.buckets[b] = EMPTY_HULL;
}
}
self.empty.push(h);
}
pub fn values(&self) -> impl Iterator<Item=EdgeIndex> + '_ {
let mut point: HullIndex = self.buckets.iter()
.filter(|b| **b != EMPTY_HULL)
.copied()
.next()
.unwrap();
let start = point;
let mut started = false;
std::iter::from_fn(move || {
let out = self.data[point].edge;
if point == start && started {
None
} else {
point = self.data[point].right;
started = true;
Some(out)
}
})
}
pub fn bucket_h(&self, h: HullIndex) -> usize {
self.bucket(self.data[h].angle)
}
pub fn bucket(&self, angle: f64) -> usize {
(angle * (self.buckets.len() as f64 - 1.0)).round() as usize
}
}