use crate::{
BaseCell, CCW, CW, CellIndex, Direction, Resolution, coord::FaceIJK,
index::bits,
};
pub fn neighbor_rotations(
origin: CellIndex,
direction: Direction,
mut rotations: u8,
) -> Option<(CellIndex, u8)> {
debug_assert_ne!(direction, Direction::Center);
let mut current = u64::from(origin);
let mut direction = direction.rotate60::<{ CCW }>(rotations.into());
let mut res = origin.resolution();
for resolution in Resolution::range(Resolution::One, res).rev() {
let from = usize::from(bits::get_direction(current, resolution));
let to = usize::from(direction);
let next_direction = if resolution.is_class3() {
let direction = NEW_DIRECTION_II[from][to];
current =
bits::set_direction(current, u8::from(direction), resolution);
NEW_ADJUSTMENT_II[from][to]
} else {
let direction = NEW_DIRECTION_III[from][to];
current =
bits::set_direction(current, u8::from(direction), resolution);
NEW_ADJUSTMENT_III[from][to]
};
if next_direction == Direction::Center {
break;
}
direction = next_direction;
res = resolution.pred().expect("parent resolution");
}
let old_base_cell = origin.base_cell();
let new_rotations = if res == Resolution::Zero {
if let Some(base_cell) = old_base_cell.neighbor(direction) {
current = h3o_bit::set_base_cell(current, base_cell.into());
old_base_cell.neighbor_rotation(direction)
} else {
let base_cell = old_base_cell
.neighbor(Direction::IK)
.expect("pentagon neighbor");
current = h3o_bit::set_base_cell(current, base_cell.into());
current = bits::rotate60::<{ CCW }>(current, 1);
rotations += 1;
old_base_cell.neighbor_rotation(Direction::IK)
}
} else {
0
};
let new_base_cell =
BaseCell::new_unchecked(h3o_bit::get_base_cell(current));
if new_base_cell.is_pentagon() {
let mut already_adjusted_k_subsequence = false;
if bits::first_axe(current) == Direction::K.axe() {
if old_base_cell == new_base_cell {
let old_leading_dir = bits::first_axe(origin.into());
old_leading_dir?;
if old_leading_dir == Direction::JK.axe() {
current = bits::rotate60::<{ CCW }>(current, 1);
rotations += 1;
} else if old_leading_dir == Direction::IK.axe() {
current = bits::rotate60::<{ CW }>(current, 1);
rotations += 5;
}
} else {
if new_base_cell.is_cw_offset(FaceIJK::from(old_base_cell).face)
{
current = bits::rotate60::<{ CW }>(current, 1);
}
already_adjusted_k_subsequence = true;
}
}
current = (0..new_rotations)
.fold(current, |acc, _| bits::pentagon_rotate60::<{ CCW }>(acc));
if old_base_cell != new_base_cell {
let direction = bits::first_axe(current);
if new_base_cell.is_polar_pentagon() {
rotations += u8::from(
u8::from(old_base_cell) != 118
&& u8::from(old_base_cell) != 8
&& direction != Direction::JK.axe(),
);
} else {
rotations += u8::from(
!already_adjusted_k_subsequence
&& direction == Direction::IK.axe(),
);
}
}
} else if new_rotations != 0 {
current = bits::rotate60::<{ CCW }>(current, new_rotations.into());
}
Some((
CellIndex::new_unchecked(current),
(rotations + new_rotations) % 6,
))
}
pub fn direction_for_neighbor(
origin: CellIndex,
destination: CellIndex,
) -> Option<Direction> {
let start = 1 + u8::from(origin.is_pentagon());
(start..=6).find_map(|value| {
let direction = Direction::new_unchecked(value);
let neighbor =
neighbor_rotations(origin, direction, 0).map(|result| result.0);
(neighbor == Some(destination)).then_some(direction)
})
}
const CENTER: Direction = Direction::Center;
const K_AXE: Direction = Direction::K;
const J_AXE: Direction = Direction::J;
const JK_AXE: Direction = Direction::JK;
const I_AXE: Direction = Direction::I;
const IK_AXE: Direction = Direction::IK;
const IJ_AXE: Direction = Direction::IJ;
const NEW_DIRECTION_II: [[Direction; 7]; 7] = [
[CENTER, K_AXE, J_AXE, JK_AXE, I_AXE, IK_AXE, IJ_AXE],
[K_AXE, I_AXE, JK_AXE, IJ_AXE, IK_AXE, J_AXE, CENTER],
[J_AXE, JK_AXE, K_AXE, I_AXE, IJ_AXE, CENTER, IK_AXE],
[JK_AXE, IJ_AXE, I_AXE, IK_AXE, CENTER, K_AXE, J_AXE],
[I_AXE, IK_AXE, IJ_AXE, CENTER, J_AXE, JK_AXE, K_AXE],
[IK_AXE, J_AXE, CENTER, K_AXE, JK_AXE, IJ_AXE, I_AXE],
[IJ_AXE, CENTER, IK_AXE, J_AXE, K_AXE, I_AXE, JK_AXE],
];
const NEW_ADJUSTMENT_II: [[Direction; 7]; 7] = [
[CENTER, CENTER, CENTER, CENTER, CENTER, CENTER, CENTER],
[CENTER, K_AXE, CENTER, K_AXE, CENTER, IK_AXE, CENTER],
[CENTER, CENTER, J_AXE, JK_AXE, CENTER, CENTER, J_AXE],
[CENTER, K_AXE, JK_AXE, JK_AXE, CENTER, CENTER, CENTER],
[CENTER, CENTER, CENTER, CENTER, I_AXE, I_AXE, IJ_AXE],
[CENTER, IK_AXE, CENTER, CENTER, I_AXE, IK_AXE, CENTER],
[CENTER, CENTER, J_AXE, CENTER, IJ_AXE, CENTER, IJ_AXE],
];
const NEW_DIRECTION_III: [[Direction; 7]; 7] = [
[CENTER, K_AXE, J_AXE, JK_AXE, I_AXE, IK_AXE, IJ_AXE],
[K_AXE, J_AXE, JK_AXE, I_AXE, IK_AXE, IJ_AXE, CENTER],
[J_AXE, JK_AXE, I_AXE, IK_AXE, IJ_AXE, CENTER, K_AXE],
[JK_AXE, I_AXE, IK_AXE, IJ_AXE, CENTER, K_AXE, J_AXE],
[I_AXE, IK_AXE, IJ_AXE, CENTER, K_AXE, J_AXE, JK_AXE],
[IK_AXE, IJ_AXE, CENTER, K_AXE, J_AXE, JK_AXE, I_AXE],
[IJ_AXE, CENTER, K_AXE, J_AXE, JK_AXE, I_AXE, IK_AXE],
];
const NEW_ADJUSTMENT_III: [[Direction; 7]; 7] = [
[CENTER, CENTER, CENTER, CENTER, CENTER, CENTER, CENTER],
[CENTER, K_AXE, CENTER, JK_AXE, CENTER, K_AXE, CENTER],
[CENTER, CENTER, J_AXE, J_AXE, CENTER, CENTER, IJ_AXE],
[CENTER, JK_AXE, J_AXE, JK_AXE, CENTER, CENTER, CENTER],
[CENTER, CENTER, CENTER, CENTER, I_AXE, IK_AXE, I_AXE],
[CENTER, K_AXE, CENTER, CENTER, IK_AXE, IK_AXE, CENTER],
[CENTER, CENTER, IJ_AXE, CENTER, I_AXE, CENTER, IJ_AXE],
];