import matplotlib.pyplot as plt
from math import sin, cos, radians
from matplotlib.animation import FuncAnimation

axiom = 'FX'
rules = {'X': 'X+YF+', 'Y': '-FX-Y'}

def apply_rules(axiom, rules, repeat):
    for _ in range(repeat):
        axiom = ''.join(rules.get(symbol, symbol) for symbol in axiom)
    return axiom

def walk(commands, position=(0, 0), angle=0, turn=90):
    path = [position]
    for move in commands:
        if move == 'F':
            position = (position[0] + cos(radians(angle)),
                        position[1] + sin(radians(angle)))
            path.append(position)
        elif move == '-': angle -= turn
        elif move == '+': angle += turn
    return path

path = walk(apply_rules(axiom, rules, 10))
x, y = zip(*path)
print(len(path))

import matplotlib as mpl
mpl.rcParams['toolbar'] = 'None'
fig = plt.figure('Heighway dragon')
#plt.title('Heighway dragon')
plt.xlim(min(x) - 1, max(x) + 1)
plt.ylim(min(y) - 1, max(y) + 1)
plot_path, = plt.plot([], [], '-')
plot_current, = plt.plot(0, 0, 'r.')

step = last_step = 0
running = False

def flip_running():
    global running
    running = not running

def reset_running():
    global step
    step = 0

import keyboard
keyboard.add_hotkey('w', flip_running)
keyboard.add_hotkey('r', reset_running)
print('press w to start/stop animation, r to reset')

def move(i):
    global step, last_step
    if step >= 0 and step != last_step:
        plot_path.set_data(*zip(*path[:step+1]))
        plot_current.set_data(*path[step])
        last_step = step
    if running:
        step += 1
        if step >= len(path):
            step = -100

animation = FuncAnimation(fig,
    func=move, 
    frames=None,
    interval=50,
    repeat_delay=0,
    repeat=True)

plt.tight_layout()
plt.show()
