use grid::{GridCoord, GridPoint3, Dir};
use super::transform::*;
use super::util::*;
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)]
pub enum TurnDir {
Left,
Right,
}
impl TurnDir {
pub fn opposite(&self) -> TurnDir {
match *self {
TurnDir::Left => TurnDir::Right,
TurnDir::Right => TurnDir::Left,
}
}
pub fn apply_one_unit(&self, dir: &mut Dir) {
match *self {
TurnDir::Left => dir.index = (dir.index + 1) % 12,
TurnDir::Right => dir.index = (dir.index + 12 - 1) % 12,
};
}
pub fn apply_two_units(&self, dir: &mut Dir) {
match *self {
TurnDir::Left => dir.index = (dir.index + 2) % 12,
TurnDir::Right => dir.index = (dir.index + 12 - 2) % 12,
};
}
}
pub fn turn_left_by_one_hex_edge(
pos: &mut GridPoint3,
dir: &mut Dir,
resolution: [GridCoord; 2],
) -> Result<(), ()> {
turn_by_one_hex_edge(pos, dir, resolution, TurnDir::Left)
}
pub fn turn_right_by_one_hex_edge(
pos: &mut GridPoint3,
dir: &mut Dir,
resolution: [GridCoord; 2],
) -> Result<(), ()> {
turn_by_one_hex_edge(pos, dir, resolution, TurnDir::Right)
}
pub fn turn_by_one_hex_edge(
pos: &mut GridPoint3,
dir: &mut Dir,
resolution: [GridCoord; 2],
turn_dir: TurnDir,
) -> Result<(), ()> {
debug_assert_pos_within_root(pos, resolution);
if !dir.points_at_hex_edge() {
return Err(());
}
#[cfg(debug)]
{
let next_pos = adjacent_pos_in_dir(*pos, *dir)?;
debug_assert_pos_within_root(next_pos, resolution);
}
turn_dir.apply_two_units(dir);
maybe_rebase_on_adjacent_root_following_rotation(pos, dir, resolution);
Ok(())
}
fn maybe_rebase_on_adjacent_root_following_rotation(
pos: &mut GridPoint3,
dir: &mut Dir,
resolution: [GridCoord; 2],
) {
if !is_on_root_edge(pos, resolution) {
return;
}
let tri = if is_pentagon(pos, resolution) {
triangle_on_pos_with_closest_mid_axis(pos, dir, resolution)
} else {
closest_triangle_to_point(pos, resolution)
};
let (new_pos, new_dir) = world_to_local(*pos, *dir, resolution, tri);
*pos = new_pos;
*dir = new_dir;
let next_pos = adjacent_pos_in_dir(*pos, *dir).expect(
"Caller should have assured we're pointing at a hex edge.",
);
let still_in_same_quad = next_pos.x >= 0 && next_pos.y >= 0;
if still_in_same_quad {
transform_into_exit_triangle(pos, dir, resolution, &tri.exits[0]);
return;
}
if next_pos.x < 0 {
pos.x = pos.y;
pos.y = 0;
*dir = dir.next_hex_edge_right();
transform_into_exit_triangle(pos, dir, resolution, &tri.exits[1]);
return;
}
if next_pos.y < 0 {
pos.y = pos.x;
pos.x = 0;
*dir = dir.next_hex_edge_left();
transform_into_exit_triangle(pos, dir, resolution, &tri.exits[4]);
return;
}
panic!("Oops, we must have forgotten a rotation case. Sounds like we didn't test hard enough!")
}
pub fn turn_around_and_face_neighbor(
pos: &mut GridPoint3,
dir: &mut Dir,
resolution: [GridCoord; 2],
last_turn_bias: super::TurnDir,
) {
if is_pentagon(pos, resolution) {
turn_by_one_hex_edge(pos, dir, resolution, last_turn_bias).unwrap();
turn_by_one_hex_edge(pos, dir, resolution, last_turn_bias).unwrap();
} else {
turn_left_by_one_hex_edge(pos, dir, resolution).unwrap();
turn_left_by_one_hex_edge(pos, dir, resolution).unwrap();
turn_left_by_one_hex_edge(pos, dir, resolution).unwrap();
}
}