penplot

An experimental and highly opinionated development environment for generative and pen plotter art.

Some features:

Zero configuration: just run a command and start writing <canvas> renderings

Fast live-reload on file save

Hotkey for high-quality PNG output

Hotkey for SVG rendering

A builtin library of utilities for random numbers, geometry tools, SVG exporting, and other functions

Easy integration with Inkscape and AxiDraw v3

Plots can be rendered in Node.js instead of the browser, sometimes useful for huge print sizes where the browser may have more strict memory limits

Quick Start

You can install this with npm.

npm install penplot -g

Here is a simple command you can use to quick-start a new plot:

penplot src/index.js --write --open

This will write a new src/index.js file and open localhost:9966. Now start editing your index.js file to see the LiveReload in action.

While in your browser session, you can hit Cmd/Ctrl + P to export the SVG to a file in your Downloads folder, or Cmd/Ctrl + S to save a PNG file.

The SVG should be formatted to fit a Letter size paper with a pen plotter like AxiDraw V3.

Penplot Modules

The penplot tool is both a development environment and kitchen sink of utility functions. It tries to make some aspects easier for you, like sizing and printing to SVG or PNG.

The --write flag generates a simple plot that looks like this:

// Some handy functions & constants

import{PaperSize,Orientation}from'penplot';

import{polylinesToSVG}from'penplot/util/svg';

import{clipPolylinesToBox}from'penplot/util/geom';

// Export the paper layout & dimensions for penplot to set up

exportconstorientation=Orientation.LANDSCAPE;

exportconstdimensions=PaperSize.LETTER;

// The plot functiond defines how the artwork will look

exportdefaultfunctioncreatePlot(context,dimensions){

const[width,height]= dimensions;

let lines =[];

// Add [ x, y ] points to the array of lines

// e.g. [ [ 5, 2 ], [ 2, 3 ] ] is one line

... algorithmic code ...

// Clip all the lines to a 1.5 cm margin for our pen plotter

constmargin=1.5;

constbox=[ margin, margin, width - margin, height - margin ];

lines =clipPolylinesToBox(lines, box);

return{

draw,

print,

background:'white'// used when exporting the canvas to PNG

};

functiondraw(){

lines.forEach(points=>{

context.beginPath();

points.forEach(p=>context.lineTo(p[0], p[1]));

context.stroke();

});

}

functionprint(){

returnpolylinesToSVG(lines,{

dimensions

});

}

}

All units here are in centimeters (including width and height), which makes it easy to reason about things like line thickness and distances.

Using an array of line primitives, you can build up complex prints that can be easily exported to SVG or PNG. However, this means everything will be built from line segments; e.g. circles are generated with cos() and sin().

Node.js

You can also use this as a tool for developing algorithmic/generative art. For example, you can develop the artwork in a browser for LiveReload and fast iterations, and when you want to print it you can set the dimensions and output size like so:

// desired orientation

exportconstorientation=Orientation.PORTRAIT;

// desired dimensions in CM (used for aspect ratio)

exportconstdimensions=PaperSize.LETTER;

// your artwork

exportdefaultfunctioncreatePlot(context,dimensions){

// your artwork...

return{

outputSize:'300 dpi'

}

}

Then, use the --node flag to run the plot with node-canvas, assuming none of your code is browser-specific.

penplot my-plot.js --node --stdout > render.png

The outputSize option can be any of the following:

a string with DPI resolution like '300dpi' or '72 DPI'

a single number to use as the pixel width; in this case the height is computed automatically based on the dimensions aspect

an array of [ width, height ], where either (or both) can be specified as pixel sizes. If you specify 'auto', -1 or null as a dimension, it will be computed automatically based on the aspect ratio