1use std::{io, path};
2
3#[non_exhaustive]
5#[derive(thiserror::Error, Debug)]
6pub enum DrawError {
7 #[error(transparent)]
8 BackendError(#[from] anyhow::Error),
9 #[error(transparent)]
10 IoError(#[from] io::Error),
11 #[error("{0}")]
13 UnsupportedFileFormat(String),
14 #[error("{0}")]
15 UnsupportedImageFormat(String),
16 #[error("{0}")]
17 UnsupportedShape(String),
18}
19
20#[derive(Copy, Clone, Debug)]
22pub struct Size {
23 pub width: u32,
24 pub height: u32,
25}
26
27#[derive(Copy, Clone, Debug)]
29pub struct Point {
30 pub x: f64,
32 pub y: f64,
34}
35
36#[derive(Copy, Clone, Debug)]
38pub struct Line {
39 pub p1: Point,
41 pub p2: Point,
43}
44
45#[derive(Copy, Clone, Debug)]
47pub struct Area {
48 pub xmin: u32,
49 pub xmax: u32,
50 pub ymin: u32,
51 pub ymax: u32,
52}
53impl Area {
54 pub fn xsize(&self) -> u32 {
56 self.xmax - self.xmin
57 }
58 pub fn ysize(&self) -> u32 {
60 self.ymax - self.ymin
61 }
62 pub fn fractional_to_point(&self, frac: Point) -> Point {
64 Point {
65 x: self.xmin as f64 + (frac.x * self.xsize() as f64),
66 y: self.ymin as f64 + (frac.y * self.ysize() as f64),
67 }
68 }
69}
70
71#[derive(Copy, Clone, Debug)]
73pub struct Color {
74 pub r: f64,
76 pub g: f64,
78 pub b: f64,
80 pub a: f64,
82}
83impl Color {
84 pub const TRANSPARENT: Color = Self { r: 0.0, g: 0.0, b: 0.0, a: 0.0, };
85 pub const BLACK: Color = Self { r: 0.0, g: 0.0, b: 0.0, a: 1.0, };
86 pub const WHITE: Color = Self { r: 1.0, g: 1.0, b: 1.0, a: 1.0, };
87 pub const RED: Color = Self { r: 1.0, g: 0.0, b: 0.0, a: 1.0, };
88 pub const ORANGE: Color = Self { r: 1.0, g: 0.64, b: 0.0, a: 1.0, };
89 pub const YELLOW: Color = Self { r: 1.0, g: 1.0, b: 0.0, a: 1.0, };
90 pub const GREEN: Color = Self { r: 0.0, g: 1.0, b: 0.0, a: 1.0, };
91 pub const BLUE: Color = Self { r: 0.0, g: 0.0, b: 1.0, a: 1.0, };
92 pub const PURPLE: Color = Self { r: 0.62, g: 0.12, b: 0.94, a: 1.0, };
93}
94
95#[non_exhaustive]
97#[derive(Copy, Clone, Debug)]
98pub enum Shape {
99 Circle { r: u32 },
100 Square { l: u32 },
101 Rectangle { h: u32, w: u32 },
102}
103impl Shape {
104 pub fn scale(&mut self, mult: u32) {
106 *self = match self {
107 Shape::Circle { r } => Shape::Circle { r: mult * *r },
108 Shape::Square { l } => Shape::Square { l: mult * *l },
109 Shape::Rectangle { h, w } => Shape::Rectangle { h: mult * *h, w: mult * *w },
110 }
111 }
112}
113
114#[derive(Copy, Clone, Debug)]
116pub struct Font {
117 pub name: FontName,
119 pub size: f32,
121 pub slant: FontSlant,
123 pub weight: FontWeight,
125}
126impl Default for Font {
127 fn default() -> Self {
128 Self {
129 name: FontName::default(),
130 size: 12.0,
131 slant: FontSlant::default(),
132 weight: FontWeight::default(),
133 }
134 }
135}
136
137#[non_exhaustive]
139#[derive(Copy, Clone, Debug)]
140pub enum FontName {
141 Arial,
142 Georgia,
143}
144impl Default for FontName {
145 fn default() -> Self {
146 Self::Arial
147 }
148}
149
150#[derive(Copy, Clone, Debug)]
152pub enum FontSlant {
153 Normal,
154 Italic,
155 Oblique,
156}
157impl Default for FontSlant {
158 fn default() -> Self {
159 Self::Normal
160 }
161}
162
163#[derive(Copy, Clone, Debug)]
165pub enum FontWeight {
166 Normal,
167 Bold,
168}
169impl Default for FontWeight {
170 fn default() -> Self {
171 Self::Normal
172 }
173}
174
175#[derive(Copy, Clone, Debug)]
177pub enum Alignment {
178 Center,
180 Left,
182 Right,
184 Top,
186 Bottom,
188 TopLeft,
190 TopRight,
192 BottomLeft,
194 BottomRight,
196}
197
198#[non_exhaustive]
200#[derive(Copy, Clone, Debug)]
201pub enum FileFormat {
202 Png,
204 Svg,
206}
207
208#[derive(Clone, Debug)]
210pub struct CanvasDescriptor {
211 pub size: Size,
213 pub face_color: Color,
215 pub image_format: ImageFormat,
217}
218impl Default for CanvasDescriptor {
219 fn default() -> Self {
220 Self {
221 size: Size { height: 100, width: 100 },
222 face_color: Color::WHITE,
223 image_format: ImageFormat::Bitmap,
224 }
225 }
226}
227
228#[non_exhaustive]
229#[derive(Copy, Clone, Debug)]
230pub enum ImageFormat {
231 Bitmap,
233 Svg,
235}
236
237#[derive(Clone, Debug)]
239pub struct ShapeDescriptor<'a> {
240 pub point: Point,
242 pub shape: Shape,
244 pub fill_color: Color,
246 pub line_width: u32,
248 pub line_color: Color,
250 pub line_dashes: &'a [f64],
252 pub clip_area: Option<Area>,
254}
255impl Default for ShapeDescriptor<'_> {
256 fn default() -> Self {
257 Self {
258 point: Point { x: 0.0, y: 0.0 },
259 shape: Shape::Circle { r: 1 },
260 fill_color: Color::WHITE,
261 line_width: 2,
262 line_color: Color::BLACK,
263 line_dashes: &[],
264 clip_area: None,
265 }
266 }
267}
268
269#[derive(Clone, Debug)]
271pub struct LineDescriptor<'a> {
272 pub line: Line,
274 pub line_width: u32,
276 pub line_color: Color,
278 pub dashes: &'a [f64],
280 pub clip_area: Option<Area>,
282}
283impl Default for LineDescriptor<'_> {
284 fn default() -> Self {
285 Self {
286 line: Line {
287 p1: Point { x: 0.0, y: 0.0 },
288 p2: Point { x: 0.0, y: 0.0 },
289 },
290 line_width: 2,
291 line_color: Color::BLACK,
292 dashes: &[],
293 clip_area: None,
294 }
295 }
296}
297
298#[derive(Clone, Debug)]
299pub struct CurveDescriptor<'a> {
300 pub points: Vec<Point>,
302 pub line_width: u32,
304 pub line_color: Color,
306 pub dashes: &'a [f64],
308 pub clip_area: Option<Area>,
310}
311impl Default for CurveDescriptor<'_> {
312 fn default() -> Self {
313 Self {
314 points: vec![],
315 line_width: 2,
316 line_color: Color::BLACK,
317 dashes: &[],
318 clip_area: None,
319 }
320 }
321}
322
323#[derive(Clone, Debug)]
324pub struct TextDescriptor {
325 pub text: String,
327 pub font: Font,
329 pub position: Point,
331 pub color: Color,
333 pub rotation: f64,
335 pub alignment: Alignment,
337 pub clip_area: Option<Area>,
339}
340impl Default for TextDescriptor {
341 fn default() -> Self {
342 Self {
343 text: "".to_owned(),
344 font: Font::default(),
345 position: Point { x: 0.0, y: 0.0 },
346 color: Color::BLACK,
347 rotation: 0.0,
348 alignment: Alignment::Center,
349 clip_area: None,
350 }
351 }
352}
353
354#[derive(Clone, Debug)]
356pub struct FillDescriptor {
357 pub points: Vec<Point>,
359 pub fill_color: Color,
361 pub clip_area: Option<Area>,
363}
364
365#[derive(Clone, Debug)]
367pub struct SaveFileDescriptor<P: AsRef<path::Path>> {
368 pub filename: P,
370 pub format: FileFormat,
372 pub dpi: u16,
374}
375
376pub trait Canvas {
378 fn new(desc: CanvasDescriptor) -> Result<Self, DrawError> where Self: Sized;
380 fn draw_shape(&mut self, desc: ShapeDescriptor) -> Result<(), DrawError>;
382 fn draw_line(&mut self, desc: LineDescriptor) -> Result<(), DrawError>;
384 fn draw_curve(&mut self, desc: CurveDescriptor) -> Result<(), DrawError>;
386 fn fill_region(&mut self, desc: FillDescriptor) -> Result<(), DrawError>;
388 fn draw_text(&mut self, desc: TextDescriptor) -> Result<(), DrawError>;
390 fn text_size(&mut self, desc: TextDescriptor) -> Result<Size, DrawError>;
392 fn save_file<P: AsRef<path::Path>>(
394 &mut self,
395 desc: SaveFileDescriptor<P>,
396 ) -> Result<(), DrawError>;
397 fn size(&self) -> Result<Size, DrawError>;
399}