Plottery is a generative art engine for pen-plotters written in Rust.
- plottery_lib (crates.io, docs.rs): Core library with shapes, geometry helpers, composition and other tools to generate art.
- plottery_editor (crates.io, releases): The macOS app to create, manage and run projects, preview artworks and control the plotter.
- plottery_cli (crates.io): Command line interface to create, run and render projects.
- plottery_project (crates.io, docs.rs): Project handling and the
Paramsderive macros. - plottery_fonts (crates.io, docs.rs): 65 bundled single-line fonts for plotters (Hershey, EMS, Cutlings, Relief, ...) with their licences. Data only, usable without Plottery.
- plottery_server: The controller of my DIY pen-plotter. It serves an HTTP interface for plot tasks and drives the motors.
On macOS, this installs the newest editor and, if cargo is installed, the matching plottery CLI:
curl -fsSL https://raw.githubusercontent.com/TobiasGrothmann/plottery/main/scripts/install_macos.sh | bashCLI only: cargo install plottery_cli.
The editor lets you manage, preview and run Plottery projects.
A project is a regular cargo package that depends on Plottery. Create one with the editor or the CLI:
plottery new /path/to/folder awesome_projectIts src/generate.rs turns a Params struct into a Layer:
use plottery_lib::*;
use plottery_project::*;
#[derive(PlotteryParams)]
pub struct Params {
pub paper: Paper,
#[value(12)]
#[range(1, 50)]
pub circles: i32,
}
pub fn generate(params: Params) -> Layer {
let frame = params.paper.frame();
let step = frame.inner_rect().width() * 0.5 / params.circles as f32;
let circles = (1..=params.circles).map(|i| Circle::new(frame.center(), step * i as f32));
let mut l = Layer::new();
l.push(frame.outer_rect());
l.push_many(circles);
l.with_name("root").optimize_recursive()
}Run it in the editor, with plottery run, or with cargo run svg to open the result as .svg.
- spiral: a path collected from polar points, masked to the margin, in named and colored sublayers. Its center is set by clicking the drawing, its direction by an enum.
- scribbles: squares that dissolve into scribbles, with a seed, a noise param, an easing curve and random ranges.
- pebbles: closed Hobby splines through random points, with rings inside.
- poster: a title fitted to the page width and a wrapped paragraph, fonts and text set in the editor.
Every example is a complete project that builds against this repository: in a clone, open its folder in the editor or run plottery run examples/spiral.
Every field of Params shows up in the editor. #[value(..)] sets the default, #[range(min, max)] shows a slider, #[section("..")] groups fields and /// comments become tooltips. All attributes and supported types are listed in the project README, and the examples use many of them.
Text is drawn with single-line fonts, so every glyph is a set of pen strokes. The bundled fonts are embedded in the library and load with no setup:
let font = Font::bundled(BundledFont::HersheyScript);
let text = Text::new("Hello plotter", &font)
.size(1.5) // em height in cm
.in_rect(frame.inner_rect()) // wraps words at the rect's width
.align(TextAlign::Center)
.vertical_align(VerticalAlign::Center)
.to_layer(SampleSettings::default())
.expect("text");
l.push_layer(text);Anchoring to a rect, wrapping, justify, fitting and rotation are shown in the text layout samples. Font::load reads SVG, TTF and OTF files, Font::load_single_line keeps the strokes of single-line TrueType fonts open, and Font::system("Helvetica") finds an installed font. The poster example picks its fonts and text in the editor. All 65 bundled fonts are shown in the fonts README.
Building from source, tests and releasing are described in DEVELOPMENT.md.
GPL-3.0. The bundled fonts have their own licences, see plottery_fonts.



