dessin/lib.rs
1//! **dessin** is library aimed at building complex drawings, combine them, move them and export them as PDF or SVG.
2//!
3//! Details about the [macro][`crate::macros`].
4//!
5//! ## Example
6//!
7//! ```
8//! use dessin::prelude::*;
9//! use palette::{named, Srgb};
10//!
11//! #[derive(Default, Shape)]
12//! struct MyShape {
13//! text: String,
14//! }
15//! impl MyShape {
16//! fn say_this(&mut self, what: &str) {
17//! self.text = format!("{} And check this out: `{what}`", self.text);
18//! }
19//! }
20//! impl From<MyShape> for Shape {
21//! fn from(MyShape { text }: MyShape) -> Self {
22//! dessin!(*Text(fill = Srgb::<f32>::from_format(named::RED).into_linear(), { text })).into()
23//! }
24//! }
25//!
26//! fn main() {
27//! let dessin = dessin!(for x in 0..10 {
28//! let radius = x as f32 * 10.;
29//!
30//! dessin!([
31//! *Circle(
32//! fill = Srgb::<f32>::from_format(named::RED).into_linear(),
33//! { radius },
34//! translate = [x as f32 * 5., 10.],
35//! ),
36//! *Text(fill = Srgb::<f32>::from_format(named::BLACK).into_linear(), font_size = 10., text = "Hi !",),
37//! ])
38//! });
39//!
40//! let dessin = dessin!([
41//! { dessin }(scale = [2., 2.]),
42//! MyShape(say_this = "Hello world"),
43//! ]);
44//! }
45//! ```
46//!
47//! ## Components
48//!
49//! [Base components][`crate::shapes`] are defined in the `shapes` module.
50//!
51//! [Components using base ones][`crate::contrib`] are defined in the `contrib` module.
52//!
53//! In `dessin`, a component is just a struct/enum that can be converted to a [Shape][crate::shapes::Shape],
54//! and implements the [Default][`std::default::Default`] trait.
55//!
56//! This means that a component can be as simple as:
57//! ```
58//! use dessin::prelude::*;
59//!
60//! #[derive(Default)]
61//! struct MyComponent {}
62//!
63//! impl From<MyComponent> for Shape {
64//! fn from(my_component: MyComponent) -> Shape {
65//! dessin!(
66//! // Implementation...
67//! )
68//! }
69//! }
70//! ```
71//!
72//! Since the [dessin!][`dessin_macros::dessin`] macro is only syntactic sugar for creating a [Shape][crate::shapes::Shape],
73//! all parameters are simply rust function with the following signature: `fn (&mut self, argument_value: ArgumentType) {...}`.
74//!
75//! It can be tedious to create these function for all parameters, so the derive macro [Shape][`dessin_macro::shape`]
76//! is here to do exactly that.
77//!
78//! So
79//! ```
80//! # use nalgebra::Transform2;
81//! # use dessin::prelude::*;
82//! # pub trait ShapeOp {}
83//! # #[derive(Clone)]
84//! #[derive(Default, Shape)]
85//! struct MyComponent {
86//! // This auto implements ShapeOp for MyComponent using `my_local_transform` as the storage.
87//! #[local_transform]
88//! my_local_transform: Transform2<f32>,
89//!
90//! // Generate a function for u32
91//! value: u32,
92//!
93//! // Does not generate a function for this field
94//! #[shape(skip)]
95//! skip_value: u32,
96//!
97//! // Generates a function for Into<u32>
98//! #[shape(into)]
99//! into_value: u32,
100//!
101//! // Generates a function that does not take any arguments, but set `my_bool` to true
102//! #[shape(bool)]
103//! my_bool: bool,
104//! }
105//!
106//! # impl From<MyComponent> for Shape {
107//! # fn from(_: MyComponent) -> Shape { unimplemented!() }
108//! # }
109//! ```
110//!
111//! becomes
112//! ```
113//! # use nalgebra::Transform2;
114//! # use dessin::prelude::*;
115//! # pub trait ShapeOp {}
116//!
117//! #[derive(Default)]
118//! struct MyComponent {
119//! my_local_transform: Transform2<f32>,
120//! value: u32,
121//! skip_value: u32,
122//! into_value: u32,
123//! my_bool: bool,
124//! }
125//!
126//! impl ShapeOp for MyComponent { /* skip impl */ }
127//!
128//! impl MyComponent {
129//! pub fn value(&mut self, value: u32) -> &mut Self {
130//! # self
131//! /* skip impl */
132//! }
133//! pub fn into_value<T: Into<u32>>(&mut self, value: T) -> &mut Self {
134//! # self
135//! /* skip impl */
136//! }
137//! pub fn my_bool(&mut self) -> &mut Self {
138//! # self
139//! /* skip impl */
140//! }
141//! }
142//!
143//! # impl From<MyComponent> for Shape {
144//! # fn from(_: MyComponent) -> Shape { unimplemented!() }
145//! # }
146//! ```
147//! To be precise, all functions generated by this derive macro, return `&mut Self` to chain function together.
148//! Generated functions have the same name as their corresponding field.
149//! This derive macro also generate corresponding `with_xxxx`, taking `self` instead of `&mut self` and returning `Self`.
150//!
151//! One still does need to implement `From<MyComponent> for Shape { ... }` manually.
152//!
153//! ## Implement own export format.
154//! Documentation can be found in the [`export`] module.
155
156#![warn(missing_docs)]
157#![allow(clippy::tabs_in_doc_comments)]
158
159pub mod macros;
160
161// We need this in order for the proc_macro to work in this library.
162// See https://github.com/rust-lang/rust/issues/56409 for more details
163extern crate self as dessin;
164
165/// Shapes made of basic [shapes][crate::shapes::Shape]
166pub mod contrib;
167/// Declarations to create an export format.
168pub mod export;
169/// Building blocks of a dessin
170pub mod shapes;
171/// Styling of the building blocks
172pub mod style;
173
174pub use ::image;
175pub use ::nalgebra;
176pub use ::palette;
177
178/// Prelude module includes everyting you need to build a dessin.
179/// You can of courses cherry pick what you need by importing directly from other modules.
180pub mod prelude {
181 pub use crate::{contrib::*, shapes::*, style::*};
182 pub use ::dessin_macros::{dessin, Shape};
183}
184
185/// Everything related to fonts.
186pub mod font {
187 pub use crate::shapes::text::font::*;
188}
189
190#[cfg(test)]
191mod tests {
192 use crate::prelude::{polygons::Octogon, *};
193
194 #[test]
195 fn types_funkyness() {
196 dessin!(Padding<Shape>(shape = dessin!(Line() > ())) > *());
197 }
198
199 #[test]
200 fn erased_type() {
201 #[derive(Default)]
202 struct Component {}
203 impl From<Component> for Shape {
204 fn from(_: Component) -> Self {
205 dessin!()
206 }
207 }
208
209 dessin!(Component() > (translate = [1., 1.]));
210 }
211
212 #[test]
213 fn group_bounding_box() {
214 let group = dessin!([Octogon(), Circle(radius = 7.),]);
215 let bb = group.local_bounding_box();
216 assert_eq!(bb.width(), 14.);
217 assert_eq!(bb.height(), 14.);
218
219 let group = dessin!([Octogon(scale = [12., 12.]), Circle(radius = 7.)]);
220 let bb = group.local_bounding_box();
221 assert_eq!(bb.width(), 24.);
222 assert_eq!(bb.height(), 24.);
223
224 let group = dessin!([Octogon(scale = [15., 15.]), Circle(radius = 7.)]);
225 let bb = group.local_bounding_box();
226 assert_eq!(bb.width(), 30.);
227 assert_eq!(bb.height(), 30.);
228
229 let group = dessin!([Octogon(scale = [13., 13.]), Circle(radius = 7.)]);
230 let bb = group.local_bounding_box();
231 assert_eq!(bb.width(), 26.);
232 assert_eq!(bb.height(), 26.);
233 }
234}