pybeamshow/effects/effect.py
2023-02-19 16:02:49 +01:00

135 lines
3.9 KiB
Python

from dataclasses import dataclass
import math
import random
from typing import Generator, Literal, Tuple
import pygame as pg
def copy_color(source: pg.Color) -> pg.Color:
return pg.Color(source.r, source.g, source.b, source.a)
@dataclass(frozen=True, slots=True)
class Colors:
Black = pg.Color(0, 0, 0)
White = pg.Color(255, 255, 255)
Red = pg.Color(255, 0, 0)
Green = pg.Color(0, 255, 0)
Blue = pg.Color(0, 0, 255)
Cyan = pg.Color(0, 255, 255)
Yellow = pg.Color(255, 255, 0)
Magenta = pg.Color(255, 0, 255)
def color_wheel(hue=0, increase=1) -> Generator[pg.Color, None, None]:
color = copy_color(Colors.Red)
h, s, l, a = color.hsla
h = hue
while True:
color.hsla = h, s, l, a
yield color
h = (h + increase) % 360
def color_randomize() -> Generator[pg.Color, None, None]:
color = copy_color(Colors.Red)
h, s, l, a = color.hsla
color.hsla = random.randint(0, 359), s, l, a
while True:
yield color
color.hsla = random.randint(0, 359), s, l, a
def rainbow_surface(
image: pg.Surface,
orientation: Literal["h", "v"] = "h",
hue: int = 0,
increase: int = 1,
):
wheel = color_wheel(hue, increase)
if orientation == "h":
h = image.get_height()
for x in range(image.get_width()):
pg.draw.line(image, next(wheel), (x, 0), (x, h))
elif orientation == "v":
w = image.get_width()
for y in range(image.get_height()):
pg.draw.line(image, next(wheel), (0, y), (w, y))
def transform_bounce(
bounds: pg.Rect,
velocity: Tuple[int, int],
x_factor: Tuple[float, float],
y_factor: Tuple[float, float],
) -> Generator[Tuple[int, int], Tuple[int, int], None]:
min_velocity = velocity[0]
max_velocity = velocity[1]
current_velocity = random.randint(min_velocity, max_velocity)
phase = random.uniform(0, 360)
current_x_factor = random.uniform(x_factor[0], x_factor[1])
current_y_factor = random.uniform(y_factor[0], y_factor[1])
size_x, size_y = yield (bounds.centerx, bounds.centery)
while True:
pos_x = int(
math.cos(current_x_factor * phase) * (bounds.width - size_x) // 2
+ bounds.centerx
)
pos_y = int(
math.sin(current_y_factor * phase) * (bounds.height - size_y) // 2
+ bounds.centery
)
phase += current_velocity / 180 * math.pi
current_velocity = random.randint(min_velocity, max_velocity)
size_x, size_y = yield (pos_x, pos_y)
def transform_oscillate(
bounds: pg.Rect,
period: int,
initial_pos: Tuple[int, int] = (-1, -1),
) -> Generator[Tuple[int, int], Tuple[int, int], None]:
pos_x = float(initial_pos[0] if initial_pos[0] > 0 else bounds.left)
pos_y = float(initial_pos[1] if initial_pos[1] > 0 else bounds.top)
direction = "+"
size_x, size_y = yield (bounds.left, bounds.top)
while True:
range_x = bounds.width - size_x
range_y = bounds.height - size_y
inc_x = range_x / period
inc_y = range_y / period
if direction == "+":
pos_x = pg.math.clamp(pos_x + inc_x, 0, range_x)
pos_y = pg.math.clamp(pos_y + inc_y, 0, range_y)
else:
pos_x = pg.math.clamp(pos_x - inc_x, 0, range_x)
pos_y = pg.math.clamp(pos_y - inc_y, 0, range_y)
if (inc_x and (pos_x > range_x - inc_x)) or (
inc_y and (pos_y > range_y - inc_y)
):
direction = "-"
elif (inc_x and (pos_x < inc_x)) or (inc_y and (pos_y < inc_y)):
direction = "+"
size_x, size_y = yield (int(pos_x), int(pos_y))
class Effect(pg.sprite.Sprite):
def __init__(
self, image: pg.Surface, rect: pg.Rect, *groups: pg.sprite.Group
) -> None:
super().__init__(*groups)
self.rect = rect
self.image = image
def draw(self, surface: pg.Surface):
surface.blit(self.image, self.rect)