Title: Draw Fibonacci word fractals in Python
This example uses the Fibonacci word to draw a fractal curve. Fibonacci words seem sort of random. They're definitely not, as you can tell by reading my post Generate Fibonacci words in Python, but they look randomish.
However, if you use the randomish digits in a Fibonacci word and the following simple rules, a remarkable fractal pops out.
- Draw in the current direction.
- If the digit is 0, continue in the same direction.
- If the digit is 1, turn 90°:
- If i is even, turn 90° to the right.
- If i is odd, turn 90° to the left.
The following sections explain how the example program applies those rules.
Making Fibonacci Points
The following code generates the Fibonacci fractal's points.
# Define directions, oriented so adding 1 turns right.
UP = 0
RIGHT = 1
DOWN = 2
LEFT = 3
def make_fibo_points(word_num):
'''Return a list of points to draw the fractal.'''
# Get the Fibonacci word.
word = fibo_word(word_num)
# Calculate the points.
points = [(0, 0)]
dir = RIGHT
for i in range(len(word)):
dir = add_point(word, points, i, dir)
This code first defines constants to represent the directions UP, RIGHT, DOWN, and LEFT.
The make_fibo_points function gets the appropriate Fibonacci word. It makes a points list that initially contains the origin (0, 0). It initializes dir = RIGHT and then loops through the word's characters. For each character, the code calls the add_point function to add the next point to the points list.
Adding Points
The following add_point method adds one point to the fractal's points list.
def add_point(word, points, i, dir):
'''Add the next point in the direction and return the new direction.'''
# Draw in the current direction.
dist = 5
x, y = points[i]
if dir == UP: y -= dist
elif dir == DOWN: y += dist
elif dir == LEFT: x -= dist
elif dir == RIGHT: x += dist
points.append((x, y))
# Turn appropriately.
# If the digit is 0, turn.
# If the digit is 1, keep the current direction:
if word[i] == '0':
if i % 2 == 0: # If i is even, turn 90° left.
dir = (dir + 3) % 4
else: # If i is odd, turn 90° right.
dir = (dir + 1) % 4
return dir
First, the code draws in the current direction. It checks the dir value and adds a distance to the last point's X or Y coordinate to move either up, down, left, or right. It then adds the new point to the points list.
Next, the code uses the Fibonacci fractal rules to change direction. The function finishes by returning the new direction.
Transforming and Drawing
With enough work, you might be able to figure out how big the fractal will be and scale and translate it appropriately. Instead of doing that, the program uses the technique described in my post Map points so they fit within a target area in Python to transform the points so they fit on the program's canvas.
Here's the code the program uses to draw the fractal.
def draw_fractal(self, *args):
'''Draw the Fibonacci fractal.'''
# Get the word nmber.
word_num = int(self.word_num_var.get())
if word_num < 2: return # It doesn't work if word_num < 2.
# Display the Fibonacci word.
self.fibo_word_var.set(fibo_word(word_num))
# Get the points.
points = make_fibo_points(word_num)
# Make the points fit.
self.canvas.update()
margin = 10
rect = (margin, margin,
self.canvas.winfo_width() - margin,
self.canvas.winfo_height() - margin)
points = transform_list(points, rect)
# Draw it.
self.canvas.delete(tk.ALL)
self.canvas.create_line(points, fill='green', width=1)
x, y = points[0]
self.canvas.create_oval(x - 3, y - 3, x + 3, y + 3, fill='red')
The code first gets the selected Fibonacci word number. It calls the fibo_word function to get that Fibonacci word (see my post Generate Fibonacci words in Python) and displays it in a read-only text box at the bottom of the program's window.
Next, the code calls make_fibo_points to get the fractal's points. It then makes a list holding the bounds of a target rectangle where it wants to draw the fractal and calls transform_list to transform the points so they fit on the canvas. (See my post Map points so they fit within a target area in Python.)
The code then uses create_line to draw the fractal on the canvas. Finally, it uses draw_oval to draw a red circle at the fractal's first point so you can tell where the fractal starts. Feel free to comment out that statement if you like.
Conclusion
If you look at the Wikipedia post Fibonacci word fractal, you'll see a slightly different result. For some reason, it uses a slightly different definition of the Fibonacci word described in the other Wikipedia post Fibonacci word. The results are similar, though, so I'm not going to worry about it. (I already wasted a bunch of time trying to see what's different.)
Download the example to experiment with it and to see additional details. Meanwhile, the following picture shows Fibonacci fractals numbers 6 through 11.
|