2025-12-06 17:38:13 +01:00

110 lines
4.0 KiB
Python

import json
import pygame
from pygame import transform, gfxdraw, Vector2, Surface
from math import sin, cos, pi
from time import time, sleep
from random import random
INTERPOLATION = {
'sin2': lambda x: sin(x) ** 2,
'sin': lambda x: sin(x),
'cos': lambda x: 1 - cos(x),
'lin': lambda x: x
}
FADE = {
(True, True): lambda x: sin(x) ** 2, # fade both
(True, False): lambda x: 1 - cos(x), # fade in
(False, True): lambda x: sin(x), # fade out
(False, False): lambda x: x / (pi/2) # linear
}
class Star:
def __init__(self, sid: str, x: int, y: int, alpha: float, rot: float, scale: float, offset: float, image: pygame.Surface):
self.sid = sid
self.x = x
self.y = y
self.rot = rot
self.alpha = alpha
self.scale = scale
self.offset = offset
self.image = image
class Screen:
def __init__(self, stars: list[Star], background: Surface | None, framerate: float = 30) -> None:
if not pygame.get_init():
pygame.init()
self.screen = pygame.display.set_mode((1920, 1080), display=1, flags=pygame.FULLSCREEN | pygame.DOUBLEBUF | pygame.WINDOWTAKEFOCUS)
self.clock = pygame.time.Clock()
self.stars = stars
self.last = time()
self.brightness = 1.0
self.active_scene = 0
self.last_scene = 0
self.scene_time: float = 0
self.framerate: float = framerate
self.overlay = pygame.surface.Surface(self.screen.get_size(), depth=32)
if background:
ratio_x = self.screen.get_width() / background.get_width()
ratio_y = self.screen.get_height() / background.get_height()
scaled_background = transform.rotozoom(background, 0, max(ratio_x, ratio_y))
self.bg_pos = (
(self.screen.get_width() - scaled_background.get_width()) / 2,
(self.screen.get_height() - scaled_background.get_height()) / 2
)
self.background = scaled_background
else:
self.background = None
self.bg_pos = None
def update(self, scene_data):
if not self.background:
self.screen.fill((0, 0, 0))
now = time()
delta = now - self.last
self.last = now
self.scene_time += delta
fade_in = scene_data[self.active_scene]['transition']['fade_in']
fade_out = scene_data[self.active_scene]['transition']['fade_out']
t = FADE[(fade_in, fade_out)](min(self.scene_time / scene_data[self.active_scene]['transition']['t'], 1) * pi/2) * pi/2
weight_x = INTERPOLATION[scene_data[self.active_scene]['transition']['x']](t)
weight_y = INTERPOLATION[scene_data[self.active_scene]['transition']['y']](t)
weight = INTERPOLATION['sin2'](t)
def update_star_scene(star, old, new):
star.x = weight_x*new[star.sid]['x'] + (1-weight_x)*old[star.sid]['x']
star.y = weight_y*new[star.sid]['y'] + (1-weight_y)*old[star.sid]['y']
star.rot = weight*new[star.sid]['rot'] + (1-weight)*old[star.sid]['rot']
star.scale = weight*new[star.sid]['scale'] + (1-weight)*old[star.sid]['scale']
# first draw the background, if any
if self.background:
self.screen.blit(self.background, self.bg_pos)
# draw all the star objects
for star in self.stars:
update_star_scene(star, scene_data[self.last_scene], scene_data[self.active_scene])
star.alpha += star.rot * delta
computed_scale = star.scale * (self.screen.get_width() / star.image.get_width() / 100)
scaled = transform.rotozoom(star.image, star.alpha, computed_scale)
(w, h) = scaled.get_size()
pos = (star.x*self.screen.get_width() - w/2, star.y*self.screen.get_height() - h/2)
self.screen.blit(scaled, pos)
self.overlay.set_alpha(int(255 * (1 - self.brightness)))
self.screen.blit(self.overlay, (0, 0))
pygame.display.flip()
self.clock.tick(self.framerate)
def close(self):
pygame.quit()