plt_draw/
lib.rs

1use std::{io, path};
2
3/// The error type for this library.
4#[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    // TODO
12    #[error("{0}")]
13    UnsupportedFileFormat(String),
14    #[error("{0}")]
15    UnsupportedImageFormat(String),
16    #[error("{0}")]
17    UnsupportedShape(String),
18}
19
20/// 2D size in dot (pixel) numbers.
21#[derive(Copy, Clone, Debug)]
22pub struct Size {
23    pub width: u32,
24    pub height: u32,
25}
26
27/// Arbitrary point.
28#[derive(Copy, Clone, Debug)]
29pub struct Point {
30    /// The x-position of the point.
31    pub x: f64,
32    /// The y-position of the point.
33    pub y: f64,
34}
35
36/// A line from point-1 to point-2.
37#[derive(Copy, Clone, Debug)]
38pub struct Line {
39    /// The first point, drawn from.
40    pub p1: Point,
41    /// The second point, drawn to.
42    pub p2: Point,
43}
44
45/// Subarea of a 2D figure by dot (pixel) indices.
46#[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    /// Get the width of the area.
55    pub fn xsize(&self) -> u32 {
56        self.xmax - self.xmin
57    }
58    /// Get the height of the area.
59    pub fn ysize(&self) -> u32 {
60        self.ymax - self.ymin
61    }
62    /// Convert a fractional point, to a dot (pixel) point.
63    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/// An RGBA float representation of a color.
72#[derive(Copy, Clone, Debug)]
73pub struct Color {
74    /// Amount of red, from 0.0 to 1.0.
75    pub r: f64,
76    /// Amount of green, from 0.0 to 1.0.
77    pub g: f64,
78    /// Amount of blue, from 0.0 to 1.0.
79    pub b: f64,
80    /// Amount of alpha, from 0.0 to 1.0.
81    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/// A drawable shape.
96#[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    /// Scales the shape by some multiplicative factor.
105    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/// Complete font settings.
115#[derive(Copy, Clone, Debug)]
116pub struct Font {
117    /// Name of the font used.
118    pub name: FontName,
119    /// Size of the font.
120    pub size: f32,
121    /// Slant of the font.
122    pub slant: FontSlant,
123    /// Weight of the font.
124    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/// The name of a text font.
138#[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/// The slant of a font.
151#[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/// The weight of a font.
164#[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/// How something should be aligned.
176#[derive(Copy, Clone, Debug)]
177pub enum Alignment {
178    /// Aligned to the center.
179    Center,
180    /// Aligned to the center of the left side.
181    Left,
182    /// Aligned to the center of the right side.
183    Right,
184    /// Aligned to the center of the top side.
185    Top,
186    /// Aligned to the center of the bottom side.
187    Bottom,
188    /// Aligned to the top left corner.
189    TopLeft,
190    /// Aligned to the top right corner.
191    TopRight,
192    /// Aligned to the bottom left corner.
193    BottomLeft,
194    /// Aligned to the bottom right corner.
195    BottomRight,
196}
197
198/// A graphics image file format.
199#[non_exhaustive]
200#[derive(Copy, Clone, Debug)]
201pub enum FileFormat {
202    /// A PNG file format.
203    Png,
204    /// An SVG file format.
205    Svg,
206}
207
208/// Describes a [`Canvas`] to be constructed.
209#[derive(Clone, Debug)]
210pub struct CanvasDescriptor {
211    /// The size in dots (pixels) of the canvas.
212    pub size: Size,
213    /// The background color of the canvas.
214    pub face_color: Color,
215    /// What type of image format will be drawn.
216    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    /// An image represented as a bitmap or pixel map.
232    Bitmap,
233    /// An image represented as an SVG image.
234    Svg,
235}
236
237/// Describes a shape to be drawn.
238#[derive(Clone, Debug)]
239pub struct ShapeDescriptor<'a> {
240    /// The point at which the shape is drawn.
241    pub point: Point,
242    /// The shape to be drawn.
243    pub shape: Shape,
244    /// The fill color of the shape.
245    pub fill_color: Color,
246    /// The width of the outline line.
247    pub line_width: u32,
248    /// The color of the outline.
249    pub line_color: Color,
250    /// How the outline will be dashed.
251    pub line_dashes: &'a [f64],
252    /// Optionally clip drawing to some area.
253    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/// Describes a line to be drawn.
270#[derive(Clone, Debug)]
271pub struct LineDescriptor<'a> {
272    /// Where to draw the line.
273    pub line: Line,
274    /// The width of the line.
275    pub line_width: u32,
276    /// The color of the line.
277    pub line_color: Color,
278    /// How the line will be dashed.
279    pub dashes: &'a [f64],
280    /// Optionally clip drawing to some area.
281    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    /// Where to draw the curve
301    pub points: Vec<Point>,
302    /// The width of the line.
303    pub line_width: u32,
304    /// The color of the line.
305    pub line_color: Color,
306    /// How the line will be dashed.
307    pub dashes: &'a [f64],
308    /// Optionally clip drawing to some area.
309    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    /// The text to be drawn.
326    pub text: String,
327    /// The font to draw the text in.
328    pub font: Font,
329    /// Where to draw the text.
330    pub position: Point,
331    /// The color of the text.
332    pub color: Color,
333    /// How the text should be rotated.
334    pub rotation: f64,
335    /// What side of the text to align to the position.
336    pub alignment: Alignment,
337    /// Optionally clip drawing to some area.
338    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/// Describes a region to be filled with a specified color.
355#[derive(Clone, Debug)]
356pub struct FillDescriptor {
357    /// Points the define the region of interest.
358    pub points: Vec<Point>,
359    /// The color of the region.
360    pub fill_color: Color,
361    /// Optionally clip drawing to some area.
362    pub clip_area: Option<Area>,
363}
364
365/// Describes how to save the image to a file.
366#[derive(Clone, Debug)]
367pub struct SaveFileDescriptor<P: AsRef<path::Path>> {
368    /// The name of the output file.
369    pub filename: P,
370    /// The image format of the file.
371    pub format: FileFormat,
372    /// The dots (pixels) per inch.
373    pub dpi: u16,
374}
375
376/// Represents a structure used for drawing.
377pub trait Canvas {
378    /// The main constructor.
379    fn new(desc: CanvasDescriptor) -> Result<Self, DrawError> where Self: Sized;
380    /// Draws a shape described by a [`ShapeDescriptor`].
381    fn draw_shape(&mut self, desc: ShapeDescriptor) -> Result<(), DrawError>;
382    /// Draws a line described by a [`LineDescriptor`].
383    fn draw_line(&mut self, desc: LineDescriptor) -> Result<(), DrawError>;
384    /// Draws a curve described by a [`CurveDescriptor`].
385    fn draw_curve(&mut self, desc: CurveDescriptor) -> Result<(), DrawError>;
386    /// Draws color in a closed, arbitrary region described by a [`FillDescriptor`].
387    fn fill_region(&mut self, desc: FillDescriptor) -> Result<(), DrawError>;
388    /// Draws text described by a [`TextDescriptor`].
389    fn draw_text(&mut self, desc: TextDescriptor) -> Result<(), DrawError>;
390    /// Returns a [`Size`] representing the extent of the text described by a [`TextDescriptor`].
391    fn text_size(&mut self, desc: TextDescriptor) -> Result<Size, DrawError>;
392    /// Save the image to a file.
393    fn save_file<P: AsRef<path::Path>>(
394        &mut self,
395        desc: SaveFileDescriptor<P>,
396    ) -> Result<(), DrawError>;
397    /// Get canvas size.
398    fn size(&self) -> Result<Size, DrawError>;
399}