Skip to content

Instantly share code, notes, and snippets.

@iuriguilherme
Last active January 26, 2023 18:43
Show Gist options
  • Save iuriguilherme/e92573094442db57cc6f903fffe23838 to your computer and use it in GitHub Desktop.
Save iuriguilherme/e92573094442db57cc6f903fffe23838 to your computer and use it in GitHub Desktop.
Fibonacci Spiral
"""
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