use crate::core::face_adjacency::FACE_ADJACENCY;
use crate::core::origin::{get_origins, quintant_to_segment};
use crate::core::serialization::serialize;
use crate::core::utils::{A5Cell, Origin};
use crate::lattice::{triple_in_bounds, triple_to_s, Orientation, Triple};
pub type NeighborDelta = (i32, i32, i32, bool);
pub const LEFT_EDGE_DELTAS: [&[NeighborDelta]; 4] = [
&[(0, 0, 0, true), (0, 0, 1, false)],
&[
(0, 0, 0, true),
(0, 1, 0, true),
(0, -1, 1, false),
(0, 1, -1, false),
],
&[],
&[(0, -1, 0, true), (0, 0, -1, false)],
];
pub const RIGHT_EDGE_DELTAS: [&[NeighborDelta]; 4] = [
&[
(0, 0, 0, true),
(0, 1, 0, true),
(-1, 1, 0, false),
(1, -1, 0, false),
],
&[(0, 0, 0, true), (1, 0, 0, false)],
&[(0, -1, 0, true), (-1, 0, 0, false)],
&[],
];
pub const CROSS_FACE_DELTAS: [&[NeighborDelta]; 2] = [
&[(0, 0, 0, true), (1, 0, 0, true), (1, 0, -1, false)],
&[(0, 0, -1, true), (0, 0, 0, false)],
];
pub struct BoundaryContext<'a> {
pub triple: Triple,
pub parity: i32,
pub source_quintant: usize,
pub origin: &'a Origin,
pub hilbert_res: usize,
pub max_s: u64,
pub max_row: i32,
pub resolution: i32,
}
fn push_triple(
out: &mut Vec<u64>,
triple: &Triple,
orientation: Orientation,
origin: &Origin,
segment: usize,
ctx: &BoundaryContext,
) {
if !triple_in_bounds(triple, ctx.max_row) {
return;
}
if let Some(s) = triple_to_s(triple, ctx.hilbert_res, orientation) {
if s >= ctx.max_s {
return;
}
if let Ok(cell_id) = serialize(&A5Cell {
origin_id: origin.id,
segment,
s,
resolution: ctx.resolution,
}) {
out.push(cell_id);
}
}
}
#[allow(clippy::too_many_arguments)]
fn push_deltas(
out: &mut Vec<u64>,
base: &Triple,
deltas: &[NeighborDelta],
edge_only: bool,
orientation: Orientation,
origin: &Origin,
segment: usize,
ctx: &BoundaryContext,
) {
for &(dx, dy, dz, is_edge) in deltas {
if edge_only && !is_edge {
continue;
}
let neighbor = Triple::new(base.x + dx, base.y + dy, base.z + dz);
push_triple(out, &neighbor, orientation, origin, segment, ctx);
}
}
pub fn get_boundary_neighbors(
ctx: &BoundaryContext,
edge_only: bool,
skip_corners: bool,
) -> Vec<u64> {
let mut out: Vec<u64> = Vec::new();
let triple = ctx.triple;
let parity = ctx.parity;
let source_quintant = ctx.source_quintant;
let origin = ctx.origin;
let max_row = ctx.max_row;
let y_odd = triple.y % 2 != 0;
let delta_index = (parity * 2 + if y_odd { 1 } else { 0 }) as usize;
let origins = get_origins();
if triple.z == 0 {
let target_quintant = (source_quintant + 4) % 5;
let (segment, orientation) = quintant_to_segment(target_quintant, origin);
let base = Triple::new(0, triple.y, triple.x);
push_deltas(
&mut out,
&base,
LEFT_EDGE_DELTAS[delta_index],
edge_only,
orientation,
origin,
segment,
ctx,
);
}
if triple.x == 0 {
let target_quintant = (source_quintant + 1) % 5;
let (segment, orientation) = quintant_to_segment(target_quintant, origin);
let base = Triple::new(triple.z, triple.y, 0);
push_deltas(
&mut out,
&base,
RIGHT_EDGE_DELTAS[delta_index],
edge_only,
orientation,
origin,
segment,
ctx,
);
}
if triple.y == max_row {
let (adj_face_id, adj_quintant) = FACE_ADJACENCY[origin.id as usize][source_quintant];
let adj_origin = &origins[adj_face_id as usize];
let (segment, orientation) = quintant_to_segment(adj_quintant, adj_origin);
let base = Triple::new(triple.z, max_row, triple.x);
push_deltas(
&mut out,
&base,
CROSS_FACE_DELTAS[parity as usize],
edge_only,
orientation,
adj_origin,
segment,
ctx,
);
}
if triple.x == 0 && triple.y == 0 && triple.z == 0 {
for q in 0..5usize {
if q == source_quintant {
continue;
}
let distance =
std::cmp::min((q + 5 - source_quintant) % 5, (source_quintant + 5 - q) % 5);
if edge_only && distance != 1 {
continue;
}
let (segment, orientation) = quintant_to_segment(q, origin);
push_triple(&mut out, &triple, orientation, origin, segment, ctx);
}
}
if !skip_corners && triple.x == -max_row && triple.y == max_row && triple.z == 0 {
let prev_quintant = (source_quintant + 4) % 5;
let (prev_adj_face_id, prev_adj_quintant) =
FACE_ADJACENCY[origin.id as usize][prev_quintant];
let prev_adj_origin = &origins[prev_adj_face_id as usize];
let (prev_adj_segment, prev_adj_orientation) =
quintant_to_segment(prev_adj_quintant, prev_adj_origin);
push_triple(
&mut out,
&triple,
prev_adj_orientation,
prev_adj_origin,
prev_adj_segment,
ctx,
);
let (cross_face_id, cross_quintant) = FACE_ADJACENCY[origin.id as usize][source_quintant];
let cross_origin = &origins[cross_face_id as usize];
let next_cross_quintant = (cross_quintant + 1) % 5;
let (cross_segment, cross_orientation) =
quintant_to_segment(next_cross_quintant, cross_origin);
push_triple(
&mut out,
&triple,
cross_orientation,
cross_origin,
cross_segment,
ctx,
);
}
out
}