Shoebot

Shoebot is a tool to automate the process of drawing vector graphics using a minimal and easy-to-understand syntax.

It is a rewrite of Nodebox 1 by Frederik de Bleser and Tom de Smedt, with the purpose of having an equivalent tool in GNU/Linux systems. Nodebox 1 is itself based on DrawBot by Just van Rossum. Shoebot draws using the cross-platform Cairo graphics engine.

It follows a rich lineage of tools dedicated to generative creativity:

For more about the nature of creative coding and generative design, be sure to read The Nodebox 1 theoretical introduction.

Purpose

Shoebot is a useful tool for many use cases:

  • creating generative and procedural works, either for screen or print output

  • teaching code to non-developers by means of immediate visual feedback

  • prototyping visualizations and design concepts

  • automatically generating sets of vector images

  • live-coding and real-time tweaking of animated graphics

  • making Cairo-based tools and experiments using a simpler language

Features

Originally built as a GNU/Linux version of Nodebox 1, Shoebot comes with many batteries included:

  • supports most of Nodebox 1’s functionality

  • user-friendly code editor

  • headless mode for quick execution without the GUI parts

  • run on a window or output files in PDF, PNG, SVG or PS formats

  • tweak running scripts via a simple GUI, a socket server or a text-based shell

  • can be loaded as a Python module to work inside existing programs

Authors

Shoebot is currently maintained by Stuart Axon and Ricardo Lafuente.

A good part of the code has also been contributed to by Francesco Fantoni, Sebastian Oliva, Paulo Silva, Dave Crossland, Gabor Papp, Julien Deswaef , Pedro Ângelo and Tetsuya Saito. Examples also contributed to by Artem Popov, Barak Itkin and Simon Budig.


Documentation Index

Installation

Shoebot runs on Python 3.7 and above.

GNU/Linux

Installing dependencies

You need a few software packages on your system before installing Shoebot. You can either run the handy ./install/install_dependencies.sh script, or paste the command relevant to your distribution from the list below.

Debian and Ubuntu:

sudo apt install build-essential gir1.2-gtk-3.0 gir1.2-rsvg-2.0 \
gobject-introspection libgirepository1.0-dev libglib2.0-dev \
libgtksourceview-3.0-dev libjpeg-dev libpango1.0-dev python3-dev python3-gi \
python3-gi-cairo python3-wrapt

Arch and Manjaro:

pacman -S cairo gobject-introspection gobject-introspection-runtime gtk3 \
gtksourceview3 libjpeg-turbo librsvg pango python python-cairo python-gobject \
python3-wrapt

Fedora and CentOS:

sudo yum install cairo-gobject redhat-rpm-config gcc cairo-devel \
libjpeg-devel python3-devel python3-gobject python3-wrapt

SuSE:

sudo zypper install gcc libjpeg62-devel python-gobject python-gobject-cairo \
python3-wrapt
Installing Shoebot

It is recommended to install Shoebot locally, although it can be also be installed system-wide with sudo.

python3 setup.py install

If you prefer using virtual environments, we recommend using virtualenvwrapper. These are the steps:

mkvirtualenv shoebot -p $(which python3)
python3 setup.py install

To run Shoebot in the future, you will need to activate the environment first with workon shoebot.

Mac OS X

Installation on Mac OS X is identical to GNU/Linux based distributions.

Dependencies can be installed with the install_dependencies.sh script mentioned above, or by running:

pip3 install wrapt --user
brew install cairo gobject-introspection gtk+3 gtksourceview3 jpeg libffi \
  librsvg py3cairo pygobject3

Windows

Shoebot will install and run on Windows 64-bit (7 and above) using msys2.

The necessary dependencies can be installed by downloading and running the install_dependencies.sh script. Save this file to your Msys2 home\user directory (the default is C:\msys64\home\%YourUserName%\), run Msys2 Sys from the Start Menu and enter:

./install_dependencies.sh

When that is complete, enter:

git clone https://github.com/shoebot/shoebot

When that is done, run MinGW 64-bit from the Start Menu and enter:

cd shoebot
python setup.py install

After installing, the compiled executables can be used without running the Msys2 shell.

Trouble?

Installation is the trickiest step in Shoebot, and can be more challenging than we’d like. If you run into install problems, check the troubleshooting page.

Getting Started

For your first time with Shoebot, you can try out the included code editor by running shoebot in your terminal.

_images/shoebot-ide.png

Open one of the example scripts and Run it through the Run -> Run script menu option or the Ctrl-R keyboard shortcut. A window should open with an image or animation.

You can now start editing the code and re-running to see the new outcome. Changing values on example scripts is a great way to understand how things work.

The Shoebot code editor is however rather limited. We recommend trying out Atom with the Shoebot extension for a more powerful and customizable coding environment.

Running in the console

If you prefer using your own text editor to edit files and just want something to run the scripts, the sbot command is what you want. Head over to the examples/ directory and try running:

sbot grid/balls.bot

By default, Shoebot will open a window with the result. But we can also output directly to a file with the -o option, short for --outputfile. Supported formats are SVG, PNG, PDF and PS.

sbot grid/balls.bot -o balls.svg

There are many features available in the console runner that aren’t accessible in the Shoebot or Atom editors, so be sure to take a look at the Command line flags section.

Exporting video

The Atom extension has a right-click option to save to video, but you can also do this in the command line with the sbot-export-video script, which generates an image sequence and runs ffmpeg to put everything into an MP4 file.

sbot-video-export animation/wishyworm.bot -o worm.mp4 -f 150

The -f option (short for --framenumber) specifies the number of frames to render; the default value is 300, or 10 seconds in 30 FPS.

In the future, this feature will become part of the sbot command line runner.

Tutorial

The original Nodebox tutorial pages are an excellent introduction to the concepts that you’ll also find in Shoebot.

We’ve omitted the tutorials that relate to Mac-specific details or non-implemented features (like Psyco support). See the Nodebox tutorial page for all of them.

Reference and examples

This is the full list of commands available in Shoebot. For a more technical outline of the Shoebot API, see the API reference.

Many parts of this reference were adapted from the original Nodebox documentation by Frederik de Bleser and Tom de Smedt.

Note that most examples here are drawn on a 100x100 size for simplicity; the Shoebot default is 300x300px.

Shapes

rect(x, y, width, height, roundness=0, draw=True)

Draw a rectangle.

four rectangles.  The last three have increasingly round corners.
fill(0.95, 0.75, 0)
rect(10, 10, 35, 35)
# see how roundness affects the shape
rect(55, 10, 35, 35, 0.3)
rect(10, 55, 35, 35, 0.7)
rect(55, 55, 35, 35, 1)
rectmode(mode=None)

Change the way rectangles are specified. Each mode alters the parameters necessary to draw a rectangle using the rect() function.

There are 3 different modes available, each expecting different parameters:

  • CORNER mode (default) – origin point and dimensions (width and height)

    • x-value of the top left corner

    • y-value of the top left corner

    • width

    • height

  • CENTER mode – draw a shape centered on a point

    • x-coordinate of the rectangle’s center point

    • y-coordinate of the rectangle’s center point

    • width

    • height

  • CORNERS mode – origin point and destination point

    • x-coordinate of the top left corner

    • y-coordinate of the top left corner

    • x-coordinate of the bottom right corner

    • y-coordinate of the bottom right corner

So while you always specify 4 parameters to the rect() function, you can use rectmode() to change the function’s behaviour according to what might suit your script’s needs.

green rectangle top left, blue centered and red at the bottom right.
nofill()
strokewidth(2)

rectmode(CORNER)  # default, red
stroke(0.8, 0.1, 0.1)
rect(25, 25, 40, 40)

rectmode(CENTER)  # green
stroke(0.1, 0.8, 0.1)
rect(25, 25, 40, 40)

rectmode(CORNERS)  # blue
stroke(0.1, 0.1, 0.8)
rect(25, 25, 40, 40)
ellipse(x, y, width, height, draw=True)

Draw an ellipse by specifying the coordinates of its top left origin point, along with its width and height dimensions. See ellipsemode() for other ways of drawing ellipses.

Two ellipses.
ellipse(10, 20, 30, 60)
ellipse(50, 30, 40, 40) # circle

This command is identical to the oval() command from Nodebox, which is also supported and works the same.

ellipsemode(mode=None)

Change the way ellipses are specified. Each mode alters the parameters necessary to draw an ellipse using the ellipse() function.

It works exactly the same as the rectmode() command.

green ellipse top left, blue centered and red at the bottom right.
nofill()
strokewidth(2)

ellipsemode(CORNER)  # default, red
stroke(0.8, 0.1, 0.1)
ellipse(25, 25, 40, 40)

ellipsemode(CENTER)  # green
stroke(0.1, 0.8, 0.1)
ellipse(25, 25, 40, 40)

ellipsemode(CORNERS)  # blue
stroke(0.1, 0.1, 0.8)
ellipse(25, 25, 40, 40)
line(x1, y1, x2, y2, draw=True)

Draw a line from (x1,y1) to (x2,y2).

3 crossing lines.
stroke(0.5)
strokewidth(5)
line(20, 20, 80, 80)
line(20, 80, 80, 20)
line(50, 20, 50, 80)
arc(x, y, radius, angle1, angle2, type=CHORD, draw=True)

Draws a circular arc with center at (x,y) between two angles.

The default arc type (CHORD) only draws the contour of the circle arc section. The PIE arc type will close the path connecting the arc points to its center, as a pie-chart-like shape.

3 arcs
nofill()
stroke(.2)
autoclosepath(False)
arc(50, 50, 40, 0, 180)
arc(50, 50, 30, -90, 0)
stroke('#ff6633')
arc(50, 50, 20, 0, 270, type=PIE)
arrow(x, y, width, type=NORMAL, draw=True)

Draw an arrow with its tip at (x,y) and the specified width. Its type can be NORMAL (default) or FORTYFIVE.

An arrow pointing right, and another pointing to the bottom right.
arrow(50, 40, 40)
arrow(90, 40, 40, FORTYFIVE)
star(x, y, points=20, outer=100, inner=50, draw=True)

Draw a star-like polygon with its center at (x,y).

Following the coordinates, this command expects the number of points, the outer radius of the star shape, and finally the inner radius.

