panasyn 0.1.0

A lightweight GPU-accelerated terminal emulator for macOS and Linux.
use winit::dpi::PhysicalPosition;

/// Convert a pixel position from a mouse event into terminal cell coordinates.
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);
    }
}