Path objects in generativepy
Martin McBride, 2022-03-07
Tags generativepy tutorial path
Categories generativepy generativepy tutorial

This tutorial covers the basics of using Path objects in generativepy.
You can think of a Path
as being an abstract shape:
- You can create a
Path
object in the same way as you would draw a shape, but creating thePath
doesn't draw anything. - You can then draw the path as many times as you want, using
fill
and/orstroke
. - You can also create a clipping region using the path.
You can draw the same path in different ways (eg different fill or stroke styles), in different positions, or even scaled and rotated.
Path example code
Here is a sample Python program for creating various filled and outlined rectangles. The code is explained later in this article:
from generativepy.drawing import make_image, setup from generativepy.color import Color from generativepy.geometry import Polygon, Text, Path, Transform ''' Demonstrate paths in the geometry module. ''' def draw(ctx, width, height, frame_no, frame_count): setup(ctx, width, height, width=5, background=Color(0.8)) # Get a polygon path object path1 = Polygon(ctx).of_points([(0, 0), (1, 1), (0.5, 2), (0.5, 1)])\ .path() # Get a text path object path2 = Text(ctx).of("Path text", (0, 0)).font("Times").size(0.2)\ .align_left().align_top().path() # Apply the polygon in various places with Transform(ctx).translate(0.5, 1): Path(ctx).of(path1).stroke(Color('darkgreen'), 0.1) with Transform(ctx).translate(1, 2.5): Path(ctx).of(path1).fill(Color('blue')) with Transform(ctx).translate(2.5, 0.5).scale(2, 2): Path(ctx).of(path1).fill(Color('orange')).stroke(Color('black'), 0.05) # Apply the text in various places with Transform(ctx).translate(0, 0): Path(ctx).of(path2).fill(Color('black')) with Transform(ctx).translate(2, 3): Path(ctx).of(path2).stroke(Color('red'), 0.01) with Transform(ctx).translate(2, 4).scale(2, 2): Path(ctx).of(path2).fill(Color('yellow')).stroke(Color('black'), 0.01) make_image("path.png", draw, 500, 500)
This code is available on github in tutorial/shapes/path.py.
Here is the resulting image:
Creating the paths
The first part of the code creates a couple of paths. Here is the first:
path1 = Polygon(ctx).of_points([(0, 0), (1, 1), (0.5, 2), (0.5, 1)])\ .path()
In this case, we have created a Polygon
in the usual way, but instead of filling or stroking the shape, we call path
. This returns a Pycario path object. You can think of the path object as being like a set of instructions for drawing the shape.
This code doesn't draw anything, it just saves the drawing instructions in path1
.
Here is the second path:
path2 = Text(ctx).of("Path text", (0, 0)).font("Times").size(0.2)\ .align_left().align_top().path()
This time we have created a complex path - some text. But the result is the same, we don't draw anything but the instructions for drawing the text are stored in path2
.
Drawing path1
Here is the code that actually draws path1
with Transform(ctx).translate(0.5, 1): Path(ctx).of(path1).stroke(Color('darkgreen'), 0.1) with Transform(ctx).translate(1, 2.5): Path(ctx).of(path1).fill(Color('blue')) with Transform(ctx).translate(2.5, 0.5).scale(2, 2): Path(ctx).of(path1).fill(Color('orange')).stroke(Color('black'), 0.05)
In the first line, we use a Transform object to moves user space by (0.5, 1)
.
We then use a Path
object. This takes the drawing instructions in path1
and adds them to the context ready to be drawn. We then stroke the shape in dark green. If you look at the definition of the polygon that is used to define path1
, you will see that the top left corner of the polygon is at (0, 0)
, but because we have translated user space, the polygon actually gets drawn at (0.5, 1)
.
The second block draws the same shape again, this time at (1, 2.5)
, and it fills it rather than outlining it.
The third block draws the shape yet again, this time at (2.5, 0.5)
. In this case, we also scale userspace by (2, 2)
, which scales user space in the x and y directions. We fill and outline the shape, and because of the scaling, the shape appears twice as big.
Drawing path2
Here is the code that draws path2
with Transform(ctx).translate(0, 0): Path(ctx).of(path2).fill(Color('black')) with Transform(ctx).translate(2, 3): Path(ctx).of(path2).stroke(Color('red'), 0.01) with Transform(ctx).translate(2, 4).scale(2, 2): Path(ctx).of(path2).fill(Color('yellow')).stroke(Color('black'), 0.01)
This illustrates the use of complex paths. We draw the same text path filled, stroked, and also scaled, filled and stroked.