use embedded_graphics::{prelude::*, primitives::Rectangle};
use heapless::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GridPosition {
pub row: u8,
pub col: u8,
pub row_span: u8,
pub col_span: u8,
}
impl GridPosition {
pub fn new(row: u8, col: u8) -> Self {
Self {
row,
col,
row_span: 1,
col_span: 1,
}
}
pub fn with_span(row: u8, col: u8, row_span: u8, col_span: u8) -> Self {
Self {
row,
col,
row_span: row_span.max(1),
col_span: col_span.max(1),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GridLayout {
pub rows: u8,
pub cols: u8,
}
impl GridLayout {
pub fn new(rows: u8, cols: u8) -> Self {
Self {
rows: rows.max(1),
cols: cols.max(1),
}
}
pub fn calculate_cell_viewport(
&self,
total_viewport: Rectangle,
position: GridPosition,
spacing: u32,
) -> Rectangle {
let total_width = total_viewport.size.width;
let total_height = total_viewport.size.height;
let h_spacing = spacing * (self.cols as u32 - 1);
let v_spacing = spacing * (self.rows as u32 - 1);
let cell_width = (total_width.saturating_sub(h_spacing)) / self.cols as u32;
let cell_height = (total_height.saturating_sub(v_spacing)) / self.rows as u32;
let x = total_viewport.top_left.x
+ (position.col as i32 * (cell_width as i32 + spacing as i32));
let y = total_viewport.top_left.y
+ (position.row as i32 * (cell_height as i32 + spacing as i32));
let width = (cell_width * position.col_span as u32)
+ (spacing * position.col_span.saturating_sub(1) as u32);
let height = (cell_height * position.row_span as u32)
+ (spacing * position.row_span.saturating_sub(1) as u32);
Rectangle::new(Point::new(x, y), Size::new(width, height))
}
pub fn calculate_viewports<const N: usize>(
&self,
total_viewport: Rectangle,
positions: &[GridPosition],
spacing: u32,
) -> crate::error::ChartResult<Vec<Rectangle, N>> {
let mut viewports = Vec::new();
for position in positions {
let viewport = self.calculate_cell_viewport(total_viewport, *position, spacing);
viewports
.push(viewport)
.map_err(|_| crate::error::ChartError::MemoryFull)?;
}
Ok(viewports)
}
}
impl Default for GridLayout {
fn default() -> Self {
Self::new(2, 2)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grid_position() {
let pos = GridPosition::new(1, 2);
assert_eq!(pos.row, 1);
assert_eq!(pos.col, 2);
assert_eq!(pos.row_span, 1);
assert_eq!(pos.col_span, 1);
let span_pos = GridPosition::with_span(0, 0, 2, 3);
assert_eq!(span_pos.row_span, 2);
assert_eq!(span_pos.col_span, 3);
}
#[test]
fn test_grid_layout_2x2() {
let layout = GridLayout::new(2, 2);
let viewport = Rectangle::new(Point::new(0, 0), Size::new(200, 200));
let cell = layout.calculate_cell_viewport(
viewport,
GridPosition::new(0, 0),
10, );
assert_eq!(cell.top_left, Point::new(0, 0));
assert_eq!(cell.size, Size::new(95, 95));
let cell = layout.calculate_cell_viewport(viewport, GridPosition::new(1, 1), 10);
assert_eq!(cell.top_left, Point::new(105, 105)); assert_eq!(cell.size, Size::new(95, 95));
}
#[test]
fn test_grid_layout_with_span() {
let layout = GridLayout::new(3, 3);
let viewport = Rectangle::new(Point::new(0, 0), Size::new(320, 320));
let cell =
layout.calculate_cell_viewport(viewport, GridPosition::with_span(0, 0, 2, 2), 10);
assert_eq!(cell.top_left, Point::new(0, 0));
assert_eq!(cell.size, Size::new(210, 210)); }
}