RosettaCodeData/Task/Chaos-game/Python/chaos-game.py

116 lines
2.5 KiB
Python

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())