Skip to content

Instantly share code, notes, and snippets.

@kms70847
Last active June 19, 2018 18:13
Show Gist options
  • Save kms70847/ec70b3dc6b0789f78fa4 to your computer and use it in GitHub Desktop.
Save kms70847/ec70b3dc6b0789f78fa4 to your computer and use it in GitHub Desktop.
Fractal Visualizer Tool
try:
from tkinter import *
except ImportError:
from Tkinter import *
try:
from queue import Queue
except ImportError:
from Queue import Queue
try:
import tkinter.simpledialog as tkSimpleDialog
except ImportError:
import tkSimpleDialog
from collections import deque
import functools
import math
import colorsys
import threading
import time
import ast
import sys
TICKS_PER_IDLE_STEP = 50
pending_calls = Queue()
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def angle(self):
return math.atan2(self.y, self.x)
def magnitude(self):
return math.hypot(self.x, self.y)
def tuple(self):
return (self.x, self.y)
def __add__(self, other):
return Point(self.x+other.x, self.y+other.y)
def __mul__(self, val):
return Point(self.x*val, self.y*val)
def exp(radius, angle):
return Point(math.cos(angle)*radius, math.sin(angle)*radius)
def get_color(tup):
hue = sum(v / 2.0**i for i,v in enumerate(tup, 1))
value = 1 - (1 / (float(len(tup))+1))
r,g,b = [int(x*255) for x in colorsys.hsv_to_rgb(hue, 0.75, value)]
return "#{:02x}{:02x}{:02x}".format(r,g,b)
def cyclic_iter(seq):
for i in range(len(seq)):
j = (i + 1) % len(seq)
yield seq[i], seq[j]
def poly(c, points, **kwargs):
for a, b in cyclic_iter(points):
c.create_line(*(a.tuple() + b.tuple()), **kwargs)
def rotated(p, angle):
r = p.magnitude()
if r == 0: return p
return exp(r, angle+p.angle())
def run_pending_calls():
while not pending_calls.empty():
pending_calls.get()()
def gen_draw_routine(offsets, scales, rotations):
root = Point(200,200), 400, 0, ()
d = deque()
d.appendleft(root)
while d:
center, side_length, angle, tup = d.pop()
if side_length < 1:
continue
corner_radius = side_length / math.sqrt(2)
points = [center + exp(corner_radius, math.radians(theta)+angle) for theta in (45, 135, 225, 315)]
pending_calls.put(functools.partial(poly, canvas, points, fill=get_color(tup)))
for idx, (offset, scale, rotation) in enumerate(zip(offsets, scales, rotations)):
d.appendleft((
rotated(offset,angle)*(side_length/2)+center,
side_length * scale,
rotation+angle,
tup + (idx,)
))
yield
while True:
yield
def scale_changed(self):
print("scale changed.")
global draw_routine
offsets = [
Point(scales[0][0].get(), scales[0][1].get()),
Point(scales[1][0].get(), scales[1][1].get()),
Point(scales[2][0].get(), scales[2][1].get())
]
_scales = [
scales[0][3].get(),
scales[1][3].get(),
scales[2][3].get()
]
rotations = [
math.radians(scales[0][2].get()),
math.radians(scales[1][2].get()),
math.radians(scales[2][2].get())
]
draw_routine = gen_draw_routine(offsets, _scales, rotations)
canvas.delete(ALL)
for _ in range(TICKS_PER_IDLE_STEP): next(draw_routine)
run_pending_calls()
def idle():
if draw_routine is not None:
for _ in range(TICKS_PER_IDLE_STEP): next(draw_routine)
run_pending_calls()
root.after(100, idle)
def import_values(s):
values = ast.literal_eval(s)
for j, row in enumerate(values):
for i, val in enumerate(row):
scales[i][j].set(val)
def export_values():
return str([[scales[i][j].get() for i in range(3)] for j in range(4)])
def import_button_clicked():
data = tkSimpleDialog.askstring("Import Data", "Enter slider data.", parent=root)
import_values(data)
def export_button_clicked():
tkSimpleDialog.askstring("Exported Data", "Copy this data to your clipboard.", parent=root, initialvalue=export_values())
root = Tk()
canvas = Canvas(root, width=400, height=400, bg="white")
canvas.grid(row=0, column=0, columnspan=4)
draw_routine = None
scales = []
for i in range(3):
x_scale = Scale(root, from_=-1, to=1, resolution = 0.01, orient=HORIZONTAL, command=scale_changed)
y_scale = Scale(root, from_=-1, to=1, resolution = 0.01, orient=HORIZONTAL, command=scale_changed)
rotation_scale = Scale(root, from_=0, to=360, resolution = 1, orient=HORIZONTAL, command=scale_changed)
scale_scale = Scale(root, from_=0.25, to=1, resolution = 0.01, orient=HORIZONTAL, command=scale_changed)
x_scale.grid(column=1+i, row=1)
y_scale.grid(column=1+i, row=2)
rotation_scale.grid(column=1+i, row=3)
scale_scale.grid(column=1+i, row=4)
scales.append((x_scale, y_scale, rotation_scale, scale_scale))
for idx, word in enumerate("X-coord Y-coord rotation scale".split()):
Label(root, text=word).grid(column=0, row=idx+1)
default_values = [
[-0.5, 0, 0.5],
[0.5, -0.5, 0.5],
[0,0,0],
[0.5, 0.5, 0.5]
]
import_values(str(default_values))
menu_bar = Menu(root)
edit_menu = Menu(menu_bar, tearoff=0)
edit_menu.add_command(label="Import", command=import_button_clicked)
edit_menu.add_command(label="Export", command=export_button_clicked)
menu_bar.add_cascade(label="Edit", menu=edit_menu)
root.config(menu=menu_bar)
root.after(100, idle)
root.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment