use winit::dpi::PhysicalPosition;
pub fn pixel_to_cell(
pos: PhysicalPosition<f64>,
cell_width: f32,
cell_height: f32,
padding_x: f32,
padding_y: f32,
rows: usize,
cols: usize,
) -> Option<(usize, usize)> {
pixel_to_cell_with_fraction(
pos,
cell_width,
cell_height,
padding_x,
padding_y,
rows,
cols,
)
.map(|hit| (hit.row, hit.col))
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CellHit {
pub row: usize,
pub col: usize,
pub x_fraction: f32,
pub y_fraction: f32,
}
pub fn pixel_to_cell_with_fraction(
pos: PhysicalPosition<f64>,
cell_width: f32,
cell_height: f32,
padding_x: f32,
padding_y: f32,
rows: usize,
cols: usize,
) -> Option<CellHit> {
if cell_width <= 0.0 || cell_height <= 0.0 {
return None;
}
let col_f = (pos.x as f32 - padding_x) / cell_width;
let row_f = (pos.y as f32 - padding_y) / cell_height;
let col = col_f.floor();
let row = row_f.floor();
if col < 0.0 || row < 0.0 || col >= cols as f32 || row >= rows as f32 {
return None;
}
Some(CellHit {
row: row as usize,
col: col as usize,
x_fraction: (col_f - col).clamp(0.0, 1.0),
y_fraction: (row_f - row).clamp(0.0, 1.0),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pixel_to_cell_rejects_positions_before_padding() {
let pos = PhysicalPosition::new(19.9, 10.0);
assert_eq!(pixel_to_cell(pos, 10.0, 20.0, 20.0, 0.0, 3, 8), None);
}
#[test]
fn pixel_to_cell_reports_fraction_within_cell() {
let pos = PhysicalPosition::new(34.0, 25.0);
let hit = pixel_to_cell_with_fraction(pos, 10.0, 20.0, 20.0, 0.0, 3, 8).unwrap();
assert_eq!(hit.row, 1);
assert_eq!(hit.col, 1);
assert!((hit.x_fraction - 0.4).abs() < f32::EPSILON);
assert!((hit.y_fraction - 0.25).abs() < f32::EPSILON);
}
}