4 stars.
star(25, 25, 5, 20, 10)  # top left
star(75, 25, 10, 20, 3)  # top right
star(25, 75, 20, 20, 17) # bottom left
star(75, 75, 40, 20, 19) # bottom right

Paths

beginpath(x=None, y=None)

Start a new Bézier path. This command is needed before any other path drawing commands such as moveto(), lineto(), or curveto(). Finally, the endpath() command draws the path on the screen.

If x and y are not specified, this command should be followed by a moveto() call.

moveto(x, y)

Move the Bézier “pen” to the specified point without drawing.

Can only be called between beginpath() and endpath().

lineto(x, y)

Draw a line from the pen’s current point to the specified (x,y) coordinates.

Can only be called between beginpath() and endpath().

curveto(x1, y1, x2, y2, x3, y3)

Draws a curve between the current point in the path and a new destination point.

The last two parameters are the coordinates of the destination point. The first 4 parameters are the coordinates of the two control points, which define the edge and slant of the curve.

Can only be called between beginpath() and endpath().

Curve example
x, y = 10, 62     # Start curve point
x1, y1 = 50, 115  # Left control point
x2, y2 = 75, 10   # Right control point
x3, y3 = 115, 62  # End curve point

# Only strokes
autoclosepath(False)
nofill()
strokewidth(12)
stroke(0.1)

# Draw the curve
beginpath()
moveto(x, y)
curveto(x1, y1, x2, y2, x3, y3)
endpath()

# To show where the control points are,
# we draw helper lines
strokewidth(2)
stroke(1, 0.2, 0.2, 0.6)
# The first control point starts at the
# x, y position
line(x, y, x1, y1)
# And the second control point is the
# end curve point
line(x2, y2, x3, y3)
arcto(x, y, radius, angle1, angle2)

Continues the path with a circular arc in a way identical to arc(). A line will be drawn between the current point and the arc’s starting point.

closepath()

Close the path; in case the current point is not the path’s starting point, a line will be drawn between them.

endpath(draw=True)

The endpath() command is the companion command to beginpath(). When endpath() is called, the path defined between beginpath() and endpath() is drawn. Optionally, when endpath(draw=False) is called, the path is not drawn but can be assigned to a variable and drawn to the screen at a later time with the drawpath() command.

drawpath(path)

Draws a path on the screen. A path is a series of lines and curves defined between beginpath() and endpath(). Normally, endpath() draws the path to the screen, unless when calling endpath(draw=False). The path can then be assigned to a variable, and this variable used as a parameter for drawpath().

Note: if you have one path that you want to draw multiple times with drawpath(), for example each with its own rotation and position, you need to supply a copy with drawpath(path.copy()).

Drawpath example
stroke(0.2)
beginpath(10, 10)
lineto(40, 10)
p = endpath(draw=False)
drawpath(p)
autoclosepath(close=True)

Defines whether paths are automatically closed by connecting the last and first points with a line. It takes a single parameter of True or False. All shapes created with beginpath() following this command will adhere to the setting.

findpath(points, curvature=1.0)

Constructs a fluid path from a list of coordinates. Each element in the list is a 2-tuple defining the x-coordinate and the y-coordinate. If the curve has more than three points, the curvature parameter offers some control on how separate segments are stitched together: from straight lines (0.0) to smooth curves (1.0).

Findpath example
points = [(10, 10), (90, 90), (350, 200)]
ellipsemode(CENTER)
for x, y in points:
    ellipse(x, y, 6, 6)

nofill()
stroke(0.2)
autoclosepath(False)
path = findpath(points)
drawpath(path)
beginclip(path)
endclip()

The beginclip() and endclip() commands define a clipping mask. The supplied parameter defines the path to be used as a clipping mask.

All basic shapes and path commands return paths that can be used with beginclip() - setting the draw parameter of a shape command will simply return the path without actually drawing the shape. Any shapes, paths, texts and images between beginclip() and endclip() are clipped: any part that falls outside the clipping mask path is not drawn.

Clipped lines
p = ellipse(20, 20, 60, 60, draw=False)
beginclip(p)
stroke(0.5)
strokewidth(15)
line(20, 20, 80, 80)
line(20, 80, 80, 20)
line(50, 20, 50, 80)
endclip()

Text

text(txt, x, y, width=None, height=None, outline=False, draw=True)

Draws a string of text according to the current font settings.

This command takes 3 mandatory arguments: the string of text to write and the (x, y) coordinates of the baseline origin.

If width is set, the text will wrap (move to the next line) when it exceeds the specified value. Setting height will limit the vertical size of the text box, after which no text will be drawn.

If the outline option is true, the resulting object will be a BezierPath instead of a Text object. It’s an alternative to using textpath().

The word 'bot' in bold and italic styles
# when using text(), the origin point
# is on the text baseline
arrow(12, 65, 10, type=FORTYFIVE, fill='#ff9966')
# place the text box
font("Inconsolata", 50)
text("Bot", 12, 65)
font(fontpath=None, fontsize=None)

Sets the font to be used in new text instances. Accepts a system font name, e.g. “Inconsolata Bold”, and an optional font size value. Returns the current font name.

A full list of your system’s font names can be viewed with the pango-list command in a terminal.

The word 'bot' in bold and italic styles
fill(0.3)
fontsize(16)

font("Liberation Mono")
text("Bot", 35, 25)
font("Liberation Mono Italic")
text("Bot", 35, 45)
font("Liberation Mono Bold")
text("Bot", 35, 65)
font("Liberation Mono Bold Italic")
text("Bot", 35, 85)

Variable fonts are supported. You can specify the value for an axis using keyword arguments with the var_ prefix: to set the wdth axis to 100, use var_wdth=100.

Alternatively, you can provide a vars dictionary with each axis’s values, e.g. font("Inconsolata", vars={"wdth": 100, "wght": 600})

The word 'bot' in bold and italic styles
fill(0.3)
fontsize(30)

for x, y in grid(5, 4, 20, 22):
    font("Inconsolata", var_wdth=y+50, var_wght=x*12)
    text("R", 3+x, 25+y)

Note that for the above example to work, you need to install the variable version of Inconsolata.

fontsize(fontsize=None)

Sets the size of the current font to use, and returns the current size.

textpath(txt, x, y, width=None, height=1000000, draw=False)

Returns an outlined path of the input text.

For an explanation of the parameters, see text(). Note that, unline text(), the draw option is False by default, as this command is meant for doing further manipulation on the text path before rendering it.

textmetrics(txt, width=None, height=None)

Returns a (width, height) tuple with the dimensions of the text box containing a string of text, according to the current font settings.

textbounds(txt, width=None, height=None)

Returns a (width, height) tuple with the dimensions of the actual shapes (inked part) of a string of text, according to the current font settings.

textwidth(txt, width=None)

Accepts a string and returns its width, according to the current font settings.

textheight(txt, width=None)

Accepts a string and returns its height, according to the current font settings.

lineheight(height=None)

Set the space between lines of text.

align(align=LEFT)

Set the way lines of text align with each other. Values can be LEFT, CENTER or RIGHT.

fontoptions(hintstyle=None, hintmetrics=None, subpixelorder=None, antialias=None)

Sets text rendering options.

The antialias option specifies the type of antialiasing to do:

  • default – use the default antialiasing for the subsystem and target device

  • none – no antialiasing

  • gray – single-color antialiasing

  • subpixel – take advantage of the order of subpixel elements on devices such as LCD panels

  • fast – prefer speed over quality

  • good – balance quality against performance

  • best – render at the highest quality, sacrificing speed if necessary

The subpixelorder sets the order to use with the antialias subpixel option:

  • rgb – arranged horizontally with red at the left

  • bgr – arranged horizontally with blue at the left

  • vrgb – arranged vertically with red at the top

  • vbgr – arranged vertically with blue at the top

The hintstyle option sets the amount of font hinting to apply:

  • default – use the default hint style for font backend and target device

  • none – do not hint outlines

  • slight – improve contrast while retaining good fidelity to the original shapes

  • medium – compromise between fidelity to the original shapes and contrast

  • full – maximize contrast

The hintmetrics option (on or off) deals with hint metrics, which means quantizing (or “rounding”) glyph outlines so that they are integer values. Doing this improves the consistency of letter and line spacing, but it also means that text will be laid out differently at different zoom factors.

Colors

Shapes can be filled, stroked, or both. The fill() and stroke() commands are used to set the colors for those operations globally. In addition, most drawing commands have fill and stroke parameters to allow setting colors for single objects.

Fill and stroke colors can be specified in a few ways:

  • grayscale: (value) and (value, alpha)

  • RGB: (red, green, blue) and (red, green, blue, alpha)

  • hex colors: ('#FFFFFF') and ('#FFFFFFFF')

  • Color objects as created by color()

The grayscale and RGB options take values between 0 and 1; this behavior can be changed with colorrange().

background(*args)

Set the background color.

Background example
background(0.9)
fill(1)
circle(40, 40, 20)
fill(color)

Sets a fill color, applying it to new paths.

nofill()

Stop applying fills to new paths.

Returns the fill color that was active before the nofill() call.

stroke(color)

Set a stroke color, applying it to new paths.

This command can be used without arguments, in which case it returns the current stroke color. When used to set a color, it returns the new color value.

nostroke()

Stop applying strokes to new paths.

Returns the stroke color that was active before the nostroke() call.

strokewidth(w=None)

Set the width of the stroke in new paths.

Returns the current stroke width.

Stroke widths
stroke(0.2)
strokewidth(1)
line(20, 20, 20, 110)
strokewidth(3)
line(40, 20, 40, 110)
strokewidth(10)
line(60, 20, 60, 110)
strokewidth(15)
line(80, 20, 80, 110)
strokedash(dashes, offset=0)

Sets a dash pattern to be used in stroked shapes.

A dash pattern is specified by dashes - a list of positive values. Each value provides the length of alternate “on” and “off” portions of the stroke.

The offset specifies an offset into the pattern at which the stroke begins.

Each “on” segment will have caps applied as if the segment were a separate sub-path. In particular, it is valid to use an “on” length of 0 with a round or square stroke cap (see strokecap()) in order to distribute dots or squares along a path.

