05. Mondrian Tiles - Drawing with Code

05. Mondrian Tiles - Drawing with Code

ยท

4 min read

Introduction

Piet Mondrian is a Dutch artist who is regarded as one of the greatest artists of the 20th century. Mondrian Tiles come from some of the paintings he did and consist of colourful tiles arranged in a pattern. In this article, we will try to recreate these patterns using an algorithm.

Mondrian Tiles 1.png

Background

As usual, we will handle this using Processing. However, instead of the default Java language, we will use the Python language in this example as the data structures in Python are helpful for this. To switch to Python, you can click on the drop-down menu on the top-right and select 'Add More...' Then select 'Python Mode for Processing 3' and click 'Install'. After this, you can use the same drop-down menu to select the Python Mode.

Implementation

Setting Up

As usual, we will create a canvas and set the background colour as zero. We will also remove the stroke and set the frame rate to 1 so that we can animate the pattern at a slow rate.

Constants and Variables

First of all, we will define a variable to hold the tiles. For this, we can use the list data type available in Python. We will then define a few constants we need.

# Splitting Constants
splits = [0.3, 0.5, 0.7]; # Few splitting ratios to split a tile
optimumDepth = 10; # Maximum depth we go within the recursive algorithm

# Drawing Constants
spacing = 1; # Spacing between tiles
minSize = 10 # Minimum size of a tile

# Colour palette
palette = ['#f3f3f3', '#f50f0f', '#fae316', '#0d7fbe', '#000000'] # 
paletteProb = [0.8, 0.05, 0.05, 0.05, 0.05] # Probabilities defined for each colour in the palette

The Code

Splitting Function

The way we achieve the tiling effect is by using a programming technique called 'Recursion'. Recursion means that we call the function from within itself. From this, we can achieve interesting mathematical outcomes.

In our case, we first start with a tile, which is the canvas itself. We then split this tile into two. We then split these tiles further, the resulting ones and so on. Thus, we end up with a recursive algorithm. If we reach the maximum depth allowed, or the resulting tile is smaller than the defined size, we simply return the location and size as a tuple data type defined in Python language.

def split(myX, myY, myWidth, myHeight, depth):
    if ((depth == optimumDepth) or (myWidth < minSize) or (myHeight < minSize)):
        return (myX, myY, myWidth, myHeight)

    else:
        tile1 = 0
        tile2 = 0

        if (random(0, 1) > 0.5):
            splitSize = myWidth * splits[int(random(0, len(splits)))]
            tile1 = (myX, myY, splitSize, myHeight)
            tile2 = (myX + splitSize, myY,  myWidth - splitSize, myHeight)

        else:
            splitSize = myHeight * splits[int(random(0, len(splits)))]
            tile1 = (myX, myY, myWidth, splitSize)
            tile2 = (myX, myY + splitSize,  myWidth, myHeight - splitSize)

        return [split(tile1[0], tile1[1], tile1[2], tile1[3], depth + 1), split(tile2[0], tile2[1], tile2[2], tile2[3], depth + 1)]

The splitting works as follows: First, we decide if splitting is done horizontally or vertically by generating a random number. Then we pick a random splitting ratio and generate the split size. Then we create one tile with the split size and another for the remaining size.

Drawing

Once we complete the tiling process, we draw the tiles on the screen. This is done by iterating over all the items in the tiles list and for each item if the item is a tuple we draw it. Otherwise, we call the same function to draw the sublist. (Recursion again)

def drawTiles(tileList):
    noStroke();

    for item in tileList:
        if (type(item) is list):
            drawTiles(item)

        else:
            if ((item[2] > (spacing * 2)) and (item[3] > (spacing * 2))):
                drawTile(item[0], item[1], item[2], item[3]);

When we draw the tiles, we skip the tiles that are too small. Each tile will be coloured with a random colour from the palette. Each colour is selected from a cumulative distribution function.

Cumulative Distribution Function

A Cumulative Distribution Function is a function that allows us to use a set of probabilities to choose a set of random items according to the probability of each item. Implementing this is simple.

def randomDist():
    pdf = [(0, paletteProb[0]), (1, paletteProb[1]), (2, paletteProb[2]), (3, paletteProb[3]), (4, paletteProb[4])]
    cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]

    return max(i for r in [random(0, 1)] for i,c in cdf if c <= r)

Mondrian Tiles 2.png

Finalising

Once all of this is done, we will call the drawTiles function from the draw function to animate our pattern. With this, we have a version of the Mondrian Tiles generated through code. We can change the colour palette, spacings and split ratios to create interesting patterns. As usual, you can find the code in the Github Repository.

Mondrian Tiles 3.gif

External Links:

ย