user11093202
user11093202

Reputation:

How to undo in turtle very fast?

I am making a program that creates a spirograph. This is my code:

from turtle import *
from random import randint
speed(10000)
for i in range(20):
    col = randint(1, 5)
    if col == 1:
        pencolor("orange")
    elif col == 2:
        pencolor("blue")
    elif col == 3:
        pencolor("green")
    elif col == 4:
        pencolor("purple")
    elif col == 5:
        pencolor("dark blue")
    circle(50)
    left(20)

while undobufferentries():
    undo()

However, the last part of the program is the problemo. I want that part, the undo, to be very fast, as fast as the spiro was created. How to make undo very fast??

Upvotes: 2

Views: 773

Answers (2)

ggorlen
ggorlen

Reputation: 56955

An alternate approach that doesn't involve undo is keeping a buffer (list) of drawing actions performed. When you're ready to "undo", loop over the buffer of actions, repeatedly re-drawing them on a freshly reset canvas. If you pop off the tail of the buffer on each rerender, you'll eventually reach an empty buffer, the origin state. This creates the illusion of gradually undoing the drawing operations.

The key here is disabling the rendering loop with tracer(0) and manually calling turtle.update() to draw each frame. sleep() sets the delay between frames, mostly for convenience; ontimer is preferred.

Bear in mind that it takes more work to redraw everything per frame on the undo path which can impact performance.

import turtle
from random import choice
from time import sleep

colors = (
    "orange",
    "blue",
    "green",
    "purple",
    "dark blue",
)

delay = 0.1
shapes = 20
step = 20
size = 50
turtle.tracer(0)
drawn = []

for _ in range(360 // step):
    turtle.pencolor(choice(colors))
    drawn.append(turtle.pencolor())
    turtle.circle(size)
    turtle.left(step)
    turtle.update()
    sleep(delay)

while drawn:
    drawn.pop()
    turtle.reset()

    for color in drawn:
        turtle.pencolor(color)
        turtle.circle(size)
        turtle.left(step)

    turtle.update()
    sleep(delay)

turtle.exitonclick()

Upvotes: 1

cdlane
cdlane

Reputation: 41872

Three times you say you want it "very fast", but you also say, "as fast as the spiro was created". We can easily make it faster than the spiro was created, but just slightly more work to make it "as fast".

This problem points out the underbelly of the undo() mechanism. To undo just one of your circles, involves a sequence of over 80 steps. But, even that doesn't make it completely clear why it's so slow. If we turn off screen updates, we can make it instantaneous. But if that's acceptable, then you might as well replace undo() with clear().

I rarely encourage folks to mess with tracer(), and even when I do, I usually write examples that do simple tracer(False) and tracer(True), to avoid the numeric argument. But this is the rare case where we want a specific numeric argument to tracer() to control the speed of the undo() graphics:

from turtle import *
from random import choice

COLORS = ["orange", "blue", "green", "purple", "dark blue"]

hideturtle()
speed('fastest')

for _ in range(20):
    pencolor(choice(COLORS))
    circle(50)
    left(20)

tracer(20)  # value unrelated to either 20 above
while undobufferentries() > 1:
    undo()  # undo everything but hideturtle()
tracer(1)

exitonclick()

On my system, the undo of the drawing is now the same speed as the do, but you may need to fine tune the argument to tracer() to achieve the effect you want.

Upvotes: 3

Related Questions