If the number of dashes is 0, dashing is disabled.

If the number of dashes is 1, a symmetric pattern is assumed with alternating on and off portions of the size specified by the single value in dashes.

Stroke dashes
nofill()
stroke(0.2)
strokewidth(3)

circle(5,5,40)

strokedash([3,2,1,2])
circle(55,5,40)

strokedash([10,15,5])
circle(5,55,40)

strokedash([10,15,5], 20)
strokecap(ROUND)
circle(55,55,40)
strokecap(cap)

Sets the cap to be drawn at the ends of strokes.

This command can be called with a new cap value:

  • BUTT – start/stop the line exactly at the start/end point

  • ROUND – use a round ending, the center of the circle is the end point

  • SQUARE – use a squared ending, the center of the square is the end point

If called with no arguments, returns the current cap value.

Stroke caps
stroke(0.2)
strokewidth(15)
line(25, 25, 25, 110)
strokecap(ROUND)
line(50, 25, 50, 110)
strokecap(SQUARE)
line(75, 25, 75, 110)
strokejoin(join)

Sets the join shape to use be drawn at the ends of strokes.

This command can be called with a new join value:

  • MITER – use a sharp angled corner (default)

  • ROUND – use a rounded join, the center of the circle is the joint point

  • BEVEL – use a cut-off join, the join is cut off at half the line width from the joint point

If called with no arguments, returns the current join value.

Stroke joins
autoclosepath(False)
nofill()
stroke(0.2)
strokewidth(15)

beginpath(10,25)
lineto(40,50)
lineto(10,75)
endpath()
translate(25,0)

strokejoin(ROUND)
beginpath(10,25)
lineto(40,50)
lineto(10,75)
endpath()
translate(25,0)

strokejoin(BEVEL)
beginpath(10,25)
lineto(40,50)
lineto(10,75)
endpath()
color(*args)

Returns a Color object that can be stored in a variable and reused.

Color reuse
teal = color("#008080")

rect(20, 20, 60, 15, fill=teal)
rect(20, 40, 60, 15, fill=teal)
rect(20, 60, 60, 15)
colormode(mode=None, crange=None)

Set the current color mode, which can be RGB or HSB, and optionally the color range.

colorrange(crange=1.0)

Set the numeric range for color values. By default colors range from 0.0 - 1.0, and this command can set this to a different range. For example, a scale of 0 to 255 can be set with colorrange(255).

Color range example
colorrange(255)
background(127)
fill(255)
circle(40, 40, 20)
blendmode(mode):

Sets the blending mode to apply to the colors of new elements.

Blending modes, also known as Porter-Duff compositing operations, are ways to combine two images. Usually, an image (destination) placed on top of another (destination) completely covers it; this is the OVER blending mode, but there are many others that give distinct results, and which you might know from image editors.

  • OVER – draw source layer on top of destination layer

  • MULTIPLY – source and destination layers are multiplied. This causes the result to be at least as dark as the darker inputs.

  • SCREEN – source and destination are complemented and multiplied. This causes the result to be at least as light as the lighter inputs.

  • OVERLAY – multiplies or screens, depending on the lightness of the destination color

  • DARKEN – replaces the destination with the source if it is darker, otherwise keeps the source

  • LIGHTEN – replaces the destination with the source if it is lighter, otherwise keeps the source.

  • COLORDODGE – brightens the destination color to reflect the source color

  • COLORBURN – darkens the destination color to reflect the source color

  • HARDLIGHT – multiplies or screens, dependent on source color

  • SOFTLIGHT – darkens or lightens, dependent on source color

  • DIFFERENCE – takes the difference of the source and destination color

  • EXCLUSION – produces an effect similar to difference, but with lower contrast

  • HUE – creates a color with the hue of the source and the saturation and luminosity of the target

  • SATURATION – creates a color with the saturation of the source and the hue and luminosity of the target. Painting with this mode onto a gray area produces no change.

  • COLOR – creates a color with the hue and saturation of the source and the luminosity of the target. This preserves the gray levels of the target and is useful for coloring monochrome images or tinting color images.

  • LUMINOSITY – creates a color with the luminosity of the source and the hue and saturation of the target. This produces an inverse effect to COLOR.

  • ATOP – draw source on top of destination content and only there

  • DEST – ignore the source

  • DEST_OVER – draw destination on top of source

  • DEST_ATOP – leave destination on top of source content and only there

  • XOR – source and destination are shown where there is only one of them

  • ADD – source and destination layers are accumulated

  • SATURATE – like over, but assuming source and dest are disjoint geometries

The Wikipedia page on blending modes is a deeper reference on how these work, and the Cairo operators page is also a good resource.

fillrule(rule=WINDING)

Sets the fill rule to be used in filled shapes.

The fill rule is used to determine which regions are inside or outside a complex (potentially self-intersecting) path.

Transforms

transform(mode=None)

Sets whether shapes are transformed along their centerpoint or (0,0).

The mode parameter can be CORNER (default) or CENTER.

It sets the registration point – the offset for rotate(), scale() and skew() commands. By default, primitives, text, and images rotate around their own centerpoints. But if you call transform() with CORNER as its mode parameter, transformations will be applied relative to the canvas top left corner (its “origin point”) instead.

See the examples in translate(), rotate(), scale() and skew() to see how the transform mode affects the result.

translate(xt, yt)

Specifies the amount to move the canvas origin point.

Once called, all commands following translate() are repositioned, which makes translate() useful for positioning whole compositions of multiple elements.

Two circles
fill(0.2)
oval(10, 10, 40, 40)
translate(45, 45)
oval(10, 10, 40, 40)
rotate(degrees=0, radians=0)

Rotates all subsequent drawing commands.

The default unit is degrees; radians can be used with rotate(radians=PI).

This command works incrementally: if you call rotate(30), and later on call rotate(60), all commands following that second rotate() will be rotated 90° (30+60).

Rotated squares
fill('#4a69bd', 0.2)
translate(25, 25)
for i in range(7):
    rotate(15)
    rect(0, 0, 50, 50)
Rotated squares
fill('#e55039', 0.2)
transform(CENTER)
for i in range(5):
    rotate(15)
    rect(25, 25, 50, 50)
scale(x=1, y=None)

Increases, decreases, or streches the size of all subsequent drawing commands.

The first parameter sets the horizontal scale and the optional second parameter the vertical scale. You can also call scale() with a single parameter that sets both the horizontal and vertical scale. Scale values are specified as floating-point (decimal) numbers with 1.0 corresponding to 100%.

This command works incrementally: if you call scale(0.5), and later on call scale(0.2), all subsequent drawing commands will be sized to 10% (0.2 of 0.5).

Scaled squares
fill('#78e08f', 0.2)
translate(25,25)
for i in range(7):
    rect(0, 0, 50, 50)
    scale(.8)
Scaled squares
fill('#60a3bc', 0.2)
transform(CENTER)
for i in range(7):
    rect(25, 25, 50, 50)
    scale(.8)
skew(x=1, y=0)

Slants the direction of all subsequent drawing commands.

The first parameter sets the horizontal skew. The second parameter is optional and sets the vertical skew.

This command works incrementally: if you call skew(10), and later on call skew(20), all subsequent drawing commands will be skewed by 30° (10+20).

Skewed squares
fill('#82ccdd', 0.2)
translate(5, 25)
for i in range(7):
    rect(0, 0, 50, 50)
    skew(.2, 0)
Skewed squares
fill('#e58e26', 0.2)
transform(CENTER)
for i in range(7):
    rect(25, 25, 50, 50)
    skew(.2, 0)
push()

Saves the current transform state.

The push() function, along with its companion pop(), allows for “saving” a transform state. All transformations, such as rotate() and skew(), defined between push() and pop() will stop being applied after pop() is called.

Text with push and pop
fill(0.2)
fontsize(14)
transform(CENTER)
rotate(45)
text("one", 40, 40)

push()
rotate(-45)
text("two", 40, 80)
pop()

text("three", 40, 120)
pop()

Restores the saved transform state.

This command is meant to be used after push(). It “loads” the transform state that was set before the call to push().

reset()

Resets the transform state to its default values.

Text with transform reset
transform(CENTER)
rotate(30)
text("one", 10, 20)
text("two", 10, 50)
reset()
text("three", 10, 80)

Images

image(path, x=0, y=0, width=None, height=None, alpha=1.0, data=None, draw=True)

Place an image on the canvas with (x,y) as its top left corner. Both bitmap and SVG images can be used; in the case of SVG images, the result is rendered as paths (not bitmaps).

If width and height are specified, the image is resized to fit. The alpha parameter (0-1) controls the image opacity.

A filename is expected, but you can use the data argument instead to pass image data as a string or file-like object.

Image example
image("source/images/sign.jpg", 0, 0, 100, 100)
imagesize(path)

Get the dimensions of an image file as a (width, height) tuple.

Utility

var(name, type, default=None, min=0, max=255, value=None, step=None, steps=256.0)

Creates a live variable, which can be manipulated using the variables UI, socket server or live coding shell.

The first two arguments are the variable name and type (NUMBER, TEXT, BOOLEAN or BUTTON).

An optional third argument is the default (initial) value. For NUMBER variables, the minimum and maximum values for the variable can be indicated.

Finally, there are two options for setting the step length on the variables interface. This is the “jump” between values if you don’t want to use a continuous scale. You can either set a fixed number of steps using the steps option, or set a step length with the step option.

random(v1=None, v2=None)

Returns a random number that can be assigned to a variable or a parameter. When no parameters are supplied, returns a floating-point (decimal) number between 0.0 and 1.0 (including 0.0 and 1.0). When one parameter is supplied, returns a number between 0 and this parameter. When two parameters are supplied, returns a number between the first and the second parameter.

Random example
r = random() # returns a float between 0 and 1
r = random(2.5) # returns a float between 0 and 2.5
r = random(-1.0, 1.0) # returns a float between -1.0 and 1.0
r = random(5) # returns an int between 0 and 5
r = random(1, 10) # returns an int between 1 and 10

# sets the fill to anything from
# black (0.0,0,0) to red (1.0,0,0)
fill(random(), 0, 0)
circle(40, 40, 20)

