Title: Draw a Sierpinski gasket fractal in Python and PIL
In addition to describing the Sierpinski curve, Sierpinski pentagon, and Sierpinski carpet (and a bunch of other stuff), Wacław Sierpiński (1882 - 1969) also described the Sierpinski gasket. It's a shape similar to the Sierpinski carpet except it uses triangles instead of rectangles.
You can download the example to see the user interface code; this post focuses on the fractal-drawing code.
The following make_gasket method starts drawing the fractal.
def make_gasket(self, depth, wid, hgt, corners, outside_color,
fill, background, outline):
'''Make the gasket image.'''
# Make the image, initially filled with outside_color.
image = Image.new('RGB', (wid, hgt), outside_color)
dr = ImageDraw.Draw(image)
# Fill the triangle with the fill color.
dr.polygon(corners, fill=fill, outline=outline)
# Recursively draw the gasket.
self.remove_center(depth, dr, corners, background, outline)
return image
This code creates an image of the desired size and fills it with the indicated outside color fg. It then draws a large triangle, filled with the fill color and outlined with the outline color.
The method then calls the following remove_center method to remove triangle's middle piece.
def remove_center(self, depth, dr, corners, background, outline):
'''Clear the center of this triangle.'''
# Find points p1, p2, and p3.
p0, p1, p2 = corners
x3 = (p0[0] + p1[0]) / 2
y3 = (p0[1] + p1[1]) / 2
p3 = (x3, y3)
x4 = (p0[0] + p2[0]) / 2
y4 = (p0[1] + p2[1]) / 2
p4 = (x4, y4)
x5 = (p1[0] + p2[0]) / 2
y5 = (p1[1] + p2[1]) / 2
p5 = (x5, y5)
# Fill the inner triangle with the background color.
dr.polygon((p3, p4, p5), fill=background, outline=outline)
# Recursively draw the corner triangles.
depth -= 1
if depth >= 0:
self.remove_center(depth, dr, (p0, p3, p4), background, outline)
self.remove_center(depth, dr, (p3, p1, p5), background, outline)
self.remove_center(depth, dr, (p4, p5, p2), background, outline)
This method must first find the corners of the triangle's four sub-triangles. The picture on the right shows the original necessary points. The method's corners parameter is a list holding points p0, p1, and p2 in that order.
To find the other points, the remove_center method averages the coordinates of the given points. For example, the p3 is halfway between p0 and p1 so the method averages their components to find p3.
Having found points p3, p4, and p5, the code fills the inner triangle p3-p4-p5 with the background color.
Next, the code decrements depth and, if depth is still at least 0, the method recursively calls itself to erase the middles of the three outer sub-triangles.
ASIDE: You may have noticed that some of the coordinates are the same for various points. In particular, p3 and p4 have the same Y coordinate, and p5 has the same Y coordinate as p1 and p2. You could use those coordinates instead of calculating the necessary coordinates separately. That works as long as you maintain the orientation shown in the picture. However, if the points passed into the recursive calls don't have the same orientation (top, left, right), then that doesn't work and the triangles are all messed up. (It's actually pretty interesting. Give it a try.)
This example explicitly calculates the p3, p4, and p5 Y coordinates so the order of the points in the corners list doesn't matter.
|
Like many recursive fractals, the code for this one is deceptively simple but it can generate very complicated results.
The following pictures show Sierpinski gaskets with depths 0 through 3.
Download the example to experiment with it and to see additional details.
|
The picture on the left is a three-dimensional version of the Sierpinski gasket called the Sierpinski pyramid.
This picture was generated by techniques described in my book Build Your Own Ray Tracer With Python.
|
|
|