use super::RecastConfig;
use super::rasterize::Heightfield;
pub fn filter_low_hanging_obstacles(hf: &mut Heightfield, cfg: &RecastConfig) {
let walkable_climb = (cfg.agent_max_climb / cfg.cell_height) as u16;
for col in hf.spans.iter_mut() {
let n = col.len();
for i in 0..n {
if !col[i].walkable {
continue;
}
if i + 1 < n {
let cur_smax = col[i].smax;
let next_smin = col[i + 1].smin;
if next_smin.saturating_sub(cur_smax) <= walkable_climb {
col[i + 1].walkable = true;
}
}
}
}
}
pub fn filter_ledge_spans(hf: &mut Heightfield, cfg: &RecastConfig) {
let walkable_climb = (cfg.agent_max_climb / cfg.cell_height) as i32;
let w = hf.width;
let h = hf.height;
let mut to_unmark: Vec<(usize, usize)> = Vec::new();
for iz in 0..h {
for ix in 0..w {
let col_idx = hf.col_idx(ix, iz);
for si in 0..hf.spans[col_idx].len() {
if !hf.spans[col_idx][si].walkable {
continue;
}
let s_smax = hf.spans[col_idx][si].smax as i32;
let mut is_ledge = false;
for (dx, dz) in [(1i32, 0i32), (-1, 0), (0, 1), (0, -1)] {
let nx = ix as i32 + dx;
let nz = iz as i32 + dz;
if nx < 0 || nz < 0 || nx >= w as i32 || nz >= h as i32 {
is_ledge = true;
break;
}
let ncol = hf.col_idx(nx as usize, nz as usize);
if hf.spans[ncol].is_empty() {
is_ledge = true;
break;
}
let min_floor = hf.spans[ncol]
.iter()
.map(|ns| ns.smin as i32)
.min()
.unwrap_or(0);
if (s_smax - min_floor).abs() > walkable_climb {
is_ledge = true;
break;
}
}
if is_ledge {
to_unmark.push((col_idx, si));
}
}
}
}
for (col_idx, si) in to_unmark {
hf.spans[col_idx][si].walkable = false;
}
}
pub fn filter_walkable_low_height(hf: &mut Heightfield, cfg: &RecastConfig) {
let walkable_height = (cfg.agent_height / cfg.cell_height).ceil() as u16;
for col in hf.spans.iter_mut() {
let n = col.len();
for i in 0..n {
if !col[i].walkable {
continue;
}
let clearance = if i + 1 < n {
col[i + 1].smin.saturating_sub(col[i].smax)
} else {
u16::MAX
};
if clearance < walkable_height {
col[i].walkable = false;
}
}
}
}