Shapes


Martin McBride, 2020-08-18
Tags geometry shape
Categories generativepy generative art

The geometry module provides classes for drawing shapes. While you can draw any shape you wish using the Pycairo API, the geometry classes make it easier and more readable.

For each shape there is typically:

  • A utility function. This simply creates the shape and adds it to the path.
  • A shape class. This allows you to create a shape with more control. In most cases there will be different ways to create the shape, and you can choose the most convenient option. Once the shape is created, you can fill it, stroke it, fill and stroke it, or just add it to the current path.

Shape classes use fluent notation to make the code readable.

All shapes inherit from the Shape class described here. The available shapes are all defined in the geometry module, but they are documented in separate pages:

There are also some line decorators that can be used to annotate lines:

Example - rectangle

To create a rectangle using the utility function you can do this (assuming we have a valid ctx, for example inside a draw function:

rectangle(ctx, 1, 2, 4, 3)
ctx.set_source_rgba(*Color(1, 0, 0))
ctx.fill()

The rectangle function adds a rectangle to the path with its corner at (1, 2), and with width 4, height 3.

We then set the colour to red and fill the rectangle.

We can do a similar thing with a Rectangle object like this:

Rectangle(ctx).of_corner_size(1, 2, 4, 3).stroke(Color(0, .5, 0), 0.1)

This time we define the same rectangle, but instead of filling it we stroke it with a green line of thickness 0.1 units.

Class contructors

All classes have the same constructor. The constructor, of course, takes the name of the shape.

Shape(ctx)
Rectangle(ctx)  
# etc...
Parameter Type Description
ctx Context The Pycairo Context to draw to

Creates a shape object with ctx as its target drawing context.

Shape class

The Shape class is an abstract base class (you cannot create a Shape object). It adds several methods that are available to all the concrete shape classes:

  • add adds the shape to the context.
  • fill fills the shape.
  • stroke strokes the shape.
  • fill_stroke fills and strokes the shape.
  • path creates a path object from the shape, that can be used with the Path object.
  • clip adds the current shape to the clip path.
  • as_sub_path add a shape to an existing shape to create a complex shape.
  • extend_path adds new lines lines and curves to an existing shape to create a complex shape.

add

Adds the shape to the context but does not draw it. The shape is added as a new path.

add()

No parameters.

fill

Adds the shape to the context and fills it.

fill(color=Color(0), fill_rule=EVEN_ODD)
Parameter Type Description
color generativepy.Color The fill colour.
fill_rule enum The fill rule.

color specfies the colour that will be used to fill the shape. it defaults to black.

fill_rule only applies to complex paths such as self intersecting paths. It controls which parts of the path will be filled, and which will be left as "holes". Possible values are drawing.EVEN_ODD and drawing.WINDING.

stroke

Adds the shape to the context and strokes it.

stroke(color=Color(0), line_width=1, dash=[], cap=SQUARE, join=MITER, miter_limit=None)
Parameter Type Description
color generativepy.Color The stroke colour.
line_width number The line width.
dash array of numbers The dash style.
cap enum The type of line cap.
join enum The type of line join.
miter_limit number The mitre limit.

Draws a line with the supplied color and line_width. The default is a black line of width 1. The line width is in user units.

dash creates dashed lines, specified by an array of numbers. For example:

  • [5] creates a dash pattern where the dashes are 5 units long, separated by gaps that are 5 units long.
  • [3, 4] creates a dash pattern where the dashes are 3 units long, separated by gaps that are 4 units long.

cap controls the style of the line ends:

  • drawing.ROUND creates rounded line ends.
  • drawing.SQUARE creates square line ends that extend slightly beyond the line start and end points.
  • drawing.BUTT creates square line ends that end exactly on the line start and end points.

join controls the style of the corners (where two line sections meet):

  • drawing.MITRE creates pointed corners.
  • drawing.ROUND creates rounded corners.
  • drawing.BEVEL is similar to MITRE but the sharp point the corner is cut off.

miter_limit is used in conjunction with the MITRE join style. For joins at small angles, the mitre can become very long. miter_limit automatically switches to BEVEL mode at low angles. miter_limit is enabled by default at an angle of about 11 degrees, which is suitable for most applications.

fill_stroke

Adds the shape to the context and fills then strokes it.

fill_stroke(fill_color, stroke_colour, line_width=1)
Parameter Type Description
fill_color generativepy.Color The fill colour.
stroke_color generativepy.Color The stroke colour.
line_width number The line width.

For fill_stroke, the two colour parameters fill_color and stroke_color are not optional. You must supply both colours, which means that the shape will be filled and stroked with flat colours.

To fill and stroke a shape with alternative sources (such as patterns or gradients) you should add the shape, then stroke and fill it with native Context calls.

path

Creates a path object from the current context.

path() # Returns a path

path is called in a similar way to fill, but instead of filling the current shape, it takes a snapshot of the shape and returns it as a Pycairo path object.

You can save this object and pass it into a Path object later. When you fill or stroke the Path, it will recreate the shape. This can be useful if you need to use the same shape more than once, or if you want to pass a shape into another function as a parameter.

You can also iterate over the path using Pycairo functions to do fancy things like placing text along a curve. Refer to the Pycairo documentation for more information.

You should generally treat the returned Pycairo path as an opaque object - that is to say, you can pass it around but you shouldn't generally try to modify it or use its internal data.

Note that path returns a flattened path. That is a path where all the curves have been converted to straight line segments. The path will be reproduced perfectly at the same scale, but if you store a path and then redraw it using a large scale factor you might see some distortion of the curve.

For more details see the path tutorial.

clip

Creates a clip region from the current context.

clip()

clip is called in a similar way to fill, but instead of filling the current shape, it establishes a clipping region using the shape.

When the clipping region is in force, anything else you draw will be clipped to that region. Anything outside that region will be protected from any drawing operations.

If you apply more than a clipping region A, and then apply another clipping region B, the result will be that the intersection of the two regions.

If you want to apply a clipping region temporarily, and remove it later, the best way is to use the context save and restore method, see useful context methods.

For more details on clipping, see the clipping tutorial.

as_sub_path

Adds a new shape to the context, without removing any existing shapes. This can be used to create complex shapes.

as_sub_path()

No parameters.

This method allows you to create complex paths, consisting of two or more separate shapes. This allows you to do things such as creating shapes with holes, or creating complex clip paths.

For more details see the complex paths tutorial.

extend_path

Extends an existing path with extra lines or curves.

extend_path(close=False)
Parameter Type Description
close bool True to close the path after adding this section

This function is used to join several open shapes, to form a more complex shape. It can be used to join any combination of the following shapes:

  • Line.
  • Bezier.
  • Polygon, but the shape should be left open (using the open method).
  • Circle, but only in arc mode (using the as_arc method).

When the final shape is added, you can optionally set the close flag to created a closed shape. You should not add any further sections after this final call. Alternatively, if the close flag is not set it will create an open shape.

For more details see the composite paths tutorial.

If you found this article useful, you might be interested in the book Computer Graphics in Python or other books by the same author.

Prev

Popular tags

2d arrays abstract data type alignment and animation arc array arrays bezier curve built-in function callable object circle classes close closure cmyk colour comparison operator comprehension context context manager conversion creational pattern data types design pattern device space dictionary drawing duck typing efficiency else encryption enumerate fill filter font font style for loop function function composition function plot functools game development generativepy tutorial generator geometry gif gradient greyscale higher order function hsl html image image processing imagesurface immutable object index inner function input installing iter iterable iterator itertools l system lambda function len line linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map monad mutability named parameter numeric python numpy object open operator optional parameter or partial application path polygon positional parameter print pure function pycairo radial gradient range recipes rectangle recursion reduce rgb rotation scaling sector segment sequence singleton slice slicing sound spirograph sprite square str stream string stroke subpath symmetric encryption template text text metrics tinkerbell fractal transform translation transparency tuple turtle unpacking user space vectorisation webserver website while loop zip