 
import argparse
import random
import shapely.geometry as geometry
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
 
 
def main(args):
    # Styles
    plt.style.use("ggplot")
 
    # Creating figure
    fig = plt.figure()
    line, = plt.plot([], [], ".")
 
    # Limit axes
    plt.xlim(0, 1)
    plt.ylim(0, 1)
 
    # Titles
    title = "Chaos Game"
    plt.title(title)
    fig.canvas.set_window_title(title)
 
    # Getting data
    data = get_data(args.frames)
 
    # Creating animation
    line_ani = animation.FuncAnimation(
        fig=fig,
        func=update_line,
        frames=args.frames,
        fargs=(data, line),
        interval=args.interval,
        repeat=False
    )
 
    # To save the animation install ffmpeg and uncomment
    # line_ani.save("chaos_game.gif")
 
    plt.show()
 
 
def get_data(n):
    """
    Get data to plot
    """
    leg = 1
    triangle = get_triangle(leg)
    cur_point = gen_point_within_poly(triangle)
    data = []
    for _ in range(n):
        data.append((cur_point.x, cur_point.y))
        cur_point = next_point(triangle, cur_point)
    return data
 
 
def get_triangle(n):
    """
    Create right triangle
    """
    ax = ay = 0.0
    a = ax, ay
 
    bx = 0.5  *  n
    by = 0.75 * (n ** 2)
    b = bx, by
 
    cx = n
    cy = 0.0
    c = cx, cy
 
    triangle = geometry.Polygon([a, b, c])
    return triangle
 
 
def gen_point_within_poly(poly):
    """
    Generate random point inside given polygon
    """
    minx, miny, maxx, maxy = poly.bounds
    while True:
        x = random.uniform(minx, maxx)
        y = random.uniform(miny, maxy)
        point = geometry.Point(x, y)
        if point.within(poly):
            return point
 
 
def next_point(poly, point):
    """
    Generate next point according to chaos game rules
    """
    vertices = poly.boundary.coords[:-1]  # Last point is the same as the first one
    random_vertex = geometry.Point(random.choice(vertices))
    line = geometry.linestring.LineString([point, random_vertex])
    return line.centroid
 
 
def update_line(num, data, line):
    """
    Update line with new points
    """
    new_data = zip(*data[:num]) or [(), ()]
    line.set_data(new_data)
    return line,
 
 
if __name__ == "__main__":
    arg_parser = argparse.ArgumentParser(description="Chaos Game by Suenweek (c) 2017")
    arg_parser.add_argument("-f", dest="frames", type=int, default=1000)
    arg_parser.add_argument("-i", dest="interval", type=int, default=10)
 
    main(arg_parser.parse_args())
 
 