# How to Paint Like Piet Mondrian with Python

RedXIII | April 14, 2021, 11:11 a.m.

# Programming like an artist.

Piet Mondrian was one of the most influential artists of the 20th century. The Dutch painter, known for his strikingly abstract art, was the founder of the De Stijl movement, a style that favored economy and symbolism.

De Stijl and Mondrian gained international recognition through the German Bauhaus, a school of art that became famous for its principles of design.

Paintings like Broadway Boogie Woogie and Composition C are prime examples of Mondrian’s style. He often used a minimal color palette, and relied on the concept of the grid.

Mondrian was a devoted artist. He’d spend hours in his studio meticulously painting until he developed blisters on his fingers, or made himself sick from exhaustion.

With Python, and a little math, we can generate our own geometric abstract art.

By observing some of the rules present in Mondrian’s work, we can create an infinite number of paintings. No blisters required!

# Drawing Graphics with Python

In order to paint with Python, we need to install a graphics library. We’ll be using PyCairo for this demonstration.

PyCairo is a free graphics library that you can use to draw shapes using Python code. Hopefully, you already have Python 3 installed on your computer.

It’s easiest to install PyCairo from the command line, like so:

`pip install pycairo`

Once the library is installed, we can use it in our Python programs.

# The Rules of Painting

Before we write any code, we’ll need to define some rules for our painting program. We need to break down the Piet Mondrian paintings into fundamental rules.

For starters, Mondrian only uses primary colors like red, yellow, and blue. These are concentrated in squares and rectangles.

Connecting the shapes are straight black lines. These lines extend from the edges of the rectangle shapes, forming their borders.

The background canvas is always white.

With these rules, we can start to imagine the algorithm for generating a Piet Mondrian painting:

2. Draw random lines extending from the edges of the canvas.
3. Fill in some of the white rectangles created by lines with random primary colors.

We can start writing code now that we have something concrete in mind.

# Generating Art

Let’s start with importing PyCario and drawing a white canvas. Use the import keyword to import a library in Python. We always import libraries at the top of the file.

We’ll need to import the random library too. This one comes standard with Python, so there’s no need to install it like we had to do with PyCairo.

Let’s start by drawing a white canvas that’s 500 pixels wide and 500 pixels tall.

`import randomimport cairoIMAGE_WIDTH = 500IMAGE_HEIGHT = 500surface = cairo.ImageSurface(cairo.FORMAT_RGB24, IMAGE_WIDTH, IMAGE_HEIGHT)ctx = cairo.Context(surface)ctx.rectangle(0,0,IMAGE_WIDTH,IMAGE_HEIGHT)ctx.set_source_rgb(1,1,1)ctx.fill()surface.write_to_png('painting.png')`

Piet Mondrian relied on the concept of the grid, and so will we. By building our painting on a grid, we can ensure that everything lines up just like in Mondrian’s work.

How do we do that in Python? Well, we could try drawing some random lines and rectangles on the canvas with PyCario, but that wouldn’t give us the result we want.

To get something like a Mondrian painting, we’ll use a grid of tiles. Each tile will be a single unit in the grid.

By changing the color of these tiles, we can draw lines and rectangles.

# Creating the tiles

The information for our tiles is going to be stored in a dictionary. We’ll use x and y coordinates as the key for the color information of each tile.

