use crate::config::{RoiHint, RoiKind};
#[derive(Debug, Clone)]
pub struct TileMask {
bits: Box<[bool]>,
}
impl TileMask {
pub fn empty(len: usize) -> Self {
Self {
bits: vec![false; len].into_boxed_slice(),
}
}
#[inline]
pub fn get(&self, idx: usize) -> bool {
self.bits.get(idx).copied().unwrap_or(false)
}
#[inline]
pub fn set(&mut self, idx: usize) {
if let Some(b) = self.bits.get_mut(idx) {
*b = true;
}
}
pub fn any(&self) -> bool {
self.bits.iter().any(|b| *b)
}
pub fn len(&self) -> usize {
self.bits.len()
}
pub fn is_empty(&self) -> bool {
self.bits.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct Region {
pub label: String,
pub kind: RoiKind,
pub tiles: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct RoiSet {
cols: u16,
rows: u16,
regions: Vec<Region>,
ignore: TileMask,
excluded: TileMask,
watch: TileMask,
}
impl RoiSet {
pub fn build(hints: &[RoiHint], cols: u16, rows: u16) -> Self {
let len = cols as usize * rows as usize;
let mut ignore = TileMask::empty(len);
let mut excluded = TileMask::empty(len);
let mut watch = TileMask::empty(len);
let mut regions = Vec::with_capacity(hints.len());
for h in hints {
let tiles = tiles_for_rect(h.rect_norm, cols, rows);
for &t in &tiles {
match h.kind {
RoiKind::Ignore => ignore.set(t),
RoiKind::Spinner | RoiKind::Volatile => excluded.set(t),
RoiKind::Watch => watch.set(t),
}
}
regions.push(Region {
label: h.label.clone(),
kind: h.kind,
tiles,
});
}
Self {
cols,
rows,
regions,
ignore,
excluded,
watch,
}
}
pub fn cols(&self) -> u16 {
self.cols
}
pub fn rows(&self) -> u16 {
self.rows
}
pub fn regions(&self) -> &[Region] {
&self.regions
}
pub fn ignore_mask(&self) -> &TileMask {
&self.ignore
}
#[inline]
pub fn is_excluded(&self, idx: usize) -> bool {
self.excluded.get(idx)
}
#[inline]
pub fn is_watch(&self, idx: usize) -> bool {
self.watch.get(idx)
}
pub fn region_indices_of_kind(&self, kind: RoiKind) -> impl Iterator<Item = usize> + '_ {
self.regions
.iter()
.enumerate()
.filter(move |(_, r)| r.kind == kind)
.map(|(i, _)| i)
}
}
pub fn tiles_for_rect(rect_norm: [f32; 4], cols: u16, rows: u16) -> Vec<usize> {
let [x, y, w, h] = rect_norm;
let cols_f = cols as f32;
let rows_f = rows as f32;
let c0 = (x * cols_f).floor().clamp(0.0, cols_f) as i64;
let c1 = ((x + w) * cols_f).ceil().clamp(0.0, cols_f) as i64;
let r0 = (y * rows_f).floor().clamp(0.0, rows_f) as i64;
let r1 = ((y + h) * rows_f).ceil().clamp(0.0, rows_f) as i64;
let mut out = Vec::new();
for r in r0..r1.max(r0) {
for c in c0..c1.max(c0) {
out.push((r as usize) * cols as usize + c as usize);
}
}
if out.is_empty() && w > 0.0 && h > 0.0 {
let c = ((x * cols_f) as i64).clamp(0, cols as i64 - 1) as usize;
let r = ((y * rows_f) as i64).clamp(0, rows as i64 - 1) as usize;
out.push(r * cols as usize + c);
}
out
}