Last active
January 26, 2023 18:43
-
-
Save iuriguilherme/e92573094442db57cc6f903fffe23838 to your computer and use it in GitHub Desktop.
Fibonacci Spiral
This file contains 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
""" | |
This was supposed to be a Moiré Fibonacci Spiral for Genuary 23 | |
https://genuary.art/prompts#jan23 | |
However, I couldn't figure out how to pair up a Moiré pattern and a | |
Fibonacci pattern. So this is just Fibonacci Spirals :P | |
It has been published as Genuary 18 "Definitely not a grid" | |
This is made with a 1366x768 screen size | |
Live action: | |
https://www.twitch.tv/collections/GRyymawpOxeuZg | |
pip install sympy | |
""" | |
import inspect | |
import logging | |
import math | |
import sympy | |
import sys | |
import threading | |
import time | |
import tkinter | |
import turtle | |
logging.basicConfig(level = 'INFO') | |
def spiral( | |
window: int, | |
log_level: str, | |
max_iterations: int, | |
max_pens: int, | |
starting_radius: int, | |
starting_angle: int, | |
root_width: int, | |
root_height: int, | |
screen_colormode: int, | |
screen_tracer: int, | |
screen_mode: str, | |
pen_speed: int, | |
precision: float, | |
close: bool, | |
pen_size: int, | |
starting_iteration: int, | |
verbose: bool, | |
*args, | |
**kwargs, | |
) -> None: | |
"""Fibonacci Spiral""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(log_level.upper()) | |
logger.info(f"""\ | |
max_iterations: {max_iterations} \ | |
max_pens: {max_pens} \ | |
starting radius: {starting_radius} \ | |
starting angle: {starting_angle} \ | |
root_width: {root_width} \ | |
root_height: {root_height} \ | |
""") | |
try: | |
window_length: int = math.floor(math.log10(window)) | |
max_iterations_length: int = \ | |
math.floor(math.log10(max_iterations)) | |
max_pens_length: int = math.floor(math.log10(max_pens)) | |
boundaries: dict[str, float] = { | |
'llx': -1.0, # left | |
'lly': -1.0, # down | |
'urx': 1.0, # right | |
'ury': 1.0, # up | |
} | |
pens: list[turtle.RawTurtle] = [] | |
root: tkinter.Tk = tkinter.Tk() | |
root.geometry("x".join([str(root_width), str(root_height)])) | |
if sys.platform in ["linux", "linux2"]: | |
root.wait_visibility(root) | |
root.overrideredirect(True) | |
root.wm_attributes("-alpha", 0.5) | |
elif sys.platform in ["darwin"]: | |
root.overrideredirect(True) | |
# ~ root.wm_attributes("-topmost", True) | |
root.wm_attributes("-transparent", True) | |
root.config(bg = 'systemTransparent') | |
elif sys.platform in ["win32"]: | |
root.overrideredirect(True) | |
root.lift() | |
# ~ root.wm_attributes("-topmost", True) | |
root.wm_attributes("-disabled", True) | |
root.wm_attributes("-transparentcolor", "white") | |
# ~ root.wm_attributes("-fullscreen", True) | |
root.update() | |
canvas: tkinter.Canvas = tkinter.Canvas( | |
root, | |
width = root_width, | |
height = root_height, | |
) | |
canvas.pack(expand = True, fill = 'both') | |
screen: turtle.TurtleScreen = turtle.TurtleScreen(canvas) | |
screen.mode(screen_mode) | |
screen.tracer(screen_tracer) | |
screen.colormode(screen_colormode) | |
for i in range(window): | |
pen: turtle.RawTurtle = turtle.RawTurtle(screen) | |
pen.hideturtle() | |
pen.speed(pen_speed) | |
pen.pensize(pen_size) | |
pens.append(pen) | |
for j in range(starting_iteration, max_iterations): | |
fib: int = sympy.fibonacci(j) | |
for k, pen in enumerate(pens): | |
root.title(f"""Fibonacci Spirals \ | |
turtle {k + 1:0{window_length + 1}d}/{window + 1} \ | |
iter {j + 1:0{max_iterations_length + 1}d}/{max_iterations} \ | |
window {window + 1:0{max_pens_length + 1}d}/{max_pens}""") | |
root.update() | |
radius: int = starting_radius + fib | |
angle: float = (starting_angle + k) / 2 | |
color: tuple[int] = ( | |
min(255, round(angle * ((512 / max(1, | |
max_iterations + max_pens)) / 2))), | |
255 - min(255, round(j * (256 / max(1, | |
max_iterations)))), | |
min(255, round(k * (256 / max(1, | |
window)))), | |
) | |
text_color: tuple[int] = ( | |
255 -color[0], | |
255 - color[1], | |
255 - color[2], | |
) | |
background_color: tuple[int] = ( | |
255 - color[0], | |
color[1], | |
255 - color[2], | |
) | |
pen.pencolor(color) | |
canvas.configure(bg = f"""#\ | |
{''.join([f'{c:02X}' for c in background_color])}""") | |
pen.circle(radius, angle) | |
pos: tuple[float] = pen.pos() | |
data: str = f"""\n\ | |
iteration: \ | |
{j + 1:0{max_iterations_length + 1}d}/{max_iterations} \ | |
turtle: {k + 1:0{window_length + 1}d}/{window + 1} \ | |
window {window + 1:0{max_pens_length + 1}d}/{max_pens} \ | |
angle: {angle:0{max_pens_length + 1}f}°\n\ | |
fibonacci: {fib}\n\ | |
turtle size: {pen_size}\n\ | |
turtle color: #{''.join([f"{c:02X}" for c in color])} \ | |
(red {color[0]}, green {color[1]}, blue {color[2]})\n\ | |
background color: #{''.join([f"{c:02X}" for c in background_color])} \ | |
(red {background_color[0]}, green {background_color[1]}, blue \ | |
{background_color[2]})\n\ | |
text color: #{''.join([f"{c:02X}" for c in text_color])} \ | |
(red {text_color[0]}, green {text_color[1]}, blue {text_color[2]})\n\ | |
turtle position:\n\tx: {pos[0]}\n\ty: {pos[1]}\n\ | |
screen scale:\n\tx: {screen.xscale}\n\ty: {screen.yscale}\n\ | |
boundaries:\n\tllx: {boundaries['llx']}\n\tlly: {boundaries['lly']}\n\t\ | |
urx: {boundaries['urx']}\n\tury: {boundaries['ury']}\n\ | |
""" | |
if verbose: | |
canvas.delete('text') | |
canvas.create_text( | |
0, | |
0, | |
fill = f"""#\ | |
{''.join([f'{c:02X}' for c in text_color])}""", | |
tag = 'text', | |
text = data, | |
) | |
print(data) | |
if boundaries.get('llx') > pos[0]: | |
new_boundary: float = pos[0] | |
boundaries['llx'] = new_boundary | |
boundaries['lly'] = new_boundary | |
boundaries['urx'] = -new_boundary | |
boundaries['ury'] = -new_boundary | |
if boundaries.get('lly') > pos[1]: | |
new_boundary: float = pos[1] | |
boundaries['llx'] = new_boundary | |
boundaries['lly'] = new_boundary | |
boundaries['urx'] = -(new_boundary) | |
boundaries['ury'] = -(new_boundary) | |
if boundaries.get('urx') < pos[0]: | |
new_boundary: float = pos[0] | |
boundaries['llx'] = -new_boundary | |
boundaries['lly'] = -new_boundary | |
boundaries['urx'] = new_boundary | |
boundaries['ury'] = new_boundary | |
if boundaries.get('ury') < pos[1]: | |
new_boundary: float = pos[1] | |
boundaries['llx'] = -new_boundary | |
boundaries['lly'] = -new_boundary | |
boundaries['urx'] = new_boundary | |
boundaries['ury'] = new_boundary | |
if precision != min( | |
precision, | |
screen.xscale, | |
screen.yscale, | |
): | |
logger.warning(f"""window {window} reached \ | |
floating point precision limit""") | |
continue | |
screen.setworldcoordinates(**boundaries) | |
time.sleep(1e-306) | |
(close or root.mainloop()) and root.destroy() | |
except Exception as e: | |
logger.exception(e) | |
raise | |
def single_window( | |
*args, | |
log_level: str, | |
max_iterations: int, | |
max_pens: int, | |
starting_radius: int, | |
starting_angle: int, | |
root_width: int, | |
root_height: int, | |
screen_colormode: int, | |
screen_tracer: int, | |
screen_mode: str, | |
pen_speed: int, | |
precision: float, | |
close: bool, | |
pen_size: int, | |
starting_iteration: int, | |
verbose: bool, | |
**kwargs, | |
) -> None: | |
"""Start the program with one single window.""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(log_level.upper()) | |
try: | |
for pens in range(255, max_pens): | |
spiral( | |
window = pens, | |
max_pens = max_pens, | |
starting_iteration = starting_iteration, | |
root_width = root_width, | |
root_height = root_height, | |
close = True, | |
# ~ close = False, | |
log_level = log_level, | |
max_iterations = max_iterations, | |
starting_radius = starting_radius, | |
starting_angle = starting_angle, | |
screen_colormode = screen_colormode, | |
screen_tracer = screen_tracer, | |
screen_mode = screen_mode, | |
pen_speed = pen_speed, | |
precision = precision, | |
pen_size = pen_size, | |
verbose = verbose, | |
) | |
except Exception as e: | |
logger.exception(e) | |
raise | |
def many_windows( | |
*args, | |
log_level: str, | |
max_iterations: int, | |
max_pens: int, | |
starting_radius: int, | |
starting_angle: int, | |
root_width: int, | |
root_height: int, | |
screen_colormode: int, | |
screen_tracer: int, | |
screen_mode: str, | |
pen_speed: int, | |
precision: float, | |
close: bool, | |
pen_size: int, | |
starting_iteration: int, | |
verbose: bool, | |
**kwargs, | |
) -> None: | |
"""Start the program with as many windows as pens. Each window has \ | |
the same pens as the last one, plus a new pen. Each window is spawn in \ | |
a new thread.""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(log_level.upper()) | |
try: | |
threads: list[threading.Thread] = [] | |
for window in range(1, max_pens): | |
thread: threading.Thread = threading.Thread( | |
target = spiral, | |
args = ( | |
window, | |
log_level, | |
max_iterations, | |
max_pens, | |
starting_radius, | |
starting_angle, | |
root_width, | |
root_height, | |
screen_colormode, | |
screen_tracer, | |
screen_mode, | |
pen_speed, | |
precision, | |
close, | |
pen_size, | |
starting_iteration, | |
), | |
) | |
threads.append(thread) | |
for thread in threads: | |
thread.start() | |
for thread in threads: | |
thread.join() | |
del(threads) | |
except Exception as e: | |
logger.exception(e) | |
raise | |
def main( | |
*args, | |
log_level: str = 'INFO', | |
max_iterations: int = 256, | |
# ~ max_iterations: int = 3, | |
# ~ max_pens: int = 108, | |
max_pens: int = 256, | |
starting_radius: int = 0, | |
starting_angle: int = 0, | |
root_width: int = 640, | |
root_height: int = 480, | |
screen_colormode: int = 255, | |
screen_tracer: int = int(1e306), | |
screen_mode: str = 'world', | |
pen_speed: int = 0, | |
precision: float = 1e-306, | |
pen_size: int = 1, | |
starting_iteration: int = 0, | |
# ~ starting_iteration: int = 128, | |
close: bool = True, | |
verbose: bool = True, | |
**kwargs, | |
) -> None: | |
"""Genuary 23, 2023""" | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel(log_level.upper()) | |
## The program used on this video collection | |
## https://www.twitch.tv/collections/GRyymawpOxeuZg | |
# ~ many_windows( | |
single_window( | |
max_pens = max_pens, | |
root_width = root_width, | |
root_height = root_height, | |
close = close, | |
log_level = log_level, | |
max_iterations = max_iterations, | |
starting_radius = starting_radius, | |
starting_angle = starting_angle, | |
screen_colormode = screen_colormode, | |
screen_tracer = screen_tracer, | |
screen_mode = screen_mode, | |
pen_speed = pen_speed, | |
precision = precision, | |
pen_size = pen_size, | |
starting_iteration = starting_iteration, | |
verbose = verbose, | |
) | |
if __name__ == '__main__': | |
logger: logging.Logger = logging.getLogger( | |
inspect.currentframe().f_code.co_name) | |
logger.setLevel('INFO') | |
try: | |
main() | |
logger.info("END") | |
except (KeyboardInterrupt, tkinter.TclError): | |
sys.exit("BYE") | |
except Exception as e: | |
logger.exception(e) | |
sys.exit(repr(e)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment