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}