# Note: new random values are returned each time the script runs.
# The variation can be locked by supplying a custom random seed:

from random import seed
seed(0)
grid(cols, rows, colSize=1, rowSize=1, shuffled=False)

This command returns an iterable object which can be traversed in a loop.

The first two parameters define the number of columns and rows in the grid. The next two parameters are optional, and set the width and height of one cell in the grid. In each loop iteration, the offset for the current column and row is returned.

If shuffled is True, the cells will be returned in a random order.

Grid example
translate(10, 10)
for x, y in grid(7, 5, 12, 12):
    rect(x, y, 10, 10)
fontnames()

Returns a list of system font faces, in the same format that font() expects.

files(path='*')

Retrieves all files from a given path and returns their names as a list. Wildcards can be used to specify which files to pick, e.g. f = files('*.gif')

autotext(sourceFile)

Accepts a source file name, and generates mock philosophy based on a context-free grammar.

Core

ximport(libName)

Imports a Nodebox library.

See the Libraries page for a full list.

size(w, h)

Sets the size of the canvas. Only the first call will have any effect.

speed(framerate)

Sets the frame rate for animations (frames per second), and returns the current frame rate.

run(inputcode, iterations=None, run_forever=False, frame_limiter=False)

Executes the contents of a Shoebot script in the current surface’s context.

Shapes

Paths

Text

Colors

Transforms

Images

Utility

Core

Showcase

Nodebox demos

This is a selection of demos from Nodebox 1, by the Nodebox authors (Frederik de Bleser and Tom de Smedt), with slight edits.

Math sculpture

Generates sculptures using a set of mathematical functions. Every iteration adds a certain value to the current coordinates. Rewriting this program to use transforms is left as an exercise for the reader.

from math import sin, cos, log10

size(400, 800)
background(0)

cX = random(1, 10)
cY = random(1, 10)

x = 200
y = 54
fontsize(10)

for i in range(278):
    x += cos(cY) * 11
    y += log10(cX) * 1.85 + sin(cX) * 5

    fill(random() - 0.4, 0.8, 0.8, random())

    s = 10 + cos(cX) * 15
    oval(x - s / 2, y - s / 2, s, s)
    # Try the next line instead of the previous one to see how
    # you can use other primitives.
    # star(x-s/2, y-s/2, random(5, 10), inner=2+s*0.1, outer=10+s*0.1)

    cX += random(0.25)
    cY += random(0.25)
from math import sin, cos, log10

size(400, 800)
background(0)

cX = random(1, 10)
cY = random(1, 10)

x = 200
y = 54
fontsize(10)

for i in range(278):
    x += cos(cY) * 11
    y += log10(cX) * 1.85 + sin(cX) * 5

    fill(random() - 0.4, 0.8, 0.8, random())

    s = 10 + cos(cX) * 15
    oval(x - s / 2, y - s / 2, s, s)
    # Try the next line instead of the previous one to see how
    # you can use other primitives.
    # star(x-s/2, y-s/2, random(5, 10), inner=2+s*0.1, outer=10+s*0.1)

    cX += random(0.25)
    cY += random(0.25)
Ball grid

Use a grid to generate a bubble-like composition.

This example shows that a grid doesn’t have to be rigid at all. It’s very easy to break loose from the coordinates Shoebot passes you, as is shown here. The trick is to add or subtract something from the x and y values Shoebot passes on. Here, we also use random sizes.

from math import sin, cos

size(600, 600)

gridSize = 40

# Translate a bit to the right and a bit to the bottom to
# create a margin.
translate(100, 100)

startval = random()
c = random()
for x, y in grid(10, 10, gridSize, gridSize):
    fill(sin(startval + y * x / 100.0), cos(c), cos(c), random())
    s = random() * gridSize
    oval(x, y, s, s)
    fill(cos(startval + y * x / 100.0), cos(c), cos(c), random())
    deltaX = (random() - 0.5) * 10
    deltaY = (random() - 0.5) * 10
    deltaS = (random() - 0.5) * 200
    oval(x + deltaX, y + deltaY, deltaS, deltaS)
    c += 0.01
from math import sin, cos

size(600, 600)

gridSize = 40

# Translate a bit to the right and a bit to the bottom to
# create a margin.
translate(100, 100)

startval = random()
c = random()
for x, y in grid(10, 10, gridSize, gridSize):
    fill(sin(startval + y * x / 100.0), cos(c), cos(c), random())
    s = random() * gridSize
    oval(x, y, s, s)
    fill(cos(startval + y * x / 100.0), cos(c), cos(c), random())
    deltaX = (random() - 0.5) * 10
    deltaY = (random() - 0.5) * 10
    deltaS = (random() - 0.5) * 200
    oval(x + deltaX, y + deltaY, deltaS, deltaS)
    c += 0.01
Blines and circloids

by Tom de Smedt <https://www.nodebox.net/code/index.php/Blines_and_Circloids>

# You'll need the Boids and Cornu libraries.
boids = ximport("boids")
cornu = ximport("cornu")

size(550, 550)
background(0.1, 0.1, 0.0)
nofill()

flock = boids.flock(10, 0, 0, WIDTH, HEIGHT)

n = 70
for i in range(n):

    flock.update(shuffled=False)

    # Each flying boid is a point.
    points = []
    for boid in flock:
        points.append((boid.x, boid.y))

    # Relativise points for Cornu.
    for i in range(len(points)):
        x, y = points[i]
        x /= 1.0 * WIDTH
        y /= 1.0 * HEIGHT
        points[i] = (x, y)

    t = float(i) / n
    stroke(0.9, 0.9, 4 * t, 0.6 * t)
    cornu.drawpath(points, tweaks=0)
# You'll need the Boids and Cornu libraries.
boids = ximport("boids")
cornu = ximport("cornu")

size(550, 550)
background(0.1, 0.1, 0.0)
nofill()

flock = boids.flock(10, 0, 0, WIDTH, HEIGHT)

n = 70
for i in range(n):

    flock.update(shuffled=False)

    # Each flying boid is a point.
    points = []
    for boid in flock:
        points.append((boid.x, boid.y))

    # Relativise points for Cornu.
    for i in range(len(points)):
        x, y = points[i]
        x /= 1.0 * WIDTH
        y /= 1.0 * HEIGHT
        points[i] = (x, y)

    t = float(i) / n
    stroke(0.9, 0.9, 4 * t, 0.6 * t)
    cornu.drawpath(points, tweaks=0)
Color grid

This example showcases the HSB color mode to select colors more naturally, by specifying a hue, saturation and brightness.

size(625, 625)

colormode(HSB)

# Set some initial values. You can and should play around with these.
h = 0
s = 0.5
b = 0.9
a = 0.5

# Size is the size of one grid square.
square_size = 50

# Using the translate command, we can give the grid some margin.
translate(50, 50)

# Create a grid with 10 rows and 10 columns. The width of the columns
# and the height of the rows is defined in the 'size' variable.
for x, y in grid(10, 10, square_size, square_size):
    # Increase the hue while choosing a random saturation.
    # Try experimenting here, like decreasing the brightness while
    # changing the alpha value etc.
    h += 0.01
    s = random()

    # Set this to be the current fill color.
    fill(h, s, b, a)

    # Draw a rectangle that is one and a half times larger than the
    # grid size to get an overlap.
    rect(x, y, square_size * 1.5, square_size * 1.5)
size(625, 625)

colormode(HSB)

# Set some initial values. You can and should play around with these.
h = 0
s = 0.5
b = 0.9
a = 0.5

# Size is the size of one grid square.
square_size = 50

# Using the translate command, we can give the grid some margin.
translate(50, 50)

# Create a grid with 10 rows and 10 columns. The width of the columns
# and the height of the rows is defined in the 'size' variable.
for x, y in grid(10, 10, square_size, square_size):
    # Increase the hue while choosing a random saturation.
    # Try experimenting here, like decreasing the brightness while
    # changing the alpha value etc.
    h += 0.01
    s = random()

    # Set this to be the current fill color.
    fill(h, s, b, a)

    # Draw a rectangle that is one and a half times larger than the
    # grid size to get an overlap.
    rect(x, y, square_size * 1.5, square_size * 1.5)
Circles and Beziers
size(640, 400)
colorrange(255)
colormode(HSB)
background(0, 0, 192)

for i in range(0, WIDTH // 4, 1):
    # ^ range requires a float, so use integer division '//'
    sz = random(WIDTH / 40, WIDTH / 5)
    xpos = random(-WIDTH / 5, WIDTH)
    ypos = random(-WIDTH / 5, HEIGHT)

    fill(0, 0, random(192, 224), random(85, 255))
    ellipse(xpos, ypos, sz, sz)

    nofill()
    stroke(0, 0, 0, 255)
    strokewidth(.1)
    ellipse(xpos, ypos, sz, sz)

    for j in range(0, WIDTH // 80, 1):
        # ^ range requires a float, so use integer division '//'
        stroke(0, 0, 0, 255)
        strokewidth(.1)
        beginpath(random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5))
        curveto(random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5),
                random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5),
                random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5))
        endpath()
size(640, 400)
colorrange(255)
colormode(HSB)
background(0, 0, 192)

for i in range(0, WIDTH // 4, 1):
    # ^ range requires a float, so use integer division '//'
    sz = random(WIDTH / 40, WIDTH / 5)
    xpos = random(-WIDTH / 5, WIDTH)
    ypos = random(-WIDTH / 5, HEIGHT)

    fill(0, 0, random(192, 224), random(85, 255))
    ellipse(xpos, ypos, sz, sz)

    nofill()
    stroke(0, 0, 0, 255)
    strokewidth(.1)
    ellipse(xpos, ypos, sz, sz)

    for j in range(0, WIDTH // 80, 1):
        # ^ range requires a float, so use integer division '//'
        stroke(0, 0, 0, 255)
        strokewidth(.1)
        beginpath(random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5))
        curveto(random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5),
                random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5),
                random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5))
        endpath()

Libraries

Shoebot has a rich set of external libraries to enable new tools and commands.

Colors

The Colors library offers a set of tools to work with color more conveniently.

You should read the original Nodebox documentation for the Colors library. The below parts are a work in progress port of the Nodebox docs.


