Last active
June 19, 2018 18:13
-
-
Save kms70847/ec70b3dc6b0789f78fa4 to your computer and use it in GitHub Desktop.
Fractal Visualizer Tool
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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