vpype

Note: a proper documentation is under construction.
vpype aims to be the one-stop-shop, Swiss Army knife1 for producing plotter-ready vector graphics. Here are, for illustration, a few examples of what it can do:
- Load an SVG file, scale it to a specific size, and export it centered on an A4-sized, ready-to-plot SVG file.
$ vpype read input.svg scale --to 10cm 10cm write --page-format a4 --center output.svg - Visualize the path structure of large SVG files, showing whether lines are properly joined or
not thanks to a colorful display.
$ vpype read input.svg show --colorful - Optimize paths to reduce plotting time (merge connected lines and sort them to minimize pen-up distance):
$ vpype read input.svg linemerge --tolerance 0.1mm linesort write output.svg - Load several SVG files and save them as a single, multi-layer SVG file (e.g. for multicolored drawings).
$ vpype read -l 1 input1.svg read -l 2 input2.svg write output.svg
- Create arbitrarily-sized, grid-like designs like this page's top banner.
$ vpype begin grid -o 1cm 1cm 10 13 script alien_letter.py scaleto 0.5cm 0.5cm end show
- Export to HPGL for vintage plotters.
$ vpype read input.svg write --device hp7475a --page-format a4 --landscape --center output.hpgl
At its core, vpype allows the user to build pipelines of commands, each of which receives a collection of vector graphics (basically, lines), modifies them and/or produce new ones, and passes them to the next command. vpype's simple CLI user interface makes it a breeze to create these pipelines, which can be expanded thanks to a plug-in architecture.
Let's take a closer look at an example:
$ vpype random --count 100 --area 10cm 10cm rotate 45 write --page-format a4 --center output.svgThis pipelines uses 3 commands (random, rotate and write) to generate 100 random lines in a 10x10cm square,
rotate them by 45 degrees, and save them in the middle of an A4-sized SVG file. This is how the output would look in
InkScape:
Because vpype focuses only on vector graphics for use as input for pen plotters, its data model is very simple and only includes paths (without formatting options like line color, width, etc.), filled shapes, bitmaps, etc. This is the core of what makes vpype both simple and powerful at what it does.
This project is young and being actively developed. Your feedback is important!
vpype is written in Python and relies on Click, Shapely, rtree, svgwrite, svgpathtools, matplotlib, and NumPy. Additionally, there are many other projects which have helped get vpype where it is today.
1Although not in the military the author is indeed Swiss :)
Getting Started
Installation
See installation instructions.
Running example scripts
A few examples of the scripting functionality are available in the examples folder, in the form of bash scripts. The script used to create
the top banner is included:
$ cd examples
$ ./alien.shDocumentation
The CLI user interface documentation is available in the shell. Use vpype --help to see a list of
all available commands, including installed plugins:
$ vpype --help
Usage: vpype [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
Options:
-v, --verbose
-I, --include PATH Load commands from a command file.
--help Show this message and exit.
Commands:
[...]
Input:
read Extract geometries from a SVG file.
script Call an external python script to generate geometries.
Transforms:
rotate Rotate the geometries.
scale Scale the geometries.
skew Skew the geometries.
translate Translate the geometries.
[...]
Use vpype COMMAND --help for information on a specific command, for example:
$ vpype scale --help
Usage: vpype scale [OPTIONS] SCALE...
Scale the geometries by a factor.
The origin used is the bounding box center, unless the `--origin` option
is used.
By default, act on all layers. If one or more layer IDs are provided with
the `--layer` option, only these layers will be affected. In this case,
the bounding box is that of the listed layers.
Example:
Double the size of the geometries in layer 1, using (0, 0) as origin:
vpype [...] scale -l 1 -o 0 0 2 2 [...]
Options:
-l, --layer LAYERS Target layer(s).
-o, --origin LENGTH... Use a specific origin.
--help Show this message and exit.
Generator-filter-sink theory
vpype commands can be broadly categorized in one of three groups, based on what they do:
- Generators create new geometries (e.g. read data in from an SVG file, generate random lines, etc.).
- Filters alter the existing geometries (e.g. translate, rotate, etc.).
- Sinks use the existing geometries to display or export without modifying them.
Typically in this data theory, generators will be at the beginning of pipelines, filters in the middle, and sinks at the end. vpype does not strictly enforce, nor internally require this theory, but it helps the user to plan pipelines. A command could both modify existing geometries and add new ones, and sinks don't actually "eat" geometries as the name may imply. Geometries are instead passed on, which enables multiple sinks to be chained (e.g. first display the result, then save it to SVG).
consider the following example:
group: [generator]->[ filter ]->[ sink ]->[ filter ]->[ sink ]->[ generate AND filter ]->[ sink ]
functions: [read svg] ->[scale 2x]->[display]->[rotate 45˚]->[write to svg]->[replicate geometry, rotating randomly]->[display]
Here is a non-exhaustive list of important commands:
read: import geometries from a SVG fileline,rect,arc,circle: create the corresponding primitivesscript: execute a Python script to generate geometries (see External scripts)translate,rotate,scale,skew: basic transformation commands which do exactly what you think they docrop: crop the geometries, removing everything outside of a rectangular arealinemerge: merge lines whose endings overlap or are very closelinesort: sort lines to minimize the total distance between the end of a path to the start of the next onemultipass: prepare two-pass (or more) files for when a single stroke isn't sufficient for a good renderframe: add a simple frame around the geometrieslmove,lcopy,ldelete: layer manipulation commandsshow: display the geometries in amatplotlibwindowwrite: save the geometries as an SVG or HPGL file
Data model and units
Being designed for plotter data, vpype only understands lines, specifically straight lines. This makes vpype a
very poor general purpose vector graphics tool, but hopefully a good one when dealing with graphics files intended for pen
plotters. When loading geometries from an existing SVG file (using the read command), curved paths such as Bezier
curves or ellipses are converted into multiple, typically small, straight segments. The curve quantization interval is 1mm by default,
but can be changed with the --quantization option of the read command.
Internally, vpype uses the CSS pixel as its default unit, which is defined as 1/96th of an inch. This also happens to be the default
unit used by the SVG format. Most commands understand other standard units though, including in, cm, mm, pt and
pc. Thus, these two commands will generate the same output (100 random lines in a 1x1in square in the middle of an A4 page):
$ vpype random --count 100 --area 96 96 write --page-format a4 --center output.svg
$ vpype random --count 100 --area 1in 1in write --page-format a4 --center output.svgLayers
vpype supports multiple layers and can produce multi-layer SVGs, which can be useful for multicolored drawings.
Most commands have a -l, --layer option which affects how layers are created and/or modified.
Layers are always referred to by a non-zero, positive integer (which aligns nicely with how official
AxiDraw tools deal with layers).
Generators such as line, script, etc. which create new geometries use --layer to specify which layer receives
these new geometries. By default, the last target layer is used:
$ vpype line --layer 3 0 0 1cm 1cm circle 0.5cm 0.5cm 0.5cm show
Here, both the line and the circle will be in layer 3. If no generator specifies a target layer, then layer 1 is used by default.
The read command honors the input SVG file's layer structure and will create vpype layers for each layer in the SVG file (i.e. for each top-level SVG group, see the CLI help
for further details). Alternatively, read can be run in single-layer mode by adding the --single-layer argument. In this case, all geometries are
loaded in one layer, regardless of the SVG file's structure.
Filters such as translate, rotate, crop, linemerge, etc. which modify existing geometries use --layer to
control if one, several or all layers will be affected:
$ vpype [...] rotate --layer 1 [...]
$ vpype [...] rotate --layer 1,2,4 [...]
$ vpype [...] rotate --layer all [...]
All these commands do exactly what you think they should do. If the --layer option is omitted, then all is assumed.
Note that if you provide a list of layers, they must be comma separated and without any whitespace, as the list must be
a single CLI argument.
Some commands do not have a --layer option, but do understand the layer data. For example, show will display each layer
in a different color by default. Last but not least, write will generate multi-layer SVG files which will work as expected
in InkScape without further modification.
Finally, layers' content can be moved or copied to other layers with the lmove and lcopy commands, and
deleted with the ldelete command. See these commands' help for details.
External scripts
The script command is a very useful generator that relies on an external Python script to produce geometries. Its
use is demonstrated by the alien.sh and alien2.sh examples. A path to a Python file must be passed as argument.
The file must implement a generate() function which returns a Shapely MultiLineString object. Creating this is very easy,
and is explained in the Shapely documentation.
script is distinct from plugins in the ways they interface with vpype data pipelines. Plugins have more direct access, but are expected to
function in the generator-filter-sink theory. This means they must be more well-behaved with how they treat incoming and outgoing data,
but are able to more easily access geometry from other generators without needing intermediary sinks (e.g. SVG file writes).
scripts on the other hand, can be a lot looser with their geometry handling, and have the ability to incorporate other, less related features,
because they are more free-form scripts.
Blocks
Blocks refer to a prepared portion of pipeline marked by the special commands begin and end.
The command immediately following begin is called a block processor and defines (directly or implicitly) how many times this portion of
the pipeline will be used. For example, the grid block processor repeatedly executes the block and arranges the
resulting geometries on a regular NxM grid. This is how the top banner was generated:
vpype begin \
grid --offset 1.5cm 1.5cm 13 20 \
script alien_letter.py \
scale --to 0.8cm 0.8cm \
end \
write --page-format a3 --center alien.svgThe pipeline above mainly consists of a block with the grid block processor. On each intersection of a 13 by 20 grid, with
a spacing of 1.5cm in both directions, it executes the script alien_letter.py to generate
some geometries, which are then scaled to a 0.8x0.8cm size. After the block, we write the result to an SVG file.
Notice how, with added newlines and proper indenting, the sequence of commands emerges as a kind of pseudo-language. You guessed it, blocks can be nested to achieve more complex compositions. Here is an example:
vpype begin \
grid --offset 8cm 8cm 2 3 \
begin \
grid --offset 2cm 2cm 3 3 \
random --count 20 --area 1cm 1cm \
frame \
end \
frame --offset 0.3cm \
end \
showThis pipeline should display the following:
Command file
As pipelines become more complex, the number of command-line arguments needed can become too large to type into the shell easily. To address
this, vpype supports the inclusion of command files in the pipeline. A command file for vpype is a text file whose content is
interpreted as if it were a series of command-line arguments.
The previous nested grid example can be converted to a command file like so:
# this is an example command file
begin
grid --offset 8cm 8cm 2 3
begin
grid --offset 2cm 2cm 3 3
random --count 20 --area 1cm 1cm
frame
end
frame --offset 0.3cm
end
showThe command file can then be loaded as an argument using the -I or --include option:
$ vpype -I command_file.vpyNewlines and indentation are ignored and only used for human readability. Comments can be written in Python style, i.e. #like this comment
Command files can be mixed with regular arguments too:
$ vpype -I generate_lines.vpy write -p a4 -c output.svg
Finally, command files can also include other command files:
# Example command file
begin
grid --offset 1cm 1cm 2 2
--include sub_command.vpy
end
show
Plug-ins
vpype supports plug-ins to extend its capabilities. Here is a (non-exhaustive) list of plug-ins.
vpype-text: generate plottable text with Hershey fonts (based on axi)
vpype-pixelart: easy pixel art plotting
(original art by Reddit user u/_NoMansDream)
hatched: convert images to hatched patterns
Creating custom plug-ins is very easy. It's a great way to implement your next plotter art project as you directly benefit from all of vpype's features (exporting to SVG, line order optimization, etc.). Check the plug-in documentation for more information on how to develop your own plug-in.
Contributing
This project is at an early (but fully functional) stage and welcomes all types of contributions. The most important way to contribute is by filing Issues describing bugs you are experiencing or features you would like to see added. Understanding your use-case and workflow is key for vpype to evolve in the right direction.
Code contributions are, of course, very welcome. Feel free to also open pull
requests to contribute actual code. Note that this project uses
black for code formatting to create a uniform style.
Development environment
Refer to the documentation.
License
This project is licensed under the MIT License - see the LICENSE file for details.






