import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
import traceback
def circle(x, y, r, rot, steps=100):
a = rot + np.linspace(0., 2. * np.pi, steps)
return np.append((np.cos(a) * r) + x, x), np.append((np.sin(a) * r) + y, y)
class Element:
def __init__(self, rate, edge_radius, path_radius, path_offset, tracers):
self.centre_x = 0
self.centre_y = 0
self.path_x = 0
self.path_y = 0
self.edge_radius = edge_radius
self.edge_handle, = plt.plot([],[], '-')
self.path_radius = path_radius
self.path_offset = path_offset
self.path_handle, = plt.plot([],[], ':')
self.tracers = tracers
self.tracer_handles = []
self.tracer_paths = []
for colour, fmt, _ in self.tracers:
hnd, = plt.plot([],[], fmt, color=colour)
self.tracer_handles += [ hnd ]
self.tracer_paths += [ [[], []] ]
self.parent = None
self.children = []
self.rate = rate
self.position = 0
self.rotation = 0
self.acc_rotation = 0
def step(self, show_elements):
self.rotation += self.rate
if (self.parent is not None):
self.acc_rotation = self.rotation + self.parent.acc_rotation
if (self.parent.path_radius <= self.parent.edge_radius):
offset = self.parent.path_radius - self.edge_radius
delta = (self.rate * self.edge_radius) / self.parent.path_radius
else:
offset = self.parent.path_radius + self.edge_radius
delta = -((self.rate * self.edge_radius) / self.parent.path_radius)
self.position -= delta*2
self.centre_x = self.parent.path_x + np.cos(self.position) * offset
self.centre_y = self.parent.path_y + np.sin(self.position) * offset
if show_elements:
self.edge_handle.set_data(*circle(self.centre_x,
self.centre_y,
self.edge_radius,
self.acc_rotation))
else:
self.edge_handle.set_data([],[])
self.path_x = self.centre_x + np.cos(self.acc_rotation) * self.path_offset
self.path_y = self.centre_y + np.sin(self.acc_rotation) * self.path_offset
if show_elements:
self.path_handle.set_data(*circle(self.path_x,
self.path_y,
self.path_radius,
self.acc_rotation))
else:
self.path_handle.set_data([],[])
for hnd, path, (_, _, offset) in zip(self.tracer_handles,
self.tracer_paths,
self.tracers):
path[0] += [ self.path_x + np.cos(self.acc_rotation) * offset ]
path[1] += [ self.path_y + np.sin(self.acc_rotation) * offset ]
hnd.set_data(*path)
for child in self.children:
child.step(show_elements)
def add(self, element):
self.children += [ element ]
self.children[-1].parent = self
class UI:
def __init__(self):
self.show_elements = True
figw, figh, figdpi = 950, 950, 50
self.figure = plt.figure(facecolor='w', figsize=(figw/figdpi, figh/figdpi), dpi=figdpi)
self.axis = plt.gca()
plt.axis('off')
plt.xlim([-1,1])
plt.ylim([-1,1])
radius = 0.45
num_pens = 10
cmap = [plt.get_cmap('RdBu')(int(idx))
for idx in np.linspace(64, 256-64, num_pens)]
pens = [(colour, '-', radius-delta)
for colour, delta in zip(cmap, np.linspace(0.1,0.3,num_pens))]
elem_0 = Element(0.05, radius, radius, 0.0, pens)
self.root_element = Element(0.0, 1.0, 1.0, 0.0, [])
self.root_element.add(elem_0)
plt.show()
def on_click(self, event):
if not (event.inaxes == self.axis): return
plt.sca(self.axis)
try:
self.show_elements = not self.show_elements
self.root_element.step(self.show_elements)
except Exception:
plt.title(traceback.format_exc())
def on_move(self, event):
if not (event.inaxes == self.axis): return
plt.sca(self.axis)
try:
self.root_element.step(self.show_elements)
except Exception:
plt.title(traceback.format_exc())
def attach(self, key, func):
self.figure.canvas.mpl_connect(key, func)
ui = UI()
def on_click(event):
global ui
ui.on_click(event)
def on_move(event):
global ui
ui.on_move(event)
ui.attach('button_press_event', on_click)
ui.attach('motion_notify_event', on_move)