`# mondrian.pyimport cairoimport random# the size of the imageIMAGE_WIDTH = 500IMAGE_HEIGHT = 500# the size of the tile gridMAP_WIDTH = 50MAP_HEIGHT = 50# the size of each tileTILE_SIZE = 10MAX_LINES = 15MIN_LINES = 6MAX_RECTS = 5MIN_RECTS = 1tiles = {}#colors (0,white),(1,black),(2,red),(3,yellow)(4,blue)def generate_tiles():    # build tile map    for x in range(MAP_WIDTH):        for y in range(MAP_HEIGHT):            # set every tile to white            tiles[x,y] = 0    draw_lines()`
`Drawing the MapEach of our tiles is going to be a square 10x10 pixels wide. The color information will be an integer. We’ll need a function to draw the tiles to the canvas once we’ve finished generating them.def draw_map():    # draw tile map using pycairo    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, IMAGE_WIDTH, IMAGE_HEIGHT)    ctx = cairo.Context(surface)    for x in range(MAP_WIDTH):        for y in range(MAP_HEIGHT):            size = TILE_SIZE            ctx.rectangle(x*size,y*size,x+size,y+size)            if tiles[x,y] == 0:                ctx.set_source_rgb(1,1,1)            elif tiles[x,y] == 1:                ctx.set_source_rgb(0,0,0)            elif tiles[x,y] == 2:                ctx.set_source_rgb(1,0,0)            elif tiles[x,y] == 3:                ctx.set_source_rgb(1,1,0)            else:                ctx.set_source_rgb(0,0,1)            ctx.fill()    surface.write_to_png('mondrian.png')`
`Drawing LinesWe need both horizontal and vertical lines to achieve the effect we’re after. We can do this by randomly choosing pixels along the edge of the grid.If the we want a horizontal line, we need to fill in the grid along the x axis. For the vertical, we want the y axis.def draw_lines():    total_lines = random.randint(MIN_LINES,MAX_LINES)    print(total_lines)    for h in range(int(total_lines/2)):        y = random.randint(0,MAP_HEIGHT)        for x in range(MAP_WIDTH):            tiles[x,y] = 1    for v in range(int(total_lines/2)):        x = random.randint(0,MAP_WIDTH)        for y in range(MAP_HEIGHT):            tiles[x,y] = 1    fill_rects()Filling the rectanglesFilling the rectangles is the most complicated part of this tutorial. We’re going to handle it in two parts.First, we’ll deal with our fill_rect() method. This method will search the map for white pixels. If it finds one, it will fill it with a random primary color.def fill_rects():    total_rects = random.randint(MIN_RECTS,MAX_RECTS)    max_iters = 5    for i in range(max_iters):        for r in range(total_rects):            x = random.randint(0,MAP_WIDTH-1)            y = random.randint(0,MAP_HEIGHT-1)            if tiles[x,y] == 0:                color = random.randint(2,4)                flood_recursion(x,y,0,color)Using flood fillThe flood fill algorithm works like the “bucket” tool on any paint application. In our case, it will fill some of the white squares created by the draw_lines() method with color.Flood recursion will spread out from the starting pixel and change any white pixels it encounters to the new color.The flood fill method is recursive, meaning it calls on itself as it moves from tile to tile. This allows it to check each neighbor of every tile it encounters. The flood fill stops when it runs into the border of the map, or a black tile.def flood_recursion(x,y,start_color,update_color):    width = MAP_WIDTH    height = MAP_HEIGHT    if tiles[x,y] != start_color:        return    elif tiles[x,y] == update_color:        return    else:        tiles[x,y] = update_color        neighbors = [(x-1,y),(x+1,y),(x-1,y-1),(x+1,y+1),(x-1,y+1),(x+1,y-1),(x,y-1),(x,y+1)]        for n in neighbors:            if 0 <= n[0] <= width-1 and 0 <= n[1] <= height-1:                flood_recursion(n[0],n[1],start_color,update_color)With the flood fill complete, we have everything necessary to generate a Mondrian style painting.You’ll find the final version of the program below.SummaryI’ve included some variables that we can play with to vary the results. By changing the number of lines and rectangles the program allows, we can easily change the final result.Hopefully you’ve enjoyed this lesson about Piet Mondrian, and learned something about Python in the process.The Code# mondrian.pyimport cairoimport random# the size of the imageIMAGE_WIDTH = 500IMAGE_HEIGHT = 500# the size of the tile gridMAP_WIDTH = 50MAP_HEIGHT = 50# the size of each tileTILE_SIZE = 10MAX_LINES = 15MIN_LINES = 6MAX_RECTS = 5MIN_RECTS = 1tiles = {}#colors (0,white),(1,black),(2,red),(3,yellow)(4,blue)def generate_tiles():    # build tile map    for x in range(MAP_WIDTH):        for y in range(MAP_HEIGHT):            # set every tile to white            tiles[x,y] = 0    draw_lines()def draw_lines():    total_lines = random.randint(MIN_LINES,MAX_LINES)    # print(total_lines)    for h in range(int(total_lines/2)):        y = random.randint(0,MAP_HEIGHT)        for x in range(MAP_WIDTH):            tiles[x,y] = 1    for v in range(int(total_lines/2)):        x = random.randint(0,MAP_WIDTH)        for y in range(MAP_HEIGHT):            tiles[x,y] = 1    fill_rects()def fill_rects():    total_rects = random.randint(MIN_RECTS,MAX_RECTS)    max_iters = 5    for i in range(max_iters):        for r in range(total_rects):            x = random.randint(0,MAP_WIDTH-1)            y = random.randint(0,MAP_HEIGHT-1)            if tiles[x,y] == 0:                color = random.randint(2,4)                flood_recursion(x,y,0,color)def flood_recursion(x,y,start_color,update_color):    width = MAP_WIDTH    height = MAP_HEIGHT    if tiles[x,y] != start_color:        return    elif tiles[x,y] == update_color:        return    else:        tiles[x,y] = update_color        neighbors = [(x-1,y),(x+1,y),(x-1,y-1),(x+1,y+1),(x-1,y+1),(x+1,y-1),(x,y-1),(x,y+1)]        for n in neighbors:            if 0 <= n[0] <= width-1 and 0 <= n[1] <= height-1:                flood_recursion(n[0],n[1],start_color,update_color)def draw_map():    # draw tile map using pycairo    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, IMAGE_WIDTH, IMAGE_HEIGHT)    ctx = cairo.Context(surface)    for x in range(MAP_WIDTH):        for y in range(MAP_HEIGHT):            size = TILE_SIZE            ctx.rectangle(x*size,y*size,x+size,y+size)            if tiles[x,y] == 0:                ctx.set_source_rgb(1,1,1)            elif tiles[x,y] == 1:                ctx.set_source_rgb(0,0,0)            elif tiles[x,y] == 2:                ctx.set_source_rgb(1,0,0)            elif tiles[x,y] == 3:                ctx.set_source_rgb(1,1,0)            else:                ctx.set_source_rgb(0,0,1)            ctx.fill()    surface.write_to_png('mondrian.png')generate_tiles()draw_map()`