use std::ops::{Sub, Add};
use crate::HananCoord;
use super::point::*;
use super::rectangle::Rect;
#[derive(Clone, Debug)]
pub struct HananGridWithCoords<T> {
pub xs: Vec<T>,
pub ys: Vec<T>,
}
impl<T> HananGridWithCoords<T>
where T: Ord + Copy + Sub<Output=T> + Add<Output=T> {
pub fn num_x(&self) -> usize {
self.xs.len()
}
pub fn num_y(&self) -> usize {
self.ys.len()
}
pub fn edge_weight_h(&self, idx: usize) -> T {
self.xs[idx + 1] - self.xs[idx]
}
pub fn edge_weight_v(&self, idx: usize) -> T {
self.ys[idx + 1] - self.ys[idx]
}
}
#[derive(Copy, Clone, Debug)]
pub struct UnitHananGrid {
rect: Rect<HananCoord>
}
impl UnitHananGrid {
pub fn new(rect: Rect<HananCoord>) -> Self {
Self {
rect
}
}
pub fn rect(&self) -> Rect<HananCoord> {
self.rect
}
pub fn is_on_boundary(&self, p: Point<HananCoord>) -> bool {
self.rect.is_on_boundary(p)
}
pub fn is_on_partial_boundary(&self, p: Point<HananCoord>, boundary: Boundary) -> bool {
self.contains_point(p) && match boundary {
Boundary::Right => p.x == self.upper_right().x,
Boundary::Top => p.y == self.upper_right().y,
Boundary::Left => p.x == self.lower_left().x,
Boundary::Bottom => p.y == self.lower_left().y
}
}
pub fn is_corner(&self, p: Point<HananCoord>) -> bool {
(p.x == self.lower_left().x || p.x == self.upper_right().x)
&& (p.y == self.lower_left().y || p.y == self.upper_right().y)
}
pub fn contains_point(&self, p: Point<HananCoord>) -> bool {
self.rect.contains_point(p)
}
pub fn all_boundary_points(&self, start: Point<HananCoord>) -> HananBoundaryIterator {
assert!(self.is_on_boundary(start), "start point must be on the boundary");
HananBoundaryIterator {
grid: *self,
next_point: start,
}
}
pub fn boundary_points(&self, boundary: Boundary) -> HananPartialBoundaryIterator {
HananPartialBoundaryIterator::new(*self, boundary)
}
pub fn lower_left(&self) -> Point<HananCoord> {
self.rect.lower_left()
}
pub fn lower_right(&self) -> Point<HananCoord> {
self.rect.lower_right()
}
pub fn upper_right(&self) -> Point<HananCoord> {
self.rect.upper_right()
}
pub fn upper_left(&self) -> Point<HananCoord> {
self.rect.upper_left()
}
pub fn compact_boundary(&self, boundary: Boundary) -> Self {
match boundary {
Boundary::Left | Boundary::Right => assert!(self.rect.width() > 0, "cannot compact left or right boundary if width is zero"),
Boundary::Bottom | Boundary::Top => assert!(self.rect.height() > 0, "cannot compact bottom or top boundary if width is zero"),
}
let mut ll = self.rect.lower_left();
let mut ur = self.rect.upper_right();
match boundary {
Boundary::Right => ur.x -= 1,
Boundary::Top => ur.y -= 1,
Boundary::Left => ll.x += 1,
Boundary::Bottom => ll.y += 1
}
Self::new(Rect::new(ll, ur))
}
pub fn expand_boundary(&self, boundary: Boundary) -> Self {
let mut ll = self.rect.lower_left();
let mut ur = self.rect.upper_right();
match boundary {
Boundary::Right => ur.x += 1,
Boundary::Top => ur.y += 1,
Boundary::Left => ll.x -= 1,
Boundary::Bottom => ll.y -= 1
}
Self::new(Rect::new(ll, ur))
}
}
#[derive(Clone, Debug)]
pub struct HananPartialBoundaryIterator {
grid: UnitHananGrid,
next_point: Point<HananCoord>,
direction: (HananCoord, HananCoord),
}
impl HananPartialBoundaryIterator {
fn new(grid: UnitHananGrid, boundary: Boundary) -> Self {
let (start, direction) = match boundary {
Boundary::Right => (grid.lower_right(), (0, 1)),
Boundary::Top => (grid.upper_left(), (1, 0)),
Boundary::Left => (grid.lower_left(), (0, 1)),
Boundary::Bottom => (grid.lower_left(), (1, 0))
};
Self {
grid,
next_point: start,
direction,
}
}
}
impl Iterator for HananPartialBoundaryIterator {
type Item = Point<HananCoord>;
fn next(&mut self) -> Option<Self::Item> {
if self.grid.contains_point(self.next_point) {
let p = self.next_point;
self.next_point.x += self.direction.0;
self.next_point.y += self.direction.1;
Some(p)
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct HananBoundaryIterator {
grid: UnitHananGrid,
next_point: Point<HananCoord>,
}
impl DoubleEndedIterator for HananBoundaryIterator {
fn next_back(&mut self) -> Option<Self::Item> {
let p = self.next_point;
self.next_point = {
let is_left_boundary = p.x == self.grid.lower_left().x;
let is_right_boundary = p.x == self.grid.upper_right().x;
let is_bottom_boundary = p.y == self.grid.lower_left().y;
let is_top_boundary = p.y == self.grid.upper_right().y;
if is_right_boundary && !is_bottom_boundary {
Point::new(p.x, p.y - 1)
} else if is_top_boundary && !is_right_boundary {
Point::new(p.x + 1, p.y)
} else if is_left_boundary && !is_top_boundary {
Point::new(p.x, p.y + 1)
} else {
debug_assert!(is_bottom_boundary && !is_left_boundary);
Point::new(p.x - 1, p.y)
}
};
debug_assert!(self.grid.contains_point(self.next_point));
Some(p)
}
}
impl Iterator for HananBoundaryIterator {
type Item = Point<HananCoord>;
fn next(&mut self) -> Option<Self::Item> {
let p = self.next_point;
self.next_point = {
let is_left_boundary = p.x == self.grid.lower_left().x;
let is_right_boundary = p.x == self.grid.upper_right().x;
let is_bottom_boundary = p.y == self.grid.lower_left().y;
let is_top_boundary = p.y == self.grid.upper_right().y;
if is_right_boundary && !is_top_boundary {
Point::new(p.x, p.y + 1)
} else if is_top_boundary && !is_left_boundary {
Point::new(p.x - 1, p.y)
} else if is_left_boundary && !is_bottom_boundary {
Point::new(p.x, p.y - 1)
} else {
debug_assert!(is_bottom_boundary && !is_right_boundary);
Point::new(p.x + 1, p.y)
}
};
debug_assert!(self.grid.contains_point(self.next_point));
Some(p)
}
}
#[test]
fn test_hanan_boundary_iter() {
let grid = UnitHananGrid::new(Rect::new((0, 0).into(), (1, 2).into()));
let start = Point::new(1, 1);
let boundary_points: Vec<_> = grid.all_boundary_points(start).take(6).collect();
let expected_boundary_points: Vec<_> = vec![(1, 1), (1, 2), (0, 2), (0, 1), (0, 0), (1, 0)]
.into_iter().map(|c| c.into()).collect();
assert_eq!(
boundary_points,
expected_boundary_points,
);
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Boundary {
Right,
Top,
Left,
Bottom,
}