You can use the library to create colors by name (like red or ivory), from pixels in an image, group them into lists of which you can then collectively manipulate hue, brightness and saturation, create lists of harmonious colors based on color theory rules (like complementary or analogous), create lists of gradient colors, work with drop shadows and gradient fills for paths, define powerful indefinite color ranges (like bright red or purplishgreenish) and more.

How to get the library up and running

Just place this line at the start of your script:

colors = ximport("colors")

Outside of Shoebot you can also just import colors. Color lists from image pixels then work with PIL/Pillow.

(…)

Colors

The Colors library has a number of commands that create a new color you can use with fill() or stroke().

rgb(r, g, b, a=None, range=1.0, name="")
hsb(h, s, b, a=None, range=1.0, name="")
cmyk(c, m, y, k, range=1.0, name="")
lab(l, a, b, range=1.0, name="")
hex(str, name="")

With the range parameter you can define how you want to supply the channel values. For example, if you want to define r, g and b between 0 and 255 instead of between 0.0 and 1.0, set range to 255.

The optional name parameter lets you define a name for the color. Otherwise, a name will be guessed using the clr.nearest_hue() method (see below).

The hex() command creates a color from a hexadecimal string (e.g. “#30343D”).

The named_color() command creates a color from a name like “olive” or “maroon” or “antiquewhite”. A list of all the named colors the command will recognize is here. The really great thing is that each of these named colors is also a command in the Colors library. So the two colors in the example below are exactly the same:

clr1 = colors.named_color("olive")
clr2 = colors.olive()
Color properties

Each of the above commands returns a Color object. It has all the standard properties a color created with the NodeBox color() command also has. You can use these to find out the color’s R, B and B values, or its C, M, Y and K values, or its H, S and B values:

  • clr.r: the red value in RGB.

  • clr.g: the green value in RGB.

  • clr.b: the blue value in RGB.

  • clr.a: the alpha value (opacity).

  • clr.c: the cyan value in CMYK.

  • clr.m: the magenta value in CMYK.

  • clr.y: the yellow value in CMYK.

  • clr.k: the black value in CMYK.

  • clr.hue: the hue of the color in HSB.

  • clr.saturation: the saturation (grayness) of the color in HSB.

  • clr.brightness: the brightness of the color in HSB.

The Color object in the Colors Library has some additional properties:

  • clr.name: the name of this color.

  • clr.is_black: will be True when the color’s R, G and B values are 0.

  • clr.is_white: will be True when the color’s R, G and B values are 1.

  • clr.is_gray: will be True when the color’s R, G and B values are the same.

  • clr.is_transparent: will be True when the color is completely transparent.

  • clr.complement: the complementary color (i.e. 180 degrees across the color wheel) of this color.

Color list math

Individual colors (or lists of colors) can be added to a list with the + operator:

clrs = colors.list(
    colors.purple().darken(),
    colors.deeppink()
)
clrs += colors.violet()
clrs.swarm(50, 50)
clrs = colors.list(
    colors.purple().darken(),
    colors.deeppink()
)
clrs += colors.violet()
clrs.swarm(50, 50)

Also, the * operator is equivalent to the list.repeat() method.

Bezier

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the Bezier library,

Cornu

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the Cornu library,

SVG

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the SVG library,

Graph

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the Graph library,

Photobot

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the Photobot library,

Boids

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the Boids library, since it should work just the same on Shoebot.

If it does not, please let us know by making a new issue on the Shoebot repository.

L-System

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the L-System library,

Audio

Grab audio frequencies to create visualizations.

Import it with ximport("sbaudio").

Requires the pysoundcard Python module to work.

fft_bandpassfilter(data, fs, lowcut, highcut)
flatten_fft(scale=1.0)
scaled_fft(fft, scale=1.0)
triple(spectrogram)
fuzzydevices(match='', min_ratio=30)
firstfuzzydevice(match='')

Video

This is a very basic video library. The library itself is built upon OpenCV, a library of programming functions mainly aimed at real time computer vision.

You need opencv and its python bindings in order to use this library, so on debian-based Linux you will have to install python-opencv and libhighgui1 or similar.

The Video Library is meant to grab frames from webcams, surveillance cameras or video files, and to make them available to Shoebot for displaying and elaboration.

For cameras, two camera interfaces can be used on Windows: Video for Windows (VFW) and Matrox Imaging Library (MIL). On Linux, we can use V4L and FireWire (IEEE1394).

For video files, this library uses specific backends on each OS:

  • ffmpeg on Linux

  • Video for Windoes (VfW) on Windows

  • QuickTime on Mac OS X

Up to now only camera and video frame grabbing is supported, but future development could introduce features like face and object recognition or video output to file for Shoebot animation. Future development will depend on evolution of the SWIG python bindings of OpenCV, which at present is still in-progress.

Basic usage

First, import the library:

videolib = ximport("sbvideo")

Then you can create two different types of capture devices:

# for video files
video = videolib.movie(file_path)
# or for webcam
camera = videolib.camera(index, width, height)

If you have only one camera, you can omit index and the first camera should be picked. Parameters width and height are optional, and they’re not guaranteed to work with your camera. Now you can grab a video frame with:

frame = video.frame() or frame = camera.frame()

frame has some properties: frame.width frame.height frame.time # strange results at present frame.data

You need frame.data in order to pass your frame image to the Shoebot canvas using the image() command:

image(None, xpos, ypos, data=frame.data)

You can manipulate and use the image as any other image in Shoebot.

When you call the video-file capture constructor, you’re supposed to be able to set a starting point in seconds from file beginning, but at present this feature is disabled as I could not get it to work properly on my linux system If you want to test it you can uncomment the relative lines in SVL. (If you succeed, please report it to the Shoebot issue tracker).

Commands
sbvideo.movie(path, start=0, stop=None)
sbvideo.camera(cam=0, width=None, height=None)

OpenCV

sbopencv.movie(path, start=0, stop=None)
sbopencv.camera(cam=0, width=None, height=None)
sbopencv.image(path=None)
sbopencv.ipl2cairo(iplimage)
sbopencv.detectHaar(iplimage, classifier)
sbopencv.findcontours(iplimage, threshold=100)

Database

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the Database library,

Bezier Editor

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the Bezier Editor library,

TUIO

We’re still in the process of porting the Nodebox library documentation to here.

In the meantime, read the original Nodebox documentation for the TUIO library,

Live Variables

Shoebot is not limited to what you write inside your .bot file; you can open your sketch for receiving and reacting to data from outside applications and environments.

These can be created using the var() command.

Using the live variables GUI

When a script uses the var keyword, the corresponding widget will appear in the live variables GUI.

The following code will make a slider with a minimum value of 0, a maximum value of 100, and an initial (default) value of 25.

var('a_number', NUMBER, 25., 0., 100.)
_images/live_vars.png

For readability names are capitalized and underscores replaced by spaces, so ‘a_number’ is displayed as ‘A number’.

Setting variables in the command line

Live variables can be set from the commandline using JSON syntax:

$ sbot --vars='{ "hue": 32 }' examples/vars/circle_circle.bot

This means that a Shoebot script can be called with different parameters without having to change the script itself – just specify the live variable initial values with --vars.

Socket server

If shoebot is run with the –serverport option, a socket server will also be started. Socket servers can accept connections from other applications or even across the network in order to set variables inside running Shoebot scripts.

$ sbot -s examples/animation/hypnoval.bot
Listening on port 7777...

Once it’s running, it’s easy to connect with telnet:

$ telnet 127.0.0.1 7777

This gets you into the shell, where you can use the commands below to list and set variables, rewind and go to frames.

The following commands can also be sent through other applications like Pure Data; there is a simple PD example in examples/socketcontrol/helloworld.pd. Be sure to take a look at the socket server examples in action inside the examples/socketserver directory.

Socketserver commands

Command

Description

goto 100

go to frame 100

pause

pause playback

rewind

set FRAME to 0

restart

set FRAME to 0 and reset all variables

vars

show content of all live variables

set n=1

set variable ‘n’ to value 1

n=1

set variable ‘n’ to value 1

help

show list of all commands

Using the live shell

Shoebot provides a live shell for communication with text editors, as well as livecoding.

All the socket server commands above are available, along with load_base64 which allows livecoding.

To experiment with the shell, run an example with the -l option:

$ sbot -l examples/animation/hypnoval.bot

The shell accepts all the socket server commands, along with a couple more; these can be useful for an editor or IDE.

Live Shell commands

Command

Description

quit

quit shoebot

load_base64

used by IDE/Editor to send new code to Shoebot

Troubleshooting

Here you’ll find help with some common problems. If your problem isn’t listed, feel free to file an issue including your error message and the output of python setup.py diagnose. You can also join us on the Shoebot Matrix channel.

Installation issues

Check progress with diagnose

If you’re having trouble with a specific package, Shoebot provides a ‘diagnose’ command to check if things are working: python3 setup.py diagnose

It’s usually easiest to start with Python3 and Pycairo, then move on to PyGobject, Pango and Gtk3.

Try PGI with CairoCFFI and GTK3 instead of PyGobject and Pycairo

Shoebot can run under PGI and CairoCFFI, which may be easier to install than the recommended setup with Pygobject and Pycairo.

In this setup Shoebot can work with the GUI, but text output is not available.

Other problems

TypeError: Couldn’t find foreign struct converter for ‘cairo.Context’

If you see this error, it means you’re missing the Python GObject interface for cairo. On Debian/Ubuntu, this should be fixed with:

sudo apt-get install python3-gi-cairo

See the installation page to know the relevant dependencies in other distros.

The Gedit plugin does not activate

Try running Gedit from the command line so that you can see debug messages. If you see one of these warnings:

** (gedit:3830): WARNING **: Could not load Gedit repository: Typelib file for namespace 'GtkSource', version '3.0' not found

or:

ImportError: cannot import name Gedit

then try installing the gir1.2-gtksource-3.0 package.

This StackOverflow answer helped on finding this solution.

Using with other tools

This section is aimed at Python dabblers and hackers who want to get into the more involved features of Shoebot.

Using Shoebot as a Python module

Shoebot can be easily loaded as a module inside a Python script.

import shoebot

# set up a canvas
bot = shoebot.create_bot(outputfile="output.svg")

# size() needs to be called first
bot.size(400,400)

# now we can draw!
bot.rect(10,10,100,100)

# finish() should be called after all drawing commands
bot.finish()

Take a snapshot of the current state:

bot.snapshot("snap.png")

Run a Shoebot/Nodebox script:

bot.run("example.bot")

Running in Jupyter

Jupyter notebooks <https://jupyter.org>_ are fantastic, and Shoebot runs pretty well inside them!

First, you need to have Jupyter installed, as well as the development version of Shoebot. Using virtualenvwrapper for this is heavily recommended.

# create the virtualenv
mkvirtualenv jupytershoebot -p $(which python3)
# install jupyter dependencies
pip3 install jupyter jupyter-pip
# clone the Shoebot repository, enter it and install
git clone https://github.com/shoebot/shoebot
cd shoebot
python3 setup.py install

After ensuring both packages are available, install the extension after cloning the jupyter-shoebot repository:

# leave the shoebot/ dir
cd ..
# clone the jupyter-shoebot repository, enter it and install
git clone https://github.com/shoebot/jupyter-shoebot
cd jupyter-shoebot
python3 setup.py install

And finally, while still on the jupyter-shoebot/ directory, run

jupyter kernelspec install shoebot_kernel --sys-prefix

All done! Now you can run jupyter notebook, go to the Kernel menu, select Change kernel and select Shoebot.

Be sure to try the notebook examples <https://github.com/shoebot/jupyter-shoebot/tree/master/example-notebooks>_ in the Jupyter Shoebot repository.

Running with PyPy

To get better performance, you can run Shoebot using PyPy3, which is experimental.

When installing Shoebot, you have to point to PyPy3 when creating your virtualenv. Instead of the first command in the Virtualenvwrapper install example, do:

mkvirtualenv shoebot -p $(which pypy3)

For the plain virtualenv approach, try:

virtualenv .env -p $(which pypy3)

Using with Django

See the shoebot-django for an example of integrating Shoebot into a Django application.

Extensions

There are a few plug-ins and extensions that make for a much better experience.

Atom extension

_images/atom.png

Atom is an excellent free software code editor, and there is a Shoebot package that turns it into the ideal tool to experiment with Shoebot.

Install it as you would any other Atom package: go to Preferences, Install and search for the “shoebot” package. Once installed, you get these extra commands:

  • Run (Ctrl-Alt-R): Run the open sketch on a window

  • Export (Ctrl-Alt-E): Run and save the output to a file (SVG, PNG or PDF)

  • Export video: Run and save to an MP4 file

Be sure to check the extension options to set the location of your Shoebot installation.

Gedit plugin

This Gedit plugin adds a menu to allow for running Shoebot scripts. It will open a Shoebot window using the current document as the Shoebot code.

Run the following commands to add the plugin to Gedit:

cd shoebot/extensions/gedit
python3 install.py

Now restart Gedit. Navigate to Edit > Preferences > Plugins and activate the Shoebotit plugin.

TODO: document plugin usage

Command-line options

This is a list of all the flags you can specify to the sbot command-line runner.

--outputfile <FILENAME>, -o <FILENAME>

Run in headless mode, outputting the result directly to a file.

Supported extensions are .png, .svg, .pdf and .ps.

--socketserver, -s

Run a socket server for controlling live variables from other programs.

--serverport <PORT>, -s <PORT>

Set the socket server port to listen for connections (default is 7777).

--vars <VARS>, -v <VARS>

Initial live variables, in JSON format.

Use single quotes outside, and double quotes inside, e.g.: --vars='{"variable1": 1}'

--namespace <NAMESPACE>, -ns <NAMESPACE>

Initial namespace, in JSON format.

Use single quotes outside, and double quotes inside, e.g.: --namespace='{"variable1": 1}'

TODO: Document the difference between –namespace and –vars

--l, -l

Open a shell to control the bot as it runs. See the Shell mode documentation for the available commands.

--repeat <TIMES>, -r <TIMES>

Set number of iterations to run the script, producing multiple images.

--window, -w

Run the script in a GTK window (default).

--fullscreen, -f

Run the script in fullscreen mode.

--title <TITLE>, -t <TITLE>

Set the window title.

--close, -c

Close the window after running the script. Use with --repeat for benchmarking.

--disable-vars, -dv

Disable the variables pane when in windowed mode.

--disable-background-thread, -dt

Don’t run code in a background thread. This option is only useful if running on OSX turns up issues.

--verbose, -V

Show internal error information in tracebacks.

--args <ARGS>, -a <ARGS>

Pass arguments to bot. [TODO: explain better]

API Reference

This is the technical descriptions of Shoebot’s objects and commands.

Path

class shoebot.data.BezierPath(bot, path=None, fill=None, fillrule=None, stroke=None, strokewidth=0, strokecap=None, strokejoin=None, strokedash=None, dashoffset=None, blendmode=None, packed_elements=None)

Represents a Bezier path as a list of PathElements.

Shoebot implementation of Nodebox’s BezierPath wrapper. While Nodebox relies on Cocoa/QT for its data structures, this is more of an “agnostic” implementation that won’t require any other back-ends to do some simple work with paths.

(this last sentence is not so correct: we use a bit of Cairo for getting path dimensions)

append(*args)
addpoint(*args)
copy()
moveto(x, y)
relmoveto(x, y)
lineto(x, y)
rellineto(x, y)
line(x1, y1, x2, y2)
curveto(x1, y1, x2, y2, x3, y3)
relcurveto(x1, y1, x2, y2, x3, y3)
arc(x, y, radius, angle1, angle2)
closepath()
ellipse(x, y, w, h, ellipsemode='corner')
rect(x, y, w, h, roundness=0.0, rectmode='corner')
contains(x, y)

Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.

property center

Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.

draw()
point(t, segments=None)

Returns the PathElement at time t (0.0-1.0) on the path.

Returns coordinates for point at t on the path. Gets the length of the path, based on the length of each curve and line in the path. Determines in what segment t falls. Gets the point on that segment. When you supply the list of segment lengths yourself, as returned from length(path, segmented=True), point() works about thirty times faster in a for-loop since it doesn’t need to recalculate the length during each iteration.

points(amount=100, start=0.0, end=1.0, segments=None)

Returns an iterator with a list of calculated points for the path. To omit the last point on closed paths: end=1-1.0/amount

extend(pathelements)
property bounds

Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.

property contours

Returns a list of contours in the path, as BezierPath objects. A contour is a sequence of lines and curves separated from the next contour by a MOVETO. For example, the glyph “o” has two contours: the inner circle and the outer circle.

property length

Returns the length of the path. Calculates the length of each spline in the path, using n as a number of points to measure. When segmented is True, returns a list containing the individual length of each spline as values between 0.0 and 1.0, defining the relative length of each spline in relation to the total path length.

property fill
inheritFromContext(ignore=())

Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.

property stroke
property strokewidth

PathElement

class shoebot.data.PathElement(cmd=None, *args)

Represents a single element in a Bezier path.

The first argument should be a command string, following the proper values according to which element we want.

Possible input:

(‘moveto’, x, y) (‘lineto’, x, y) (‘rlineto’, x, y) (‘curveto’, c1x, c1y, c2x, c2y, x, y) (‘rcurveto’, c1x, c1y, c2x, c2y, x, y) (‘arc’, x, y, radius, angle1, angle2) (‘ellipse’, x, y, w, h) (‘close’,)

Mind the trailing comma in the ‘close’ example, since it just needs an argument. The trailing comma is a way to tell python this really is supposed to be a tuple.

set_ctrl1(ctrl1)
get_ctrl1()
set_ctrl2(ctrl2)
get_ctrl2()
property ctrl1
property ctrl2

Image

class shoebot.data.Image(bot, path=None, x=0, y=0, width=None, height=None, alpha=1.0, data=None, **kwargs)
draw()
property fill
inheritFromContext(ignore=())

Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.

property stroke
property strokewidth
property center

Returns the center point of the path, disregarding transforms.

copy()

Color

class shoebot.data.Color(*args, **kwargs)

Represents a single color.

Attributes (RGB and HSL) are values between 0 and 1

This stores color values as a list of 4 floats (RGBA) in a 0-1 range.

The value can come in the following flavours: - v - (v) - (v,a) - (r,g,b) - (r,g,b,a) - #RRGGBB - RRGGBB - #RRGGBBAA - RRGGBBAA

property red
property green
property blue
property alpha
property data
copy()

Clipping

class shoebot.data.ClippingPath(bot, path=None, **kwargs)
addpoint(*args)
append(*args)
arc(x, y, radius, angle1, angle2)
property bounds

Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.

property center

Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.

closepath()
contains(x, y)

Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.

property contours

Returns a list of contours in the path, as BezierPath objects. A contour is a sequence of lines and curves separated from the next contour by a MOVETO. For example, the glyph “o” has two contours: the inner circle and the outer circle.

copy()
curveto(x1, y1, x2, y2, x3, y3)
draw()
ellipse(x, y, w, h, ellipsemode='corner')
extend(pathelements)
property fill
inheritFromContext(ignore=())

Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.

property length

Returns the length of the path. Calculates the length of each spline in the path, using n as a number of points to measure. When segmented is True, returns a list containing the individual length of each spline as values between 0.0 and 1.0, defining the relative length of each spline in relation to the total path length.

line(x1, y1, x2, y2)
lineto(x, y)
moveto(x, y)
point(t, segments=None)

Returns the PathElement at time t (0.0-1.0) on the path.

Returns coordinates for point at t on the path. Gets the length of the path, based on the length of each curve and line in the path. Determines in what segment t falls. Gets the point on that segment. When you supply the list of segment lengths yourself, as returned from length(path, segmented=True), point() works about thirty times faster in a for-loop since it doesn’t need to recalculate the length during each iteration.

points(amount=100, start=0.0, end=1.0, segments=None)

Returns an iterator with a list of calculated points for the path. To omit the last point on closed paths: end=1-1.0/amount

rect(x, y, w, h, roundness=0.0, rectmode='corner')
relcurveto(x1, y1, x2, y2, x3, y3)
rellineto(x, y)
relmoveto(x, y)
property stroke
property strokewidth
class shoebot.data.EndClip(bot, **kwargs)
draw()
inheritFromContext(ignore=())

Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.

Text

class shoebot.data.Text(bot, text, x=0, y=0, width=None, height=None, outline=False, ctx=None, draw=True, **kwargs)
Changes from Nodebox 1:

font in Nodebox is a native Cocoa font, here it is the font name. _fontsize, _fontsize, _lineheight, _align in Nodebox are public fields.

Implementation of fonts uses Pango instead of Cocoa.

property baseline
property metrics
property bounds
Returns

TextBounds namedtuple containing bounds as (x, y, width, height)

property path
property center

Returns the center point of the path, disregarding transforms.

copy()
property fill
inheritFromContext(ignore=())

Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.

property stroke
property strokewidth

Point

class shoebot.data.Point(*args)

Taken from Nodebox and modified

Bot

class shoebot.grammar.NodeBot(canvas=None, namespace=None, vars=None)
color_mode = 'rgb'
color_range = 1
image(path, x, y, width=None, height=None, alpha=1.0, data=None, draw=True, **kwargs)

Draws a image with (x,y) as the top left corner.

If width and height are specified, resize the image to fit.

form path, in x,y and resize it to width, height dimensions.

Parameters
  • path (filename) – location of the image on disk

  • x (float) – x-coordinate of the top left corner

  • y (float) – y-coordinate of the top left corner

  • width (float or None) – image width (leave blank to use its original width)

  • height (float or None) – image height (leave blank to use its original height)

  • alpha (float) – opacity

  • data (binary data) – image data to load. Use this instead of path if you want to load an image from memory or have another source (e.g. using the web library)

  • draw (bool) – whether to place the image immediately on the canvas or not

imagesize(path)
Parameters

path – Path to image file.

Returns

image size as a (width, height) tuple

rect(x, y, width, height, roundness=0.0, draw=True, **kwargs)

Draw a rectangle.

Parameters
  • x – top left x-coordinate

  • y – top left y-coordinate

  • width – rectangle width

  • height – rectangle height

  • roundness – rounded corner radius

  • draw (boolean) – whether to draw the shape on the canvas or not

  • fill – fill color

Returns

BezierPath representing the rectangle

rectmode(mode=None)

Get or set the current rectmode.

Parameters

mode (CORNER, CENTER or CORNERS) – the mode to draw new rectangles in

Returns

current rectmode value

ellipse(x, y, width, height, draw=True, **kwargs)

Draw an ellipse.

Parameters
  • x – top left x-coordinate

  • y – top left y-coordinate

  • width – ellipse width

  • height – ellipse height

  • draw (boolean) – whether to draw the shape on the canvas or not

Returns

BezierPath representing the ellipse

oval(x, y, width, height, draw=True, **kwargs)

Draw an ellipse.

Parameters
  • x – top left x-coordinate

  • y – top left y-coordinate

  • width – ellipse width

  • height – ellipse height

  • draw (boolean) – whether to draw the shape on the canvas or not

Returns

BezierPath representing the ellipse

circle(x, y, diameter, draw=True, **kwargs)

Draw a circle :param x: x-coordinate of the top left corner :param y: y-coordinate of the top left corner :param diameter: circle diameter :param boolean draw: whether to draw the shape on the canvas or not :return: BezierPath representing the circle

ellipsemode(mode=None)

Set the current ellipse drawing mode.

Parameters

mode – CORNER, CENTER, CORNERS

Returns

ellipsemode if mode is None or valid.

line(x1, y1, x2, y2, draw=True, **kwargs)

Draw a line from (x1,y1) to (x2,y2).

Parameters
  • x1 – x-coordinate of the first point

  • y1 – y-coordinate of the first point

  • x2 – x-coordinate of the second point

  • y2 – y-coordinate of the second point

  • draw (boolean) – whether to draw the shape on the canvas or not

Returns

BezierPath representing the line

arc(x, y, radius, angle1, angle2, type='chord', draw=True, **kwargs)

Draw an arc with center (x,y) between two angles in degrees.

Parameters
  • x1 – start x-coordinate

  • y1 – start y-coordinate

  • radius – arc radius

  • angle1 – start angle

  • angle2 – end angle

arrow(x, y, width, type='1', draw=True, **kwargs)

Draw an arrow.

Parameters
  • x – arrow tip x-coordinate

  • y – arrow tip y-coordinate

  • width – arrow width (also sets height)

  • type (NORMAL or FORTYFIVE) – arrow type

  • draw (boolean) – whether to draw the shape on the canvas or not

Returns

BezierPath object representing the arrow

star(x, y, points=20, outer=100, inner=50, draw=True, **kwargs)

Draws a star.

Parameters
  • x – center x-coordinate

  • y – center y-coordinate

  • points – amount of points

  • outer – outer radius

  • inner – inner radius

  • draw (boolean) – whether to draw the shape on the canvas or not

beginpath(x=None, y=None, **kwargs)

Start a new Bézier path.

This command is needed before any other path drawing commands.

Parameters
  • x (float or None) – x-coordinate of the starting point

  • y (float or None) – y-coordinate of the starting point

moveto(x, y)

Move the Bézier “pen” to the specified point without drawing.

Parameters
  • x (float) – x-coordinate of the point to move to

  • y (float) – y-coordinate of the point to move to

lineto(x, y)

Draw a line from the pen’s current point.

Parameters
  • x (float) – x-coordinate of the point to draw to

  • y (float) – y-coordinate of the point to draw to

curveto(x1, y1, x2, y2, x3, y3)
arcto(x, y, radius, angle1, angle2)
closepath()
endpath(draw=True, closed=None)
drawpath(path, **kwargs)
drawimage(image, x=None, y=None)
Parameters
  • image – Image to draw

  • x – optional, x coordinate (default is image.x)

  • y – optional, y coordinate (default is image.y)

Returns

autoclosepath(close=True)
relmoveto(x, y)

Move relatively to the last point.

rellineto(x, y)

Draw a line using relative coordinates.

relcurveto(h1x, h1y, h2x, h2y, x, y)

Draws a curve relatively to the last point.

findpath(points, curvature=1.0)

Constructs a path between the given list of points.

Interpolates the list of points and determines a smooth bezier path betweem them.

The curvature parameter offers some control on how separate segments are stitched together: from straight angles to smooth curves. Curvature is only useful if the path has more than three points.

beginclip(path)

Use a path as a clipping mask.

All drawing commands between beginclip() and endclip() will be drawn inside the clipping mask set by beginclip().

Parameters

path (BezierPath) – the path to be used as a clipping mask

endclip()

Finish a clipping mask and render the result.

transform(mode=None)

Set the current transform mode.

Parameters

mode (CORNER or CENTER) – the mode to base new transformations on

translate(xt, yt)

Translate the canvas origin point by (xt, yt).

Parameters
  • xt – Amount to move horizontally

  • yt – Amount to move vertically

rotate(degrees=0, radians=0)

Set the current rotation in degrees or radians.

Parameters
  • degrees – Degrees to rotate

  • radians – Radians to rotate

scale(x=1, y=None)

Set a scale at which to draw objects.

1.0 draws objects at their natural size.

Parameters
  • x – Scale on the horizontal plane

  • y – Scale on the vertical plane

skew(x=1, y=0)
push()
pop()
reset()
outputmode()

NOT IMPLEMENTED

colormode(mode=None, range=None)

Set the current colormode (can be RGB or HSB) and eventually the color range.

If called without arguments, it returns the current colormode.

Parameters
  • mode (RGB or HSB) – Color mode to use

  • crange (float) – Maximum value for the new color range to use

Returns

Current color mode

colorrange(crange=None)

Sets the current color range.

The default is 0-1; for a range of 0-255, use colorrange(256).

Parameters

crange (float) – maximum value for new color range

Returns

current range value

fill(*args)

Sets a fill color, applying it to new paths.

Parameters

args – color in supported format

nofill()

Stop applying fills to new paths.

Returns

fill color before nofill() was called

fillrule(r=None)

Set the fill rule to use in new paths.

Parameters

r (WINDING or EVENODD) – fill rule to apply

Returns

current fill rule value

stroke(*args)

Set a stroke color, applying it to new paths.

Parameters

args – color in supported format

Returns

new stroke color

nostroke()

Stop applying strokes to new paths.

Returns

stroke color before nostroke() was called

strokewidth(w=None)

Set the stroke width to be used by stroke().

Parameters

w – width of the stroke to use

Returns

current stroke width value

strokedash(dashes=None, offset=0)

Sets the dash pattern to be used by stroke().

Parameters
  • dashes (list) – a sequence specifying alternate lengths of on and off stroke portions

  • offset (float) – an offset into the dash pattern at which the stroke should start

Returns

tuple with dashes value and offset

strokecap(cap=None)

Set the stroke cap.

Parameters

w – new stroke cap value

Returns

current stroke cap value

strokejoin(join=None)

Set the stroke join.

Parameters

w – new stroke join value

Returns

current line join value

background(*args)

Set the canvas background color.

Parameters

color – background color to apply

Returns

new background color

blendmode(mode=None)

Set the current blending mode.

Parameters

mode – mode name (e.g. “multiply”)

font(fontpath=None, fontsize=None, vars=None, *args, **kwargs)

Set the font to be used with new text instances.

Parameters
  • fontpath – font name (can include styles like “Bold”)

  • fontsize – font size

  • vars – font variant values, as a dict of axis/value pairs (variable fonts only)

  • var_XXXX – set variant value (variable fonts only)

Returns

current fontpath (if fontpath param not set)

Accepts TrueType and OpenType files. Depends on FreeType being installed.

fontsize(fontsize=None)

Sets and/or returns the current font size.

Parameters

fontsize – Font size in pt

Returns

the current font size value

text(txt, x, y, width=None, height=1000000, outline=False, draw=True, **kwargs)

Draws a string of text according to the current font settings.

Parameters
  • txt – Text to output

  • x – x-coordinate of the top left corner

  • y – y-coordinate of the top left corner

  • width – text width

  • height – text height

  • outline – If True, draws a path instead of a text object (defaults to False)

  • draw – Set to False to inhibit immediate drawing (defaults to True)

Returns

Path object representing the text.

textpath(txt, x, y, width=None, height=1000000, draw=False, **kwargs)

Generates an outlined path of the input text.

Parameters
  • txt – Text to output

  • x – x-coordinate of the top left corner

  • y – y-coordinate of the top left corner

  • width – text width

  • height – text height

  • draw – Set to False to inhibit immediate drawing (defaults to False)

Returns

BezierPath representing the text

textmetrics(txt, width=None, height=None, **kwargs)

Returns the dimensions of the text box of a string of text, according to the current font settings.

Returns

(width, height) tuple

textbounds(txt, width=None, height=None, **kwargs)

Returns the dimensions of the actual shapes (inked part) of a string of text, according to the current font settings.

Returns

(width, height) tuple

textwidth(txt, width=None, **kwargs)

Returns the width of a string of text according to the current font settings.

Returns

textheight(txt, width=None, **kwargs)

Returns the height of a string of text according to the current font settings.

Parameters
  • txt – string to measure

  • width – width of a line of text in a block

lineheight(height=None)

Set text lineheight.

Parameters

height – line height.

align(align='left')

Set text alignment

Parameters

align – Text alignment (LEFT, CENTER, RIGHT)

fontoptions(hintstyle=None, hintmetrics=None, subpixelorder=None, antialias=None)

Set font rendering options.

Parameters
  • hintstyle – Hinting style (NONE, SLIGHT, MEDIUM, FULL)

  • hintmetrics – Quantize font metrics (ON, OFF)

  • antialias – Antialiasing type (NONE, GRAY, SUBPIXEL, FAST, GOOD, BEST)

  • subpixelorder – Order of pixels when antialiasing in SUBPIXEL mode (RGB, BGR, VRGB, VBGR)

fontnames()
autotext(sourceFile)
property canvas

Not entirely sure compatible the Shoebot ‘canvas’ is with Nodebox but there you go. :return:

angle(x0, y0, x1, y1)
distance(x0, y0, x1, y1)
coordinates(x0, y0, distance, angle)
property FRAME
MOUSEX = -1

The x-value of the mouse cursor coordinates.

MOUSEY = -1

The y-value of the mouse cursor coordinates.

choice(seq)

Choose a random element from a non-empty sequence.

cm = 28.3465
color(*args)
Parameters

args – color in a supported format.

Returns

Color object containing the color.

files(path='*')

Returns a list of files.

Use wildcards to specify which files to pick, e.g. f = files('*.gif').

Parameters

path – wildcard to use in file list

Returns

list of file names

finish()
grid(cols, rows, colSize=1, rowSize=1, shuffled=False)

Returns an iterator that contains coordinate tuples.

This command can be used to quickly create grid-like structures. A common usage pattern is:

for x, y in grid(10,10,12,12):
    rect(x,y, 10,10)
inch = 72
key = '-'
keycode = 0
keydown = False
mm = 2.8346
mousedown = False

Is True if the mouse button is pressed.

random(v1=None, v2=None)
run(inputcode, max_iterations=None, run_forever=False, frame_limiter=False, verbose=False)
show(format='png', as_data=False)

Returns an Image object of the current surface. Used for displaying output in Jupyter notebooks. Adapted from the cairo-jupyter project.

size(w=None, h=None)

Set the canvas size

Only the first call will actually be effective.

Parameters
  • w – Width

  • h – height

snapshot(target=None, defer=None, autonumber=False)

Save the contents of current surface into a file or cairo surface/context.

Parameters
  • filename – Can be a filename or a Cairo surface.

  • defer – When to snapshot, if set to True waits until the frame has finished rendering.

  • autonumber – If true then a number will be appended to the filename.

speed(framerate=None)

Set animation framerate.

Parameters

framerate – Frames per second to run bot.

Returns

Current framerate of animation.

var(name, type, default=None, min=0, max=255, value=None, step=None)
ximport(libName)

Import Nodebox libraries.

The libraries get _ctx, which provides them with the nodebox API.

Parameters

libName – Library name to import

Nodebox compatibility

Shoebot was originally developed as a rewrite of Nodebox, attempting to follow its behaviour as close as possible. However, the developers eventually wanted some functionality that was not in Nodebox, and there are many aspects of Nodebox that, for one reason or other, were not ported over.

Now that the original Nodebox isn’t being developed further, we have decided to go on and keep implementing original features whenever appropriate.

In this page, you’ll find the features and behavior that differs between Nodebox and Shoebot.

If you find any difference that isn’t documented here, please file an issue.

Additional Shoebot features

These are features that were created in Shoebot and are not available in Nodebox, or where Shoebot behavior is distinct.

New functions
  • Drawing

    • rectmode() and ellipsemode() are inspired by Processing and cater to the preferences of users who expect alternative ways to draw primitives. These are also useful for specific situations where coordinate calculation would be otherwise necessary.

    • arc() is provided by Cairo, so we support it as well.

  • Others

    • snapshot() is a simple way to output to an image in the middle of the script execution.

    • run() can execute another script in the current context. This is mostly useful when using Shoebot as a Python module inside another application.

Bundled libraries

Nodebox provides a set of external libraries that can be downloaded and added to a project. Shoebot comes with ported versions of those libraries already included and available.

New libraries

Additional external libraries were developed for Shoebot:

Unsupported Nodebox features

These are the Nodebox bits that aren’t available in Shoebot.

CMYK color

Nodebox was implemented using Mac OS X’s Cocoa toolkit, which supports CMYK color. Shoebot runs on the Cairo graphics backend, which does not. Nodebox commands that deal with CMYK color are therefore unsupported:

  • outputmode() is not available.

  • colormode() is implemented, but CMYK is not an accepted argument – only RGB or HSB.

Path operations

Operations between bézier paths are not supported yet. These are:

  • path.union()

  • path.intersect()

  • path.difference()

Fitting a path using path.fit() isn’t supported either.

Animation

While animations work well in a window, Shoebot does not support exporting them to GIF or video formats.

Unported libraries

Contributing

Non-development tasks

Help improve our documentation

We’re missing a few details and we’d definitely welcome some help here!

Port Nodebox library pages

Most of the pages in the libraries section are stubs. While the original Nodebox documentation is good enough reference, it’s becoming important to port those docs over to the Shoebot manual because most of the images are missing on the Nodebox site. (The PlotDevice manual thankfully keeps its own adaptation.)

It’s not hard to adapt the Nodebox docs to the Shoebot manual: docs are written in the ReStructured Text format (a kind of souped-up Markdown), and you can take a look at the source of the colors library page for an example of what Sphinx files look like. This cheat sheet explains the basics. From there, you can help complete the stub pages, which would be just excellent.

Report errors or missing parts

Pointing out parts that are missing or wrong is a great way to help. If you spot something, file an issue with the documentation label on the issue tracker to help us on this, or just pop over on the Matrix channel.

Development tasks

Make new examples or port existing ones

We’re always eager to welcome new examples that explain a concept or show off an interesting technique.

You can either contribute your own examples, or help port existing scripts:

They should work mostly without modifications – we need help testing them. Try them out and post any issues you find on our issue tracker in case you hit a wall.

Be sure to also check the brief guidelines in Coding style for examples so that your efforts can be included in Shoebot.

Help port libraries

We’re missing a few Nodebox libraries; can you help us port them to Shoebot?

See the full list of Unported libraries.

Incidentally, we’re also missing documentation to explain how to port Nodebox libraries. If you’re interested but stuck, file an issue and we’ll help you.

Add support for another operating system

We support a few operating systems, but we’d like to expand the list.

To add support for another OS, you just need to find out its package names for the libraries that Shoebot depends on:

  • Python 3

  • Pycairo

  • PyGObject3

  • Pango

  • Pillow

  • GTK3 (for the windowed canvas)

  • GTKSourceView3 (for the IDE)

The community for your operating system may be able to offer help here. The install_dependencies.sh script might help too.

Look for ‘Help Out’ issues

The issues tagged ‘Help Out’ don’t need a deep knowledge of Shoebot internals, and there’s a good variety of tasks to be done.

Make text editor plugins

While our simple editor is around, power-users will be using their favourite text editor to hack on Shoebot scripts. Having plugins for any popular text editor would be a fantastic addition.

Integrate Shoebot with other software

Shoebot can be a great tool to complement other software, be it for

  • SVG, PDF or bitmap generation

  • simple visualizations

  • interact in real-time with the socket server

If you see a use case where Shoebot could be helpful, we’ll do our best to support you in implementing it.

Tips for Developers

Coding style for the Shoebot core code

We’re not picky here, other than following PEP8 style guidelines. We use the Black syntax checker in our code editors to keep us strict, and recommend it.

Coding style for examples

When creating examples for including in Shoebot, we try to adhere to a set of writing guidelines to make it easy for newcomers to understand what’s going on.

  • Avoid one-letter variables (other than x and y), and avoid two-letter names as well (things like dx can be expanded to deltax). It will look less compact, but really helps understanding what’s going on.

  • Start the example with a docstring specifying the title of the example, author info and some details about the script and its workings. If you want to format this text, use Markdown.

  • Use Flake8 or similar linter plugin to find necessary style fixes.

  • Comments in English.

  • Variables and functions are in lowercase and underscored_lowercase, class names are in CamelCase.

Making a release

This is our checklist to be sure we don’t miss any detail when we put out a release.

  • update the version number in these files:

    • Makefile

    • VERSION

    • setup.py

    • doc/source/conf.py

    • shoebot/ide/ide.py

  • update the changelogs

    • CHANGELOG

    • debian/changelog

  • tag the release commit

  • publish release on GitHub

  • push to PyPI

    • register on PyPI and place your credentials in ~/.pypirc

    • install Twine

    • make a source build with python setup.py sdist

    • make a test upload to TestPyPI with twine upload --repository-url https://test.pypi.org/legacy/ dist/shoebot-1.3.tar.gz

    • if all is good, upload to PyPI with twine upload dist/shoebot-1.3.tar.gz

    • be sure to change the version numbers in the previous commands according to the current Shoebot version

Building Debian packages

There are some dependencies to look out for:

sudo apt-get install rename dh-python cdbs

Be sure to go through this checklist:

  • update the debian/changelog file

Then, generate the Debian packages with the make builddeb command.