dessin 0.8.23

Build complex drawing for PDF, SVG, Images or Dioxus
Documentation
//! **dessin** is library aimed at building complex drawings, combine them, move them and export them as PDF or SVG.
//!
//! Details about the [macro][`crate::macros`].
//!
//! ## Example
//!
//! ```
//! use dessin::prelude::*;
//! use palette::{named, Srgb};
//!
//! #[derive(Default, Shape)]
//! struct MyShape {
//!   text: String,
//! }
//! impl MyShape {
//!   fn say_this(&mut self, what: &str) {
//!     self.text = format!("{} And check this out: `{what}`", self.text);
//!   }
//! }
//! impl From<MyShape> for Shape {
//!   fn from(MyShape { text }: MyShape) -> Self {
//!     dessin!(*Text(fill = Srgb::<f32>::from_format(named::RED).into_linear(), { text })).into()
//!   }
//! }
//!
//! fn main() {
//!   let dessin = dessin!(for x in 0..10 {
//!   let radius = x as f32 * 10.;
//!
//!   dessin!([
//!    *Circle(
//!      fill = Srgb::<f32>::from_format(named::RED).into_linear(),
//!      { radius },
//!      translate = [x as f32 * 5., 10.],
//!    ),
//!    *Text(fill = Srgb::<f32>::from_format(named::BLACK).into_linear(), font_size = 10., text = "Hi !",),
//!  ])
//!   });
//!
//!   let dessin = dessin!([
//!  { dessin }(scale = [2., 2.]),
//!  MyShape(say_this = "Hello world"),
//!   ]);
//! }
//! ```
//!
//! ## Components
//!
//! [Base components][`crate::shapes`] are defined in the `shapes` module.
//!
//! [Components using base ones][`crate::contrib`] are defined in the `contrib` module.
//!
//! In `dessin`, a component is just a struct/enum that can be converted to a [Shape][crate::shapes::Shape],
//! and implements the [Default][`std::default::Default`] trait.
//!
//! This means that a component can be as simple as:
//! ```
//! use dessin::prelude::*;
//!
//! #[derive(Default)]
//! struct MyComponent {}
//!
//! impl From<MyComponent> for Shape {
//!   fn from(my_component: MyComponent) -> Shape {
//!     dessin!(
//!       // Implementation...
//!     )
//!   }
//! }
//! ```
//!
//! Since the [dessin!][`dessin_macros::dessin`] macro is only syntactic sugar for creating a [Shape][crate::shapes::Shape],
//! all parameters are simply rust function with the following signature: `fn (&mut self, argument_value: ArgumentType) {...}`.
//!
//! It can be tedious to create these function for all parameters, so the derive macro [Shape][`dessin_macro::shape`]
//! is here to do exactly that.
//!
//! So
//! ```
//! # use nalgebra::Transform2;
//! # use dessin::prelude::*;
//! # pub trait ShapeOp {}
//! # #[derive(Clone)]
//! #[derive(Default, Shape)]
//! struct MyComponent {
//!   // This auto implements ShapeOp for MyComponent using `my_local_transform` as the storage.
//!   #[local_transform]
//!   my_local_transform: Transform2<f32>,
//!
//!   // Generate a function for u32
//!   value: u32,
//!
//!   // Does not generate a function for this field
//!   #[shape(skip)]
//!   skip_value: u32,
//!
//!   // Generates a function for Into<u32>
//!   #[shape(into)]
//!   into_value: u32,
//!
//!   // Generates a function that does not take any arguments, but set `my_bool` to true
//!   #[shape(bool)]
//!   my_bool: bool,
//! }
//!
//! # impl From<MyComponent> for Shape {
//! #   fn from(_: MyComponent) -> Shape { unimplemented!() }
//! # }
//! ```
//!
//! becomes
//! ```
//! # use nalgebra::Transform2;
//! # use dessin::prelude::*;
//! # pub trait ShapeOp {}
//!
//! #[derive(Default)]
//! struct MyComponent {
//!   my_local_transform: Transform2<f32>,
//!   value: u32,
//!   skip_value: u32,
//!   into_value: u32,
//!   my_bool: bool,
//! }
//!
//! impl ShapeOp for MyComponent { /* skip impl */ }
//!
//! impl MyComponent {
//!   pub fn value(&mut self, value: u32) -> &mut Self {
//! #   self
//!     /* skip impl */
//!  }
//!   pub fn into_value<T: Into<u32>>(&mut self, value: T) -> &mut Self {
//! #   self
//!    /* skip impl */
//!  }
//!   pub fn my_bool(&mut self) -> &mut Self {
//! #   self
//!     /* skip impl */
//!  }
//! }
//!
//! # impl From<MyComponent> for Shape {
//! #   fn from(_: MyComponent) -> Shape { unimplemented!() }
//! # }
//! ```
//! To be precise, all functions generated by this derive macro, return `&mut Self` to chain function together.
//! Generated functions have the same name as their corresponding field.
//! This derive macro also generate corresponding `with_xxxx`, taking `self` instead of `&mut self` and returning `Self`.
//!
//! One still does need to implement `From<MyComponent> for Shape { ... }` manually.
//!
//! ## Implement own export format.
//! Documentation can be found in the [`export`] module.

#![warn(missing_docs)]
#![allow(clippy::tabs_in_doc_comments)]

pub mod macros;

// We need this in order for the proc_macro to work in this library.
// See https://github.com/rust-lang/rust/issues/56409 for more details
extern crate self as dessin;

/// Shapes made of basic [shapes][crate::shapes::Shape]
pub mod contrib;
/// Declarations to create an export format.
pub mod export;
/// Building blocks of a dessin
pub mod shapes;
/// Styling of the building blocks
pub mod style;

pub use ::image;
pub use ::nalgebra;
pub use ::palette;

/// Prelude module includes everyting you need to build a dessin.
/// You can of courses cherry pick what you need by importing directly from other modules.
pub mod prelude {
	pub use crate::{contrib::*, shapes::*, style::*};
	pub use ::dessin_macros::{dessin, Shape};
}

/// Everything related to fonts.
pub mod font {
	pub use crate::shapes::text::font::*;
}

#[cfg(test)]
mod tests {
	use crate::prelude::{polygons::Octogon, *};

	#[test]
	fn types_funkyness() {
		dessin!(Padding<Shape>(shape = dessin!(Line() > ())) > *());
	}

	#[test]
	fn erased_type() {
		#[derive(Default)]
		struct Component {}
		impl From<Component> for Shape {
			fn from(_: Component) -> Self {
				dessin!()
			}
		}

		dessin!(Component() > (translate = [1., 1.]));
	}

	#[test]
	fn group_bounding_box() {
		let group = dessin!([Octogon(), Circle(radius = 7.),]);
		let bb = group.local_bounding_box();
		assert_eq!(bb.width(), 14.);
		assert_eq!(bb.height(), 14.);

		let group = dessin!([Octogon(scale = [12., 12.]), Circle(radius = 7.)]);
		let bb = group.local_bounding_box();
		assert_eq!(bb.width(), 24.);
		assert_eq!(bb.height(), 24.);

		let group = dessin!([Octogon(scale = [15., 15.]), Circle(radius = 7.)]);
		let bb = group.local_bounding_box();
		assert_eq!(bb.width(), 30.);
		assert_eq!(bb.height(), 30.);

		let group = dessin!([Octogon(scale = [13., 13.]), Circle(radius = 7.)]);
		let bb = group.local_bounding_box();
		assert_eq!(bb.width(), 26.);
		assert_eq!(bb.height(), 26.);
	}
}