devpage_design/tiles/
mod.rs1use kurbo::Rect as KRect;
7
8use crate::display::List;
9use crate::scene::Theme;
10
11mod cache;
12mod plan;
13
14pub use cache::*;
15pub use plan::*;
16
17pub const TILE: u32 = 256;
19
20pub fn tile_rect(list: &List, scale: f64, col: u32, row: u32) -> KRect {
23 square((list.area.x as f64, list.area.y as f64), scale, col, row)
24}
25
26pub fn square((x, y): (f64, f64), scale: f64, col: u32, row: u32) -> KRect {
29 let side = TILE as f64 / scale;
30 KRect::new(
31 x + col as f64 * side,
32 y + row as f64 * side,
33 x + (col + 1) as f64 * side,
34 y + (row + 1) as f64 * side,
35 )
36}
37
38#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
40pub struct Level(pub i64);
41
42impl Level {
43 pub fn of(scale: f64) -> Level {
45 Level((scale * 1024.0).round().max(1.0) as i64)
46 }
47
48 pub fn scale(self) -> f64 {
49 self.0 as f64 / 1024.0
50 }
51}
52
53#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
56pub struct Key {
57 pub theme: Theme,
58 pub level: Level,
59 pub col: u32,
60 pub row: u32,
61}
62
63impl Key {
64 pub fn rect(&self, origin: (f64, f64)) -> KRect {
67 square(origin, self.level.scale(), self.col, self.row)
68 }
69
70 pub fn overlaps(&self, origin: (f64, f64), rects: &[KRect]) -> bool {
73 let r = self.rect(origin);
74 rects
75 .iter()
76 .any(|d| d.x0 < r.x1 && d.x1 > r.x0 && d.y0 < r.y1 && d.y1 > r.y0)
77 }
78}
79
80pub trait Tiled: Copy + Eq + std::hash::Hash {
83 fn key(&self) -> Key;
84}
85
86impl Tiled for Key {
87 fn key(&self) -> Key {
88 *self
89 }
90}
91
92#[derive(Clone, Copy, Debug)]
95pub struct View {
96 pub width: f64,
97 pub height: f64,
98 pub zoom: f64,
99 pub origin: (f64, f64),
100}
101
102impl View {
103 pub fn tiles(&self, area: KRect, level: Level) -> Vec<(u32, u32)> {
106 let side = TILE as f64;
107 let scale = level.scale();
108 let (ox, oy) = self.origin;
109 let count = |v: f64| (v * scale / side).ceil() as i64;
110 let (cols, rows) = (count(area.width()), count(area.height()));
111 let span = |o: f64, n: f64, count: i64| {
112 let first = ((-o) / side).floor().max(0.0) as i64;
113 let last = (((n - o) / side).ceil() as i64).min(count);
114 first..last.max(first)
115 };
116 let (cs, rs) = (span(ox, self.width, cols), span(oy, self.height, rows));
117 let mut out: Vec<(u32, u32)> = rs
118 .flat_map(|r| cs.clone().map(move |c| (c as u32, r as u32)))
119 .collect();
120 let (mx, my) = (
121 (self.width / 2.0 - ox) / side,
122 (self.height / 2.0 - oy) / side,
123 );
124 let d = |p: &(u32, u32)| (p.0 as f64 + 0.5 - mx).powi(2) + (p.1 as f64 + 0.5 - my).powi(2);
125 out.sort_by(|a, b| d(a).total_cmp(&d(b)));
126 out
127 }
128
129 pub fn place(&self, area: KRect, key: &Key) -> Option<KRect> {
132 let rect = square((area.x0, area.y0), key.level.scale(), key.col, key.row);
133 let (ox, oy) = self.origin;
134 let to = |x: f64, y: f64| {
135 (
136 ox + (x - area.x0) * self.zoom,
137 oy + (y - area.y0) * self.zoom,
138 )
139 };
140 let (x0, y0) = to(rect.x0, rect.y0);
141 let (x1, y1) = to(rect.x1, rect.y1);
142 (x1 >= 0.0 && y1 >= 0.0 && x0 <= self.width && y0 <= self.height)
143 .then(|| KRect::new(x0, y0, x1, y1))
144 }
145}
146
147#[cfg(test)]
148mod tests;