This repository has been archived on 2025-05-08. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
animations/flowsnake.py
2021-12-06 11:35:47 +01:00

134 lines
3.5 KiB
Python

import random
from math import pi, cos, sin, atan, sqrt, acos
import pygame
from animations import Animation
from turtle import Turtle
COLORS = [
0xFF0000,
0xFFFF00,
0x008800,
0x00FFFF,
0x0000FF,
]
BACKGROUND = (17, 17, 17)
WIDTH, HEIGHT = 600, 600
RECORD = False
FPS = 30
SECONDS = 7
FRAMES = FPS * SECONDS
def get_color(t):
k = int(t * (len(COLORS) - 1))
color1 = [COLORS[k] >> j & 0xFF for j in range(16, -1, -8)]
color2 = [COLORS[min(k + 1, len(COLORS) - 1)] >> j & 0xFF for j in range(16, -1, -8)]
t -= k / (len(COLORS) - 1)
t *= len(COLORS) - 1
return [round(color1[j] * (1 - t) + color2[j] * t) for j in range(3)]
class FlowSnake(Animation):
def __init__(self):
super().__init__(WIDTH, HEIGHT, "flowsnake", FPS, RECORD)
self.order = 7
self.lines = []
self.calculate()
def calculate(self):
points = [(0, 0)]
def draw_line(_, b):
points.append(b)
turtle = Turtle(0, 0, 0, draw_line)
self.draw_flowsnake(turtle, self.order, True, 1)
padding = 50
dx = points[-1][0] - points[0][0]
dy = points[-1][1] - points[0][1]
angle = -atan(dy / dx) - acos(5 / 2 / sqrt(7))
if points[0][0] > points[-1][0]:
angle += pi
minx = 1e1337
maxx = -1e1337
miny = 1e1337
maxy = -1e1337
for i, (x, y) in enumerate(points):
x, y = x * cos(angle) - y * sin(angle), x * sin(angle) + y * cos(angle)
points[i] = x, y
minx = min(minx, x)
maxx = max(maxx, x)
miny = min(miny, y)
maxy = max(maxy, y)
last = None
lines = []
for i, (x, y) in enumerate(points):
x = (x - minx) / (maxx - minx) * (self.width - padding * 2) + padding
y = (y - miny) / (maxy - miny) * (self.height - padding * 2) + padding
if last:
t = (i - 1) / (len(points) - 1)
lines.append((last, (x, y), t))
last = x, y
self.lines = lines
def render(self):
k = 100_000
lines = [line for _, line in sorted([(i + random.randint(-k, k), line) for i, line in enumerate(self.lines)])]
self.win.fill(BACKGROUND)
for a, b, t in lines:
pygame.draw.line(self.win, get_color(t), a, b)
for _ in range(FPS // 2):
yield
head = 0
tail = -len(lines) // 3
cnt = 0
total = (len(lines) - tail) // FRAMES
while tail < len(lines):
if head < len(lines):
a, b, _ = lines[head]
pygame.draw.line(self.win, (24, 24, 24), a, b)
if tail >= 0:
a, b, t = lines[tail]
pygame.draw.line(self.win, get_color(t), a, b)
head += 1
tail += 1
cnt += 1
if cnt % total == 0:
yield
def draw_flowsnake(self, turtle: Turtle, order: int, axiom: bool, length: float):
if not order:
turtle.forward(length)
return
for k in "A-B--B+A++AA+B-" if axiom else "+A-BB--B-A++A+B":
if k == "A":
self.draw_flowsnake(turtle, order - 1, True, length)
elif k == "B":
self.draw_flowsnake(turtle, order - 1, False, length)
elif k == "+":
turtle.turn(-60)
elif k == "-":
turtle.turn(60)
animation = FlowSnake()
animation.run()