use std::iter::Chain;
use std::slice;
use super::{GridCoord, GridPoint3, PosInOwningRoot, Dir};
use movement::{move_forward, turn_left_by_one_hex_edge};
pub struct Neighbors {
iter: NeighborsImpl,
}
impl Neighbors {
pub fn new(pos: GridPoint3, resolution: [GridCoord; 2]) -> Neighbors {
let above_and_below = AboveAndBelow::new(pos);
let is_away_from_root_edges = pos.x > 0 && pos.x < resolution[0] - 1 && pos.y > 0 &&
pos.y < resolution[1] - 1;
if is_away_from_root_edges {
let fast_intra_root_neighbors = FastIntraRootNeighbors::new(pos);
Neighbors {
iter: NeighborsImpl::FastIntra(above_and_below.chain(fast_intra_root_neighbors)),
}
} else {
let slow_general_edge_neighbors = SlowGeneralEdgeNeighbors::new(pos, resolution);
Neighbors {
iter: NeighborsImpl::SlowGeneral(
above_and_below.chain(slow_general_edge_neighbors),
),
}
}
}
}
impl Iterator for Neighbors {
type Item = GridPoint3;
fn next(&mut self) -> Option<GridPoint3> {
match self.iter {
NeighborsImpl::SlowGeneral(ref mut iter) => iter.next(),
NeighborsImpl::FastIntra(ref mut iter) => iter.next(),
}
}
}
enum NeighborsImpl {
SlowGeneral(Chain<AboveAndBelow, SlowGeneralEdgeNeighbors>),
FastIntra(Chain<AboveAndBelow, FastIntraRootNeighbors>),
}
struct SlowGeneralEdgeNeighbors {
resolution: [GridCoord; 2],
origin: GridPoint3,
first_neighbor: Option<GridPoint3>,
current_dir: Dir,
}
impl SlowGeneralEdgeNeighbors {
pub fn new(mut pos: GridPoint3, resolution: [GridCoord; 2]) -> SlowGeneralEdgeNeighbors {
pos = PosInOwningRoot::new(pos, resolution).into();
let start_dir = if pos.x == resolution[0] && pos.y == resolution[1] {
Dir::new(6)
} else {
Dir::new(0)
};
SlowGeneralEdgeNeighbors {
resolution: resolution,
origin: pos,
first_neighbor: None,
current_dir: start_dir,
}
}
}
impl Iterator for SlowGeneralEdgeNeighbors {
type Item = GridPoint3;
fn next(&mut self) -> Option<GridPoint3> {
let mut pos = self.origin;
let mut dir = self.current_dir;
move_forward(&mut pos, &mut dir, self.resolution).expect(
"Oops, we started from an invalid position.",
);
pos = PosInOwningRoot::new(pos, self.resolution).into();
if let Some(first_neighbor) = self.first_neighbor {
if first_neighbor == pos {
return None;
}
} else {
self.first_neighbor = Some(pos);
}
turn_left_by_one_hex_edge(&mut self.origin, &mut self.current_dir, self.resolution)
.expect("Oops, we picked a bad starting direction.");
Some(pos)
}
}
struct FastIntraRootNeighbors {
origin: GridPoint3,
offsets: slice::Iter<'static, (GridCoord, GridCoord)>,
}
impl FastIntraRootNeighbors {
pub fn new(pos: GridPoint3) -> FastIntraRootNeighbors {
use super::cell_shape::NEIGHBOR_OFFSETS;
FastIntraRootNeighbors {
origin: pos,
offsets: NEIGHBOR_OFFSETS.iter(),
}
}
}
impl Iterator for FastIntraRootNeighbors {
type Item = GridPoint3;
fn next(&mut self) -> Option<GridPoint3> {
self.offsets.next().map(|offset| {
self.origin.with_x(self.origin.x + offset.0).with_y(
self.origin.y +
offset.1,
)
})
}
}
struct AboveAndBelow {
origin: GridPoint3,
yielded_above: bool,
yielded_below: bool,
}
impl AboveAndBelow {
pub fn new(pos: GridPoint3) -> AboveAndBelow {
AboveAndBelow {
origin: pos,
yielded_above: false,
yielded_below: false,
}
}
}
impl Iterator for AboveAndBelow {
type Item = GridPoint3;
fn next(&mut self) -> Option<GridPoint3> {
if !self.yielded_above {
self.yielded_above = true;
return Some(self.origin.with_z(self.origin.z + 1));
}
if !self.yielded_below {
if self.origin.z == 0 {
return None;
}
self.yielded_below = true;
return Some(self.origin.with_z(self.origin.z - 1));
}
None
}
}