12 unstable releases (3 breaking)

0.4.0 Jan 21, 2024
0.4.0-rc.0 Jan 20, 2024
0.3.0 Dec 28, 2023
0.2.0 Oct 22, 2023
0.1.0-alpha.2 Sep 30, 2023

#165 in Visualization

Download history 1/week @ 2024-01-14 1/week @ 2024-01-21 15/week @ 2024-02-25 8/week @ 2024-03-10 53/week @ 2024-03-31

53 downloads per month

MIT license

6MB
6.5K SLoC

whiskers: interactive sketches for plotter generative art

github Latest version Documentation GitHub

👉 Try the live demo!

whiskers is a Processing-like interactive sketch environment and API built over vsvg and vsvg-viewer. It's similar to vsketch, but faster, web-ready, and with much stronger foundations.

image

Installation

Simply add whiskers as a dependency to your project:

cargo add whiskers

Usage

Here is the code for a basic sketch:

use whiskers::prelude::*;

#[sketch_app]  // <- this is the magic to make the sketch interactive
struct HelloWorldSketch {
    width: f64,   // <- interactive UI is automagically built for these fields
    height: f64,
}

impl Default for HelloWorldSketch {
    fn default() -> Self {
        Self {
            width: 400.0,
            height: 300.0,
        }
    }
}

impl App for HelloWorldSketch {
    fn update(&mut self, sketch: &mut Sketch, _ctx: &mut Context) -> anyhow::Result<()> {
        sketch.color(Color::DARK_RED).stroke_width(3.0);

        // the `Sketch` API is a delight to work with
        sketch
            .translate(sketch.width() / 2.0, sketch.height() / 2.0)
            .rect(0., 0., self.width, self.height);

        Ok(())
    }
}

fn main() -> Result {
    HelloWorldSketch::runner()
        .with_page_size_options(PageSize::A5H)
        .run()
}

This is the result:

image

whiskers is part of the vsvg project.

Features

  • Interactive UI automatically built based on the sketch struct's fields.
  • Sketch parameter UI highly customisable using #[param(...)] attributes (see e.g. asteroid example).
  • Sketch parameter UI easily extendable for custom data types (see e.g. custom_ui example).
  • Page size management UI.
  • Export to SVG.
  • Support for curves (including quadratic Béziers, cubic Bézier, Catmull-Rom splines—circles, ellipses and arcs are supported but internally converted to cubic Bézier).
  • Time parameter management UI (for animated sketches).
  • Random Number Generator UI with seed control (see e.g. asteroid example).
  • Integrated profiler (based on puffin).
  • Grid helper for grid-based sketches.
  • HexGrid helper for hexagonal-grid-based sketches
  • Configuration handling (save/restore config, etc.).
  • Compiled sketches are also a flexible CLI utility with the capability to batch generate sketch outputs with parameter ranges.
  • Export to other format through templating (HPGL, g-code, etc. — for now, please use vpype).
  • ... (please complete this list)

Isn't that vsketch?

Compared to vsketch, whiskers offers the following advantages:

  • It's in Rust, so it's faaast! 🚀
  • Sketches can be compiled to WebAssembly and published on the Web (try it here).
  • It's built on a stack (mainly egui and wgpu) that's much faster and easier to work with.

On the other hand:

  • It's in Rust, which has a steeper learning curve than Python.
  • Since sketches are compiled, the edit code/execute cycle is heavier than with vsketch.

I have the vague dream to slap a Python front-end on top of whiskers to build vsketch 2.0, but that's a lot of work and other things have a higher priority, so no promises on this front.

Running examples

To run the example, use the following command (in this case to run crates/whiskers/examples/asteroid.rs):

cargo run --release --package whiskers --example asteroid

Non-interactive use

The whiskers::Sketch object can be used stand-alone, without the interactive sketch runner. This is useful if you want to use the drawing capabilities in your own tools.

For example, I use whiskers::Sketch to build the generative asteroids in my Rusteroïds toy game.

Here is how the code could look:

use whiskers::prelude::*;

fn main() -> Result {
    Sketch::with_page_size_options(PageSize::A5)
        .scale(Units::Cm)
        .translate(7.0, 6.0)
        .circle(0.0, 0.0, 2.5)
        .translate(1.0, 4.0)
        .rotate_deg(45.0)
        .rect(0., 0., 4.0, 1.0)
        .save("output.svg")?;

    Ok(())
}

If the viewer feature of *whiskers is enabled (which it is by default), the sketch can be displayed using the basic viewer using sketch.show().

Dependencies

~23–69MB
~1M SLoC