Shapes

By Martin McBride, 2022-06-05
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 is also a line decorator that can be used to annotate lines:

  • Arrowhead - this is likely to be deprecated soon in favour of a general line end feature.

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 constructors

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 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(pattern=Color(0), fill_rule=WINDING)
Parameter Type Description
pattern Color or Pattern The fill pattern/colour.
fill_rule enum The fill rule.

pattern specifies the colour or pattern that will be used to fill the shape. It can either be:

  • A Color object to specify a flat colour fill.
  • A Pattern object to specify a special fill (for example a LinearGradient).

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(pattern=Color(0), line_width=1, dash=[], cap=SQUARE, join=MITER, miter_limit=None)
Parameter Type Description
pattern Color or Pattern The stroke pattern/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.

pattern specifies the colour or pattern that will be used to stroke the shape. It can either be:

  • A Color object to specify a flat colour fill.
  • A Pattern object to specify a special fill (for example a LinearGradient).

It defaults to black.

line_width controls the width of the line. The line width is in user units, and default to 1.

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 of 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 - DEPRECATED

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

This function has been deprecated. To fill and stroke a shape, you should call fill and then stroke. These functions give full access to all the fill and stroke parameters. It is also possible to call stroke then fill to achieve a different effect.

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.

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 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.

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 create 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.

See also

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

Join the PythonInformer Newsletter

Sign up using this form to receive an email when new content is added:

Popular tags

2d arrays abstract data type alignment and angle animation arc array arrays bar chart bar style behavioural pattern bezier curve built-in function callable object chain circle classes clipping close closure cmyk colour combinations comparison operator comprehension context context manager conversion count creational pattern data science data types decorator design pattern device space dictionary drawing duck typing efficiency ellipse else encryption enumerate fill filter font font style for loop formula function function composition function plot functools game development generativepy tutorial generator geometry gif global variable gradient greyscale higher order function hsl html image image processing imagesurface immutable object in operator index inner function input installing iter iterable iterator itertools join l system lambda function latex len lerp line line plot line style linear gradient linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map marker style matplotlib monad mutability named parameter numeric python numpy object open operator optimisation optional parameter or pandas partial application path pattern permutations pie chart pil pillow polygon pong positional parameter print product programming paradigms programming techniques pure function python standard library radial gradient range recipes rectangle recursion reduce regular polygon repeat rgb rotation roundrect scaling scatter plot scipy sector segment sequence setup shape singleton slice slicing sound spirograph sprite square str stream string stroke structural pattern subpath symmetric encryption template tex text text metrics tinkerbell fractal transform translation transparency triangle truthy value tuple turtle unpacking user space vectorisation webserver website while loop zip zip_longest