use crate::core::origin::{get_origins, segment_to_quintant};
use crate::core::serialization::{deserialize, serialize, FIRST_HILBERT_RESOLUTION};
use crate::core::utils::{A5Cell, Origin};
use crate::lattice::{
anchor_to_triple, s_to_anchor, triple_in_bounds, triple_parity, triple_to_s, Orientation,
Triple,
};
use crate::traversal::global_neighbors::get_global_cell_neighbors;
use crate::traversal::lattice_boundary::{get_boundary_neighbors, BoundaryContext};
struct LatticeSource<'a> {
origin: &'a Origin,
segment: usize,
s: u64,
resolution: i32,
hilbert_res: usize,
quintant: usize,
orientation: Orientation,
triple: Triple,
max_s: u64,
max_row: i32,
}
fn decode_source(cell_id: u64) -> Option<LatticeSource<'static>> {
let cell = deserialize(cell_id).ok()?;
if cell.resolution < FIRST_HILBERT_RESOLUTION {
return None;
}
let origin = &get_origins()[cell.origin_id as usize];
let hilbert_res = (cell.resolution - FIRST_HILBERT_RESOLUTION + 1) as usize;
let (quintant, orientation) = segment_to_quintant(cell.segment, origin);
let anchor = s_to_anchor(cell.s, hilbert_res, orientation);
let triple = anchor_to_triple(&anchor);
Some(LatticeSource {
origin,
segment: cell.segment,
s: cell.s,
resolution: cell.resolution,
hilbert_res,
quintant,
orientation,
triple,
max_s: 4u64.pow(hilbert_res as u32),
max_row: (1i32 << hilbert_res) - 1,
})
}
fn boundary_context<'a>(src: &'a LatticeSource<'a>) -> BoundaryContext<'a> {
BoundaryContext {
triple: src.triple,
parity: triple_parity(&src.triple),
source_quintant: src.quintant,
origin: src.origin,
hilbert_res: src.hilbert_res,
max_s: src.max_s,
max_row: src.max_row,
resolution: src.resolution,
}
}
type Delta = (i32, i32, i32);
const SUPERSET_DELTAS: &[Delta] = &[
(-1, -1, -1),
(-1, -1, 0),
(-1, -1, 1),
(-1, 0, -1),
(-1, 0, 0),
(-1, 0, 1),
(-1, 1, -1),
(-1, 1, 0),
(-1, 1, 1),
(0, -1, -1),
(0, -1, 0),
(0, -1, 1),
(0, 0, -1),
(0, 0, 1),
(0, 1, -1),
(0, 1, 0),
(0, 1, 1),
(1, -1, -1),
(1, -1, 0),
(1, -1, 1),
(1, 0, -1),
(1, 0, 0),
(1, 0, 1),
(1, 1, -1),
(1, 1, 0),
(1, 1, 1),
];
const PARITY_EVEN_DELTAS: &[Delta] = &[(1, 0, 0), (0, 1, 0), (0, 0, 1)];
const PARITY_ODD_DELTAS: &[Delta] = &[(-1, 0, 0), (0, -1, 0), (0, 0, -1)];
pub fn get_lattice_neighbors(cell_id: u64, edge_only: bool) -> Vec<u64> {
let src = match decode_source(cell_id) {
Some(s) => s,
None => return get_global_cell_neighbors(cell_id, edge_only),
};
let deltas: &[Delta] = if edge_only {
if triple_parity(&src.triple) == 0 {
PARITY_EVEN_DELTAS
} else {
PARITY_ODD_DELTAS
}
} else {
SUPERSET_DELTAS
};
let mut result: Vec<u64> = Vec::new();
for &(dx, dy, dz) in deltas {
let candidate = Triple::new(src.triple.x + dx, src.triple.y + dy, src.triple.z + dz);
if !triple_in_bounds(&candidate, src.max_row) {
continue;
}
if let Some(candidate_s) = triple_to_s(&candidate, src.hilbert_res, src.orientation) {
if candidate_s < src.max_s && candidate_s != src.s {
if let Ok(cell_id) = serialize(&A5Cell {
origin_id: src.origin.id,
segment: src.segment,
s: candidate_s,
resolution: src.resolution,
}) {
result.push(cell_id);
}
}
}
}
for c in get_boundary_neighbors(&boundary_context(&src), edge_only, edge_only) {
result.push(c);
}
result
}