use crate::cell::Cell;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Rect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
impl Rect {
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
Self { x, y, width, height }
}
pub fn from_size(width: u16, height: u16) -> Self {
Self {
x: 0,
y: 0,
width,
height,
}
}
pub fn contains(&self, x: u16, y: u16) -> bool {
x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
}
pub fn inner(&self, border: u16) -> Self {
Rect::new(
self.x + border,
self.y + border,
self.width.saturating_sub(border * 2),
self.height.saturating_sub(border * 2),
)
}
pub fn right(&self) -> u16 {
self.x + self.width
}
pub fn bottom(&self) -> u16 {
self.y + self.height
}
pub fn is_valid(&self) -> bool {
self.width > 0 && self.height > 0
}
pub fn intersect(&self, other: &Rect) -> Option<Rect> {
let x = self.x.max(other.x);
let y = self.y.max(other.y);
let right = self.right().min(other.right());
let bottom = self.bottom().min(other.bottom());
if right > x && bottom > y {
Some(Rect::new(x, y, right - x, bottom - y))
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub struct Surface {
width: u16,
height: u16,
cells: Vec<Vec<Cell>>,
dirty_cells: Vec<Vec<bool>>,
first_dirty_row: Option<u16>,
last_dirty_row: Option<u16>,
}
impl Surface {
pub fn new(width: u16, height: u16) -> Self {
let cells = vec![vec![Cell::default(); width as usize]; height as usize];
let dirty_cells = vec![vec![false; width as usize]; height as usize];
Self {
width,
height,
cells,
dirty_cells,
first_dirty_row: None,
last_dirty_row: None,
}
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
pub fn area(&self) -> Rect {
Rect::from_size(self.width, self.height)
}
pub fn get(&self, row: u16, col: u16) -> Option<&Cell> {
if row < self.height && col < self.width {
self.cells.get(row as usize).and_then(|r| r.get(col as usize))
} else {
None
}
}
pub fn get_mut(&mut self, row: u16, col: u16) -> Option<&mut Cell> {
if row < self.height && col < self.width {
let r = self.cells.get_mut(row as usize)?;
r.get_mut(col as usize)
} else {
None
}
}
pub fn set(&mut self, row: u16, col: u16, cell: Cell) {
if row < self.height && col < self.width {
self.cells[row as usize][col as usize] = cell;
self.mark_dirty(row, col);
}
}
pub fn write_string(&mut self, row: u16, col: u16, s: &str) {
for (i, c) in s.chars().enumerate() {
let col = col + i as u16;
if col >= self.width {
break;
}
self.set(row, col, Cell::new(c));
}
}
pub fn fill_row(&mut self, row: u16, start_col: u16, end_col: u16, cell: Cell) {
for col in start_col..end_col.min(self.width) {
self.set(row, col, cell.clone());
}
}
pub fn fill(&mut self, cell: Cell) {
for row in 0..self.height {
for col in 0..self.width {
self.set(row, col, cell.clone());
}
}
}
pub fn clear(&mut self) {
for row in 0..self.height {
for col in 0..self.width {
let mut c = Cell::default();
c.reset();
self.cells[row as usize][col as usize] = c;
}
}
self.clear_dirty();
}
fn mark_dirty(&mut self, row: u16, col: u16) {
self.dirty_cells[row as usize][col as usize] = true;
self.first_dirty_row = Some(self.first_dirty_row.map_or(row, |r| r.min(row)));
self.last_dirty_row = Some(self.last_dirty_row.map_or(row, |r| r.max(row)));
}
pub fn is_dirty(&self, row: u16, col: u16) -> bool {
if row < self.height && col < self.width {
self.dirty_cells[row as usize][col as usize]
} else {
false
}
}
pub fn first_dirty(&self) -> Option<u16> {
self.first_dirty_row
}
pub fn last_dirty(&self) -> Option<u16> {
self.last_dirty_row
}
pub fn is_any_dirty(&self) -> bool {
self.first_dirty_row.is_some()
}
pub fn clear_dirty(&mut self) {
for row in &mut self.dirty_cells {
for cell in row.iter_mut() {
*cell = false;
}
}
self.first_dirty_row = None;
self.last_dirty_row = None;
}
pub fn mark_all_dirty(&mut self) {
for row in 0..self.height {
for col in 0..self.width {
self.dirty_cells[row as usize][col as usize] = true;
}
}
self.first_dirty_row = Some(0);
self.last_dirty_row = Some(self.height.saturating_sub(1));
}
pub fn diff_from(&mut self, other: &Surface) {
for row in 0..self.height.min(other.height) {
for col in 0..self.width.min(other.width) {
if self.cells[row as usize][col as usize]
!= other.cells[row as usize][col as usize]
{
self.mark_dirty(row, col);
}
}
}
}
}
impl Default for Surface {
fn default() -> Self {
Self::new(80, 24)
}
}