1#![allow(missing_docs)]
2
3use std::fmt::Arguments;
6
7use crate::{graph::VertexIndex, numeric::v2f::V2f};
8
9pub mod svg;
10
11#[cfg(feature = "tiny_skia")]
12pub mod tiny_skia;
13
14#[cfg(feature = "imgui")]
15pub mod imgui;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct Color {
19 pub r: u8,
20 pub g: u8,
21 pub b: u8,
22 pub a: u8,
23}
24
25impl Color {
26 #[allow(clippy::unreadable_literal)]
27 pub const BLUE: Color = Color::from_hex(0x4e4afbff);
28
29 #[allow(clippy::unreadable_literal)]
30 pub const RED: Color = Color::from_hex(0xf92672ff);
31
32 #[allow(clippy::unreadable_literal)]
33 pub const BLACK: Color = Color::from_hex(0x000000ff);
34
35 #[allow(clippy::unreadable_literal)]
36 pub const LIGHT_GRAY: Color = Color::from_hex(0xccccccff);
37
38 #[allow(clippy::unreadable_literal)]
39 pub const DARK_GRAY: Color = Color::from_hex(0x444444ff);
40
41 #[must_use]
42 pub const fn from_hex(hex: u32) -> Color {
43 Color {
44 r: ((hex >> 24) & 0xff) as u8,
45 g: ((hex >> 16) & 0xff) as u8,
46 b: ((hex >> 8) & 0xff) as u8,
47 a: (hex & 0xff) as u8,
48 }
49 }
50
51 #[must_use]
52 pub const fn faded(self, alpha: u8) -> Color {
53 Color {
54 a: ((self.a as f32) * (alpha as f32 / 255.0)) as u8,
55 ..self
56 }
57 }
58}
59
60#[cfg(feature = "tiny_skia")]
61impl From<Color> for ::tiny_skia::Color {
62 fn from(color: Color) -> ::tiny_skia::Color {
63 ::tiny_skia::Color::from_rgba8(color.r, color.g, color.b, color.a)
64 }
65}
66
67#[cfg(feature = "imgui")]
68impl From<Color> for ::imgui::ImColor32 {
69 fn from(color: Color) -> ::imgui::ImColor32 {
70 ::imgui::ImColor32::from_rgba(color.r, color.g, color.b, color.a)
71 }
72}
73
74#[cfg(feature = "mint")]
75impl From<Color> for ::mint::Vector4<f32> {
76 fn from(color: Color) -> ::mint::Vector4<f32> {
77 ::mint::Vector4 {
78 x: color.r as f32 / 255.0,
79 y: color.g as f32 / 255.0,
80 z: color.b as f32 / 255.0,
81 w: color.a as f32 / 255.0,
82 }
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
87pub enum Tile {
88 Square {
89 color: Color,
90 },
91 Circle {
92 tile_color: Color,
93 circle_color: Color,
94 },
95 Char {
96 tile_color: Color,
97 text_color: Color,
98 letter: char,
99 },
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
103pub enum TextAlignment {
104 Left,
105 Center,
106 Right,
107}
108
109pub trait Canvas {
111 fn rect(&mut self, position: V2f, size: V2f, color: Color);
112
113 fn circle(&mut self, position: V2f, radius: f32, color: Color);
114
115 fn line(&mut self, start: V2f, end: V2f, weight: f32, color: Color);
116
117 fn text(&mut self, position: V2f, text: Arguments<'_>, alignment: TextAlignment, color: Color);
118
119 fn large_char(&mut self, letter: char, position: V2f, color: Color);
120
121 fn tile(&mut self, position: V2f, tile: Tile) {
122 let tile_size = Self::tile_size();
123 match tile {
124 Tile::Square { color } => {
125 self.rect(position, tile_size, color);
126 }
127 Tile::Circle {
128 tile_color,
129 circle_color,
130 } => {
131 self.rect(position, tile_size, tile_color);
132 self.circle(position + tile_size * 0.5, tile_size.x * 0.4, circle_color);
133 }
134 Tile::Char {
135 tile_color,
136 text_color,
137 letter,
138 } => {
139 self.rect(position, tile_size, tile_color);
140 self.large_char(letter, position, text_color);
141 }
142 }
143 }
144
145 fn highlight_tile(&mut self, position: V2f, color: Color) {
146 let tile_size = Self::tile_size();
147 let weight = Self::thick_line_weight() * 2.0;
148
149 self.line(
150 position,
151 position
152 + V2f {
153 x: tile_size.x,
154 y: 0.0,
155 },
156 weight,
157 color,
158 );
159 self.line(
160 position,
161 position
162 + V2f {
163 x: 0.0,
164 y: Self::tile_size().y,
165 },
166 weight,
167 color,
168 );
169 self.line(
170 position
171 + V2f {
172 x: tile_size.x,
173 y: 0.0,
174 },
175 position + tile_size,
176 weight,
177 color,
178 );
179 self.line(
180 position
181 + V2f {
182 x: 0.0,
183 y: tile_size.y,
184 },
185 position + tile_size,
186 weight,
187 color,
188 );
189 }
190
191 fn grid(&mut self, position: V2f, columns: u32, rows: u32) {
192 let cell_size = Self::tile_size();
193 let grid_weight = Self::thick_line_weight();
194
195 for row in 0..=rows {
196 let line_start = V2f {
197 x: position.x,
198 y: grid_weight.mul_add(
199 row as f32 + 0.5,
200 cell_size.y.mul_add(row as f32, position.y),
201 ),
202 };
203 let line_end = V2f {
204 x: grid_weight.mul_add(
205 (columns + 1) as f32,
206 cell_size.x.mul_add(columns as f32, position.x),
207 ),
208 y: grid_weight.mul_add(
209 row as f32 + 0.5,
210 cell_size.y.mul_add(row as f32, position.y),
211 ),
212 };
213 self.line(
214 line_start,
215 line_end,
216 Self::thick_line_weight(),
217 Color::BLACK,
218 );
219 }
220
221 for column in 0..=columns {
222 let line_start = V2f {
223 x: grid_weight.mul_add(
224 column as f32 + 0.5,
225 cell_size.x.mul_add(column as f32, position.x),
226 ),
227 y: position.y,
228 };
229 let line_end = V2f {
230 x: grid_weight.mul_add(
231 column as f32 + 0.5,
232 cell_size.x.mul_add(column as f32, position.x),
233 ),
234 y: grid_weight.mul_add(
235 (rows + 1) as f32,
236 cell_size.y.mul_add(rows as f32, position.y),
237 ),
238 };
239 self.line(
240 line_start,
241 line_end,
242 Self::thick_line_weight(),
243 Color::BLACK,
244 );
245 }
246 }
247
248 fn vertex(&mut self, position: V2f, color: Color, _idx: VertexIndex) {
249 let radius = Self::vertex_radius();
250 self.circle(position, radius, Color::BLACK);
251 self.circle(position, radius - 1.0, color);
252 }
253
254 fn tile_size() -> V2f;
255
256 fn vertex_radius() -> f32 {
257 Self::tile_size().x * 0.25
259 }
260
261 fn thick_line_weight() -> f32;
262
263 fn thin_line_weight() -> f32 {
264 Self::thick_line_weight() * 0.5
265 }
266
267 fn tile_position(x: u8, y: u8) -> V2f {
268 let tile_size = Self::tile_size();
269 let grid_weight = Self::thick_line_weight();
270 V2f {
271 x: (x as f32).mul_add(tile_size.x, (x + 1) as f32 * grid_weight),
272 y: (y as f32).mul_add(tile_size.y, (y + 1) as f32 * grid_weight),
273 }
274 }
275}
276
277#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
278pub struct BoundingBox {
279 pub top_left: V2f,
280 pub bottom_right: V2f,
281}
282
283impl BoundingBox {
284 pub fn size(self) -> V2f {
285 V2f {
286 x: f32::abs(self.bottom_right.x - self.top_left.x),
287 y: f32::abs(self.bottom_right.y - self.top_left.y),
288 }
289 }
290}
291
292pub trait Draw {
293 fn draw<C>(&self, canvas: &mut C)
295 where
296 C: Canvas;
297
298 fn required_canvas<C>(&self) -> BoundingBox
300 where
301 C: Canvas;
302}