Search code examples
pythonturtle-graphicsscenefractals

Is there a way turtle-graphics lets us change the fill color?


I want to make a scenery with this code of fractals, and I need the fill color to change every second. I've tried a for-loop to redraw the whole thing, but that interferes with the other parts of my code that shouldn't be repeated

Is there a way turtle-graphics lets us change the fill color?

from turtle import *
from time import sleep, perf_counter
tracer(0)
def hilbert(size, level, parity):
    if level == 0:
        return
    left(parity * 90)
    hilbert(size, level - 1, -parity)
    forward(size)
    right(parity * 90)
    hilbert(size, level - 1, parity)
    forward(size)
    hilbert(size, level - 1, parity)
    right(parity * 90)
    forward(size)
    hilbert(size, level - 1, -parity)
    left(parity * 90)
def fractal(dist, depth, dir):
    if depth < 1:
        fd(dist)
        return
    fractal(dist / 3, depth - 1, dir)
    lt(60 * dir)
    fractal(dist / 3, depth - 1, dir)
    rt(120 * dir)
    fractal(dist / 3, depth - 1, dir)
    lt(60 * dir)
    fractal(dist / 3, depth - 1, dir)
reset()
speed(0)
ht()
pu()
size = 6

setpos(-33*size, -32*size)
pd()
fillcolor("chocolate")
begin_fill()
fd(size)

hilbert(size, 6, 1)

fd(size)
for i in range(3):
    lt(90)
    fd(size*(64+i%2))
pu()
for i in range(2):
    fd(size)
    rt(90)
pd()
for i in range(4):
    fd(size*(66+i%2))
    rt(90)
end_fill()
update()

Output:

Fractals


Solution

  • Encapsulating the drawing code in functions, and adding a timer, behind the scenes we can redraw it every second with a different background color. This leaves you free to use the turtle to draw other things while the background color changes:

    from turtle import Screen, Turtle
    from itertools import cycle
    
    COLORS = ['chocolate', 'tomato', 'plum', 'salmon', 'bisque', 'lime', 'olive', 'wheat']
    
    size = 6
    
    def hilbert(t, size, level, parity=1):
        if level == 0:
            return
    
        t.left(parity * 90)
        hilbert(t, size, level - 1, -parity)
        t.forward(size)
        t.right(parity * 90)
        hilbert(t, size, level - 1, parity)
        t.forward(size)
        hilbert(t, size, level - 1, parity)
        t.right(parity * 90)
        t.forward(size)
        hilbert(t, size, level - 1, -parity)
        t.left(parity * 90)
    
    def filled_hilbert(t, color):
        t.clear()
        t.setheading(0)
    
        t.penup()
        t.setposition(-33 * size, -32 * size)
        t.pendown()
    
        t.fillcolor(next(color))
        t.begin_fill()
    
        t.forward(size)
    
        hilbert(t, size, 6, 1)
    
        t.forward(size)
    
        for i in range(3):
            t.left(90)
            t.forward(size * (64 + i % 2))
    
        t.penup()
    
        for i in range(2):
            t.forward(size)
            t.right(90)
    
        t.pendown()
    
        for i in range(4):
            t.forward(size * (66 + i % 2))
            t.right(90)
    
        t.end_fill()
    
        screen.update()
    
        screen.ontimer(lambda: filled_hilbert(t, color), 1000)
    
    screen = Screen()
    screen.tracer(0)
    
    turtle = Turtle()
    turtle.hideturtle()
    
    filled_hilbert(turtle, cycle(COLORS))
    
    screen.mainloop()
    

    However, any turtle that's drawing comes to the front, so this image won't necessarily stay in the background unless you synchronize your forground drawing with it.