use crate::{
Grid,
any_angle::geometry::{is_endpoint_valid, sampling_segment_is_legal},
};
use condor_core::Point2;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RowRun {
pub left: f64,
pub right: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RowRunIndex {
runs_by_row: Vec<Vec<RowRun>>,
width: usize,
height: usize,
}
impl RowRunIndex {
#[must_use]
pub fn build(grid: &Grid) -> Self {
let width = grid.width();
let height = grid.height();
let mut runs_by_row = Vec::with_capacity(height + 1);
for row in 0..=height {
runs_by_row.push(build_row_runs(grid, row));
}
Self {
runs_by_row,
width,
height,
}
}
#[must_use]
pub fn runs_on_row(&self, row: i32) -> &[RowRun] {
if row < 0 || row as usize >= self.runs_by_row.len() {
return &[];
}
&self.runs_by_row[row as usize]
}
#[must_use]
pub fn run_containing(&self, point: Point2) -> Option<RowRun> {
let row = point.y.round() as i32;
let x = point.x;
self.runs_on_row(row)
.iter()
.copied()
.find(|run| x + 1e-12 >= run.left && x <= run.right + 1e-12)
}
#[must_use]
pub const fn height(&self) -> usize {
self.height
}
#[must_use]
pub fn bytes(&self) -> usize {
self.runs_by_row.len() * std::mem::size_of::<Vec<RowRun>>()
+ self
.runs_by_row
.iter()
.map(|row| row.len() * std::mem::size_of::<RowRun>())
.sum::<usize>()
}
}
fn build_row_runs(grid: &Grid, row: usize) -> Vec<RowRun> {
let y = row as f64;
let width = grid.width();
let mut runs = Vec::new();
let mut x = 0usize;
while x <= width {
let start = Point2::new(x as f64, y);
if !is_endpoint_valid(grid, start) {
x += 1;
continue;
}
let mut end = x;
while end < width {
let next = Point2::new((end + 1) as f64, y);
if !is_endpoint_valid(grid, next)
|| !sampling_segment_is_legal(grid, Point2::new(end as f64, y), next)
{
break;
}
end += 1;
}
runs.push(RowRun {
left: x as f64,
right: end as f64,
});
x = end + 1;
}
runs
}