Cookbook¶
SVG reading and writing recipes¶
Wrapping a vpype pipeline in a shell script¶
Optimizing a SVG file is quite possibly the most common use of vpype. It usually is done with a pipeline similar to this example:
$ vpype read my_file.svg linemerge linesort reloop linesimplify write my_file_optimized.svg
In particular, it is rather common to name the output file after the input file, maybe with a _processed
suffix.
Such a vpype invocation can easily be packaged in a shell script using some simple path expression. Here is the content of such a shell script:
#!/bin/sh
vpype read "$1" linemerge linesort reloop linesimplify \
write "%prop.vp_source.with_stem(prop.vp_source.stem + '_processed')%"
(Shell scripts are typically named with a .sh
extension and should be marked as “executable” to be used. This can be done with the chmod +x my_script.sh
command.)
The script might be used as follows:
$ ./my_script.sh /path/to/my_file.svg
The argument passed to the script is forwarded to vpype through the use of $1
. Then, the output path provided to the write command corresponds to the input path, with a _processed
suffix added (e.g. /path/to/my_file_processed.svg
in this case). This is achieved by the prop.vp_source.with_stem(prop.vp_source.stem + '_processed')
expression.
Preserve color (or other attributes) when reading SVG¶
By default, the read command sorts geometries into layers based on the input SVG’s top-level groups, akin to Inkscape’s layers. Stroke color is preserved only if it is identical for every geometries within a layer.
When preserving the color is desirable, the read command can sort geometries by colors instead of by top-level groups. This is achieved by using the --attr
option:
$ vpype read --attr stroke input.svg [...]
Here, we tell the read command to sort geometry by stroke
, which is the SVG attribute that defines the color of an element. As a result, a layer will be created for each different color encountered in the input SVG file.
The same applies for any SVG attributes, even those not explicitly supported by vpype. For example, --attr stroke-width
will sort layers by stroke width and --attr stroke-dasharray
by type of stroke dash pattern.
Multiple attributes can even be provided:
$ vpype read --attr stroke --attr stroke-width input.svg [...]
In this case, a layer will be created for each unique combination of color and stroke width.
Merge multiple SVGs into a multilayer file¶
This command will read two SVG files onto two different layers, then write them into a single SVG file:
$ vpype \
forfile "*.svg" \
read --layer %_i+1% %_path% \
end \
write output.svg
Load multiple files, merging their layers by name¶
Let us consider a collection of SVG files, each with one or more named layer(s). It could be for example a collection of CMYK SVGs, some of which with all four layers, but other with a sub-set of the layers (say, only “yellow” and “black”). This recipe shows how to load these files, making sure identically-named layers are properly merged.
Here is the full pipeline:
$ vpype \
eval "names={};n=100" \
forfile "*.svg" \
read %_path% \
forlayer \
eval "%if _name not in names: names[_name] = n; n = n+1%" \
lmove %_lid% "%names[_name]%" \
end \
end \
write combined.svg
This pipeline makes use of two nested blocks and some clever expressions. Let us break down how it works.
The core idea is to build a dictionary names
which maps “destination” layer IDs to layer name. Destination layer IDs is where geometries will be merged, and we choose a starting layer ID value n
of 100 to avoid interfering with the input file layers. At the beginning, names
is an empty dictionary ({}
). Here is how it could look at the end of a typical run:
names = { 'cyan': 100, 'magenta': 101, 'yellow': 102, 'black': 103 }
The outer block, marked by the forfile "*.svg"
command, iterates over SVG files in the current directory. Each file is first read using read %_path%
. Then, we iterate over its layers using the forlayer
block processor.
This is where it becomes interesting. For each layer, we first test whether its name exists in the names
dictionary. If not, we create a new item in the dictionary with the layer name, and assign the value of n
. This is the layer ID at which identically-named layers must lend. Since the layer ID n
is now assigned, we increment its value for the next time an “unknown” layer name is encountered.
Now that we made sure we have a destination layer ID for the current layer’s name, we can move it using the lmove %_lid% "%names[_name]%"
command. Here, _lid
is the current layer ID as set by forlayer
and names[_name]
the destination layer.
This recipe can be further augmented to arrange each file on a grid. This is covered in the Laying out multiple SVGs on a grid recipe.
Saving each layer as a separate file¶
Some plotter workflows require a different file for each layer, as opposed to a single, multi-layer SVG file. For example, this is often the case for gcode input using the vpype-gcode plug-in.
This can be achieved using the forlayer command:
$ vpype read input.svg forlayer write "output_%_name or _lid%.svg" end
Here, we construct the output file name either based on the layer name if available (which forlayer stores in the _name
variable), or on the layer ID (_lid
) otherwise.
Make a previsualisation SVG¶
The SVG output of write can be used to previsualize and inspect a plot. By default, paths are colored by layer. It can be useful to color each path differently to inspect the result of linemerge:
$ vpype read input.svg linemerge write --color-mode path output.svg
Likewise, pen-up trajectories can be included in the SVG to inspect the result of linesort:
$ vpype read input.svg linesort write --pen-up output.svg
Note that write --pen-up
should only be used for previsualization purposes as the pen-up trajectories may end-up being plotted otherwise. The Axidraw software will ignore the layer in which the pen-up trajectories are written, so it is safe to keep them in this particular case.
Layout recipes¶
Basic layout examples¶
There are two ways to layout geometries on a page. The preferred way is to use commands such as layout, scale, scaleto, translate. In particular, layout handles most common cases by centering the geometries on page and optionally scaling them to fit specified margins. These commands act on the pipeline and their effect can be previewed using the show command. The following examples all use this approach.
Alternatively, the write commands offers option such as --page-size
and
--center
which can also be used to layout geometries. It must be understood that these
options only affect the output file and leave the pipeline untouched. Their effect cannot be previewed by the
show command, even if it is placed after the write command.
This command will read a SVG file, and then write it to a new SVG file sized to A4 in landscape orientation, with the design centred on the page:
$ vpype read input.svg layout --landscape a4 write output.svg
The layout command implicitly centers the geometries on the page. The pagesize command can be used to choose the page size without changing the geometries:
$ vpype read input.svg pagesize --landscape a4 write output.svg
This command will read a SVG file and lay it out to 3cm margin with a top vertical alignment (a generally pleasing arrangement for square designs on the portrait-oriented page), and then write it to a new SVG:
$ vpype read input.svg layout --fit-to-margins 3cm --valign top a4 write output.svg
This command will read a SVG file, scale it down to 80% of its original size, and then write it to a new A5-sized SVG, centred on the page:
$ vpype read input.svg scale 0.8 0.8 layout a5 write output.svg
This command will read a SVG file, scale it down to a 5x5cm square (using the scaleto command), and then write it to a new A5-sized SVG, centred on the page:
$ vpype read input.svg scaleto 5cm 5cm layout a5 write output.svg
This command will read a SVG file, crop it to a 10x10cm square positioned 57mm from the top and left corners of the design, and then write it to a new SVG whose page size will be identical to the input SVG:
$ vpype read input.svg crop 57mm 57mm 10cm 10cm write output.svg
This command will read a SVG file, add a single-line frame around the design, 5cm beyond its bounding box, and then write it to a new SVG:
$ vpype read input.svg frame --offset 5cm write output.svg
Cropping and framing geometries¶
The following pipeline can be used to crop geometries and frame them with a given margin:
$ vpype \
read input.svg \
eval "m=2*cm; w, h = prop.vp_page_size" \
crop %m% %m% "%w-2*m%" "%h-2*m%" \
rect %m% %m% "%w-2*m%" "%h-2*m%" \
write output.svg
Laying out multiple SVGs on a grid¶
The grid command can be used to layout multiple SVGs onto a regular grid. This recipe shows how.
The basic idea is covered by the following pipeline:
$ vpype \
eval "files=glob('*.svg')" \
eval "cols=3; rows=ceil(len(files)/cols)" \
grid -o 10cm 15cm "%cols%" "%rows%" \
read --no-fail "%files[_i] if _i < len(files) else ''%" \
layout -m 0.5cm 10x15cm \
end \
write combined.svg
Here are the key insights to understand how this pipeline works:
An expression with the
glob()
function (see Built-in symbols) is used to create a list of files to include on the grid.Another expression computes the number of rows needed to include all files, given a number of column (hard-coded to 3 in this case).
The grid command uses expressions again as argument to use the previously computed column and row count.
For the read command, multiple tricks are used. The variable
_i
is set by the grid command and corresponds to the cell counter. We use it to look up the file path to read from our file list. We must however handle the last row, which might be incomplete. This is done with a conditional expression (see Conditional expressions) which returns an empty path''
if the cell index is beyond the end of the file list. Normally, the read would fail when passed a non-existing path. This is avoided by using the--no-fail
option.Finally, the layout command fits the SVGs in the cell with a margin.
One limitation of the pipeline above is that it will merge layers by their ID, disregarding properties such as layer name or color. In some cases, this may be an issue. Depending on the nature of the input SVGs, this can be addressed by reading each file in a different layer, like in Merge multiple SVGs into a multilayer file. This can be done by simply adding the --layer %_i+1%
option to the read command.
When input SVGs have layer names, they can be used to merge similarly named layers together. This is done by the following pipeline:
$ vpype \
eval "files=glob('*.svg')" \
eval "cols=3; rows=ceil(len(files)/cols)" \
eval "names={};n=100" \
grid -o 10cm 15cm "%cols%" "%rows%" \
read --no-fail "%files[_i] if _i < len(files) else ''%" \
layout -m 0.5cm 10x15cm \
forlayer \
eval "%if _name not in names: names[_name] = n; n = n+1%" \
lmove %_lid% "%names[_name]%" \
end \
end \
write combined.svg
See Load multiple files, merging their layers by name for an explanation on how this works.
Given the number of parameters involved, it may be useful to make these pipelines interactive (see Create interactive scripts with input()). Using a command file is also a nice way to make easy to reuse. Here is an example of command file:
# Content of file grid.vpy
# Ask user for some information, using sensible defaults.
eval "files=glob(input('Files [*.svg]? ') or '*.svg')" # glob() creates a list of file based on a pattern
eval "cols=int(input('Number of columns [3]? ') or 3)"
eval "rows=ceil(len(files)/cols)" # the number of rows depends on the number of files
eval "col_width=convert_length(input('Column width [10cm]? ') or '10cm')" # convert_length() converts string like '3cm' to pixels
eval "row_height=convert_length(input('Row height [10cm]? ') or '10cm')"
eval "margin=convert_length(input('Margin [0.5cm]? ') or '0.5cm')"
eval "output_path=input('Output path [output.svg]? ') or 'output.svg'"
# Create a grid with provided parameters.
grid -o %col_width% %row_height% %cols% %rows%
# Read the `_i`-th file. The last row may be incomplete so we use an empty path and `--no-fail`.
read --no-fail "%files[_i] if _i < len(files) else ''%"
# Layout the file in the cell.
layout -m %margin% %col_width%x%row_height%
end
# wWrite the output file.
write "%output_path%"
It can be used as follows:
$ vpype -I grid.vpy
Files [*.svg]?
Number of columns [3]? 4
Column width [10cm]?
Row height [10cm]? 15cm
Margin [0.5cm]?
Output path [output.svg]?
The various parameters are queried and if nothing is provided as input, sensible defaults are used.
Processing recipes¶
Optimizing a SVG for plotting¶
This command will read a SVG file, merge any lines whose endings are less than 0.5mm from each other with linemerge, and then write a new SVG file:
$ vpype read input.svg linemerge --tolerance 0.5mm write output.svg
In some cases such as densely connected meshes (e.g. a grid where made of touching square paths), linemerge may not be able to fully optimize the plot by itself. Using splitall before linemerge breaks geometries into their constituent segments and enables linemerge to perform a more aggressive optimization, at the cost of an increased processing time:
$ vpype read input.svg splitall linemerge --tolerance 0.5mm write output.svg
This command will read a SVG file, simplify its geometry by reducing the number of segments in a line until they’re a maximum of 0.1mm from each other using linesimplify, and then write a new SVG file:
$ vpype read input.svg linesimplify --tolerance 0.1mm write output.svg
This command will read a SVG file, randomise the seam location for paths whose beginning and end points are a maximum of 0.03mm from each other with reloop, and then write a new SVG file:
$ vpype read input.svg reloop --tolerance 0.03mm write output.svg
This command will read a SVG file, extend each line with a mirrored copy of itself three times using multipass, and then write a new SVG file. This is useful for pens that need a few passes to get a good result:
$ vpype read input.svg multipass --count 3 write output.svg
This command will read a SVG file, use linesort to sort the lines to minimise pen-up travel distance, and then write a new SVG file:
$ vpype read input.svg linesort write output.svg
Filtering out small lines¶
In some cases (for example when using Blender’s freestyle renderer), SVG files can contain a lot of tiny lines which significantly increase the plotting time and may be detrimental to the final look. These small lines can easily be removed thanks to the filter command:
$ vpype read input.svg filter --min-length 0.5mm write output.svg
Splitting layers by drawing distance¶
Certain medium such as paintbrush or Posca pens require manual intervention after a certain drawing distance. One way to achieve this is to ensure that the cumulative distance of the lines within each layer remains below that drawing distance. This can be achieved using the splitdist command:
$ vpype read input.svg splitdist 50cm write output.svg
A finer-grained approach consists of splitting all lines into their constituent segments using the splitall command and subsequently using the linemerge to put everything back together:
$ vpype read input.svg splitall splitdist 50cm linemerge write output.svg
Inserting regular “dipping” patterns for plotting with paint¶
Plotting with paint is a tricky process where the brush must be regularly dipped in a paint bucket. This can be achieved by using the splitdist command:
$ vpype \
read input.svg \
forlayer \
lmove %_lid% 1 \
splitdist 1m \
forlayer \
lmove %_lid% "%_lid*2%" \
read -l "%_lid*2-1%" dip_%_name%.svg \
end \
lmove all %_lid% \
name -l %_lid% %_name% \
color -l %_lid% %_color% \
end \
write output.svg
For this to work, the layers in input.svg
must be named after their respective color and, for each such color, a file named dip_COLORNAME.svg
must exist. For example, if input.svg
has two layers named “red” and “blue”, then the dip_red.svg
and dip_blue.svg
files must exist.
The output file will have the same layers as the input file, but they will start with the corresponding dipping pattern, which will also be interspersed regularly based on the cumulative drawing distance provided to the splitdist command.
HPGL export recipes¶
Converting a SVG to HPGL¶
For vintage plotters, the write command is capable of generating HPGL code instead of SVG. HPGL output format
is automatically selected if the output path file extension is .hpgl
. Since HPGL coordinate systems vary widely from
plotter to plotter and even for different physical paper format, the plotter model must be provided
to the write command:
$ vpype read input.svg write --device hp7475a output.hpgl
The plotter paper size will be inferred from the current page size (as set by the input SVG or using either the pagesize or layout commands).
The plotter type/paper format combination must exist in the built-in or user-provided configuration file. See
Creating a custom configuration file for a HPGL plotter for information on how to create one. If a matching plotter paper size cannot be found,
an error will be generated. In this case, the paper size must manually specified with the --page-size
option:
$ vpype read input.svg write --device hp7475a --page-size a4 --landscape output.hpgl
Here the --landscape
is also used to indicate that landscape orientation is desired. As
for SVG output, the --center
is often use to center the geometries in the middle of the page.
It is typically useful to optimize the input SVG during the conversion. The following example is typical of real-world use:
$ vpype read input.svg linesimplify reloop linemerge linesort layout a4 write --device hp7475a output.hpgl
Defining a default HPGL plotter device¶
If you are using the same type of plotter regularly, it may be cumbersome to systematically add the --device
option to the write command. The default device can be set in a configuration file (see
Creating a custom configuration file) by adding the following section:
[command.write] default_hpgl_device = "hp7475a"
Creating a custom configuration file for a HPGL plotter¶
The configuration for a number of HPGL plotter is bundled with vpype (run vpype write --help
for a list). If your
plotter is not included, it is possible to define your own plotter configuration in a custom configuration file
(see Creating a custom configuration file).
The configuration file must first include a plotter section with the following format:
[device.my_plotter] name = "My Plotter" # human-readable name for the plotter plotter_unit_length = "0.02488mm" # numeric values in pixel or string with units pen_count = 6 # number of pen supported by the plotter info = "Plot configuration..." # (optional) human-readable information on how # the plotter must be configured for this # configuration to work as expected
In the configuration file, all numerical values are in CSS pixel unit (1/96th of an inch). Alternatively, strings containing the numerical value with a unit can be used and will be correctly interpreted.
Then, the configuration file must include one paper
section for each paper format supported by the plotter:
[[device.my_plotter.paper]] name = "a" # name of the paper format paper_size = ["11in", "8.5in"] # (optional) physical paper size / CAUTION: order must # respect the native X/Y axis orientation of the plotter # unless paper_orientation is specified # Note: may be omitted if the plotter support arbitrary # paper size paper_orientation = "portrait" # (optional) "portrait" or "landscape" # specify the orientation of the plotter coordinate # system on the page ("landscape" means the X axis is # along the long edge) origin_location = [".5in", "8in"] # physical location from the page's top-left corner of # the (0, 0) plotter unit coordinates origin_location_reference = "topleft" # (optional) reference used for origin_location # "topleft" (default) or "botleft" x_range = [0, 16640] # (optional) admissible range in plotter units along # the X axis y_range = [0, 10365] # (optional) admissible range in plotter units along # the Y axis y_axis_up = true # set to true if the plotter's Y axis points up on # the physical page rotate_180 = true # (optional) set to true to rotate the geometries by # 180 degrees on the page aka_names = ["ansi_a", "letter"] # (optional) name synonyms that will be recognised by # the `--paper-format` option of the `write` command set_ps = 0 # (optional) if present, a PS command with the # corresponding value is generated final_pu_params = "0,0" # (optional) if present, specifies parameter to pass # to the final `PU;` command info = "Paper loading..." # (optional) human-readable information on how the # paper must be loaded for this configuration to work # as expected
While most of the parameters above are self-explanatory or easy to understand from the comments, there are several aspects that require specific caution:
paper_size
must be defined in the order corresponding to the plotter’s native X/Y axis orientation. In the example above, the long side is specified before the short side because the plotter’s native coordinate system has its X axis oriented along the long side and the Y axis oriented along the short side of the page.origin_location
defines the physical location of (0, 0) plotter unit coordinate on the page, with respect to the top-left corner of the page in the orientation implied bypaper_size
. In the example above, since the long edge is defined first,origin_location
is defined based on the top-left corner in landscape orientation.y_axis_up
defines the orientation of the plotter’s native Y axis. Note that a value oftrue
does not imply thatorigin_location
is measured from the bottom-left corner, unlessorigin_location_reference
is set to"botleft"
.
Using arbitrary paper size with HPGL output¶
Some plotters such as the Calcomp Designmate support arbitrary paper sizes. Exporting HPGL with arbitrary paper size
requires a specific paper configuration. vpype ships with the flex
and flexl
configurations for the
Designmate, which can serve as examples to create configurations for other plotters.
For arbitrary paper size, the paper configuration must omit the paper_size
parameter and specify a value for
paper_orientation
. Here is the flexl
configuration for the Designmate when paper is loaded in landscape
orientation in the plotter:
[[device.designmate.paper]] name = "flexl" y_axis_up = true paper_orientation = "landscape" origin_location = ["15mm", "15mm"] origin_location_reference = "botleft" rotate_180 = true final_pu_params = "0,0"
Note the missing paper_size
, as well as the values for paper_orientation
and origin_location_reference
.
When using arbitrary paper size, the paper size is assumed to be identical to the current page size as set by the read, pagesize, or layout commands. Here is a typical example of use:
$ vpype read input.svg layout --fit-to-margins 3cm 30x50cm write -d designmate -p flexl output.hpgl
In this case, the page size is set by the layout command (30x50cm) and the write command is set to
use the flexl
paper configuration because the paper is loaded in landscape orientation in the plotter. If the input
SVG is already sized and laid out according to the paper size, the layout command may be omitted.
Customizing vpype¶
Creating a custom configuration file¶
Some of vpype’s features (such as HPGL export) or plug-in (such as vpype-gcode) can be customized using a configuration file using the TOML format. The documentation of the features or plug-in using such a configuration file explains what it should contain. This section focuses on how a custom config file is made available to vpype.
The most common way is to create a .vpype.toml file at the root of your user directory, e.g.:
C:\Users\username\.vpype.toml
on Windows/Users/username/.vpype.toml
on Mac/home/username/.vpype.toml
on Linux
If such a file exists, it will be automatically loaded by vpype whenever it is used.
Note
The .
prefix in the file name will make the file hidden on most systems. This naming is typical for configuration files in the Unix world.
Alternatively, a configuration file may be provided upon invocation of vpype using the --config
option (or -c
for short), e.g.:
(vpype_venv) $ vpype --config my_config_file.toml [...]
Note that vpype does not “remember” the provided configuration file. The --config
option must thus be provided on each invocation.
Note
vpype is bundled with a configuration file. It is strongly discouraged to edit this file as it will be overwritten each time vpype is installed or updated.
Creating a custom pen configuration¶
Pen configurations associate names, colors, and/or pen widths to specific layers and are applied by the pens
command. For example, the included cmyk
pen configuration sets the name and color or layers 1 to 4 to cyan, magenta,
yellow, resp. black, while leaving pen widths unchanged. New pen configurations can be defined in a custom config file
(see Creating a custom configuration file).
Pen configurations must conform to the following format to be valid:
[pen_config.my_pen_config] # my_pen_config is this pen configuration's name layers = [ # for each layer, a layer_id must be provided, but name, color and # pen_width are optional { layer_id = 1, name = "black layer", color = "black", pen_width = "0.15mm" }, # any valid CSS color string and length unit may be used { layer_id = 2, name = "red layer", color = "#e00", pen_width = "0.05in" }, # any attribute may be omitted, except layer_id { layer_id = 4, color = "#00de00" }, # etc. (a pen configuration may have an arbitrary number of layers defined) ]
The above pen configuration can be used by referring to its name, in this case my_pen_config
:
$ vpype [...] pens my_pen_config [...]
Miscellaneous recipes¶
Batch renaming layers¶
The name command can be used to assign a new name to a given layer. It is typically used as follows:
$ vpype [...] name --layer 3 "Layer 3" [...]
If the --layer
option is omitted, the provided name is assigned to all layers. This behaviour can be useful when combined with the lid
expression built-in variable:
$ vpype random --layer 1 random --layer 3 name "Layer %lid%" write output.svg
Here, two layers with IDs 1 and 3 are created with some random lines (e.g. to simulate loading a multi-layer file). Then, these layers are renamed with “Layer 1” and “Layer 3”, respectively, and the result written to the output.svg
file. The layer names can be verified by opening output.svg
in Inkscape.
Controlling the AxiDraw plotting process using layer names¶
See this article.
Create interactive scripts with input()
¶
The Python input()
function is available in expressions. It can be used to interactively query the use for parameter values. For example, this pipeline asks the user for a margin value and uses it to layout a SVG:
$ vpype \
eval "margin = float(input('Margin in cm? '))" \
read input.svg \
layout --fit-to-margins %margin*cm% a4 \
write output.svg
Margin in cm? 3
This pattern can be improved by providing a default value, allowing the user to simply type <Enter> to use it:
$ vpype \
eval "margin = float(input('Margin in cm [3cm]? ') or 3)" \
...
This works because of the particular way in which the or
operator behaves. It evaluates to the first operand whose truthiness is True
. When the user directly hits <Enter>, the first operand is an empty string, whose truthiness is False
. The or
expression thus evaluates to the second operand in this case.
See Laying out multiple SVGs on a grid for a real-world example that makes use of this pattern.
Batch processing multiple SVGs with forfile
¶
The forfile block processor can be used to apply the same processing on multiple files using the following pattern:
$ vpype forfile "*.svg" \
read "%_path%" \
linemerge linesort reloop linesimplify \
write "%_path.parent / _path.stem%_processed.svg" \
end
The basic idea is to enclose the desired pipeline, including the read and write commands, in a forfile block. The input path is the _path
expression variable set by forfile. A suitable output path can be constructed by combining _path.parent
(the directory the input file) and _path.stem
(the input file name without extension) in an expression, and adding the _processed.svg
suffix (see Using paths).
One of the drawbacks of this approach is that each file is processed sequentially without exploiting multiple CPU cores. For a large number of files, this may take some time. The next recipe introduces an alternative batch processing method which enables multi-core processing.
Batch processing multiple SVGs with parallel
¶
Computers offer endless avenues for automation, which depend on OS and the type of task at hand. Here is one way to
easily process a large number of SVG with the same vpype pipeline. This approach relies on the
GNU Parallel software and is best suited to Unix/Linux/macOS computers.
Thanks to parallel
, this approach takes advantage of all available processing cores.
This is an example that illustrates the general idea:
$ parallel --plus vpype read {} linemerge linesort write {/.svg/_processed.svg} ::: *.svg
Let’s break down how this works:
GNU parallel
will execute the command before the:::
maker for each argument it finds after the marker. In this example, we used*.svg
which expends to the list of SVG files in the current directory.The marker
{}
is replaced byGNU parallel
with the current item being processed (e.g. the current SVG file).The marker
{/.svg/_processed.svg}
does the same but it replaces.svg
by_processed.svg
. This way, if one of the original SVG file is calledmy_file.svg
, it will be saved asmy_file_processed.svg
once processed.The
--plus
option toGNU parallel
is required to enable the string replacement syntax.
The results can easily be customised by changing one or more of these elements. When designing your own command, it is
best to start with the --dry-run
option so that GNU parallel
just prints the jobs instead of actually executing
them:
$ parallel --dry-run --plus vpype read {} linemerge linesort write {/.svg/_processed.svg} ::: *.svg
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. This is very easy and explained in the Shapely documentation.