Computer-generated Mondrian art #1

Inspired by Michael Fogleman's blog post from 2011, here is a Python implementation of his algorithm for generating images which resemble the paintings of Dutch painter Piet Mondrian (1872 - 1944). A slightly different, object-oriented approach will be presented in a subsequent post.

The algorithm

We start with a canvas defined as the unit square $0 \le x \le1$, $0\le y \le 1$ (we will later map co-ordinates on this canvas to an SVG image). A line on this canvas is defined by the tuple (y, (x1,y2)) in the case of a horizontal line from (x1,y) to (x2,y) and by the tuple (x, (y1,y2)) in the case of a vertical line from (x,y1) to (x,y2). The lines are collected together in two lists associated with a dictionary, keyed by HORIZONTAL and VERTICAL which are the values True and False respectively. We start by setting down boundary lines along the edges of the canvas:

HORIZONTAL, VERTICAL = True, False
lines = {
HORIZONTAL: [(0, (0,1)), (1, (0,1))],
VERTICAL: [(0, (0,1)), (1, (0,1))]
}


We add nlines lines in randomly-chosen orientations (horizontal or vertical) by first choosing a random point on an existing randomly-selected line to place the start point, and then finding the set of all the lines that can be reached by a perpendicular line from this point. The end point of our new line is the intersection of the perpendicular line with a randomly-chosen line from this set.

Of course, the first line to be added must cross the entire image:

Subsequent lines may have several choices for their endpoints, however:

To colour the regions defined by the lines, we need to find the boxes defined by the intersections: to do this, we first sort the list of horizontal lines by their $y$-coordinate. Next, loop over this list and for each line find all the adjacent vertical lines which intersect it and extend above it: this gives the lower corners of the boxes with thier lower edges on this line.

Next follow the vertical lines upwards and find their next intersections with a horizontal line to get the upper corners of the box. The boxes are stored as the tuple of coordinates (bx1, by1, bx2, by2) where (bx1, by1) is the lower lefthand corner and (bx2, by2) is the upper righthand corner.

The box colour is selected at random from a weighted distribution: each colour is associated with a point on a cumulative probability distribution function (cdf) and a number chosen at random from a uniform distribution on [0,1). The value of this number then determines which colour is selected according to which range it falls into in the cdf.

colours = ['blue', 'red', 'yellow', 'white']
colours_cdf = [0.15, 0.3, 0.45, 1.0]


Thus, there is a probability of 0.15 that each of blue, red or yellow will be chosen and a probability of 0.65 for white.

The SVG image is created by rendering a series of coloured rectangles for the "boxes" with rect element, followed by the lines. The canvas coordinates are simply multiplied by the desired SVG image width and height dimensions.

Current rating: 4.3