use kurbo::Rect as KRect;
use crate::display::List;
use crate::resolve::Theme;
mod cache;
pub use cache::*;
pub const TILE: u32 = 256;
pub fn tile_rect(list: &List, scale: f64, col: u32, row: u32) -> KRect {
square((list.area.x as f64, list.area.y as f64), scale, col, row)
}
pub fn square((x, y): (f64, f64), scale: f64, col: u32, row: u32) -> KRect {
let side = TILE as f64 / scale;
KRect::new(
x + col as f64 * side,
y + row as f64 * side,
x + (col + 1) as f64 * side,
y + (row + 1) as f64 * side,
)
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct Level(pub i64);
impl Level {
pub fn of(scale: f64) -> Level {
Level((scale * 1024.0).round().max(1.0) as i64)
}
pub fn scale(self) -> f64 {
self.0 as f64 / 1024.0
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Key {
pub theme: Theme,
pub level: Level,
pub col: u32,
pub row: u32,
}
impl Key {
pub fn rect(&self, origin: (f64, f64)) -> KRect {
square(origin, self.level.scale(), self.col, self.row)
}
}
pub trait Tiled: Copy + Eq + std::hash::Hash {
fn key(&self) -> Key;
}
impl Tiled for Key {
fn key(&self) -> Key {
*self
}
}
#[derive(Clone, Copy, Debug)]
pub struct View {
pub width: f64,
pub height: f64,
pub zoom: f64,
pub origin: (f64, f64),
}
impl View {
pub fn tiles(&self, area: KRect, level: Level) -> Vec<(u32, u32)> {
let side = TILE as f64;
let scale = level.scale();
let (ox, oy) = self.origin;
let count = |v: f64| (v * scale / side).ceil() as i64;
let (cols, rows) = (count(area.width()), count(area.height()));
let span = |o: f64, n: f64, count: i64| {
let first = ((-o) / side).floor().max(0.0) as i64;
let last = (((n - o) / side).ceil() as i64).min(count);
first..last.max(first)
};
let (cs, rs) = (span(ox, self.width, cols), span(oy, self.height, rows));
let mut out: Vec<(u32, u32)> = rs
.flat_map(|r| cs.clone().map(move |c| (c as u32, r as u32)))
.collect();
let (mx, my) = (
(self.width / 2.0 - ox) / side,
(self.height / 2.0 - oy) / side,
);
let d = |p: &(u32, u32)| (p.0 as f64 + 0.5 - mx).powi(2) + (p.1 as f64 + 0.5 - my).powi(2);
out.sort_by(|a, b| d(a).total_cmp(&d(b)));
out
}
pub fn place(&self, area: KRect, key: &Key) -> Option<KRect> {
let rect = square((area.x0, area.y0), key.level.scale(), key.col, key.row);
let (ox, oy) = self.origin;
let to = |x: f64, y: f64| {
(
ox + (x - area.x0) * self.zoom,
oy + (y - area.y0) * self.zoom,
)
};
let (x0, y0) = to(rect.x0, rect.y0);
let (x1, y1) = to(rect.x1, rect.y1);
(x1 >= 0.0 && y1 >= 0.0 && x0 <= self.width && y0 <= self.height)
.then(|| KRect::new(x0, y0, x1, y1))
}
}
#[cfg(test)]
mod tests;