pybeamshow/beamshow.py

203 lines
5.5 KiB
Python
Raw Normal View History

2023-02-16 01:14:50 +01:00
from argparse import ArgumentParser
2023-02-18 23:16:49 +01:00
from typing import Generator, Iterable, Tuple
2023-02-15 21:08:47 +01:00
import pygame as pg
import sys
2023-02-18 23:16:49 +01:00
from effects.effect import Effect, color_wheel, Colors
from effects.moonflower import Moonflower
2023-02-17 02:08:21 +01:00
from effects.presets import Presets
2023-02-15 21:08:47 +01:00
2023-02-16 01:14:50 +01:00
def print_displays() -> None:
pg.display.init()
display = pg.display.get_desktop_sizes()
for i, d in enumerate(display):
print(f"#{i} - {d[0]}x{d[1]}")
sys.exit(0)
2023-02-15 21:08:47 +01:00
2023-02-15 22:38:02 +01:00
2023-02-16 01:14:50 +01:00
def initialize(
display_id: int, windowed: bool, trails: bool
) -> Tuple[pg.Surface, pg.Surface]:
pg.display.init()
displays = pg.display.get_desktop_sizes()
if not 0 <= display_id < len(displays):
raise ValueError(
f"Display ID {display_id} invalid. Must be between 0 and {len(displays)}!"
)
2023-02-15 21:08:47 +01:00
2023-02-16 01:14:50 +01:00
win = pg.display.set_mode(
size=displays[display_id]
if not windowed
else (displays[display_id][0] // 2, displays[display_id][1] // 2),
2023-02-16 01:21:56 +01:00
flags=pg.FULLSCREEN if not windowed else 0,
2023-02-16 01:14:50 +01:00
display=display_id,
)
if trails:
background = pg.Surface(win.get_size(), flags=pg.SRCALPHA)
background.fill(pg.Color(0, 0, 0, 5))
else:
background = pg.Surface(win.get_size())
2023-02-17 02:08:21 +01:00
background.fill(Colors.Black)
2023-02-16 01:14:50 +01:00
return win, background
2023-02-15 21:08:47 +01:00
2023-02-16 01:14:50 +01:00
def render_loop_normal(
window: pg.Surface,
2023-02-18 23:16:49 +01:00
background: pg.Surface,
2023-02-16 01:14:50 +01:00
effects: Iterable[Effect],
clock: pg.time.Clock,
2023-02-18 23:16:49 +01:00
) -> Generator[None, None, None]:
2023-02-16 01:14:50 +01:00
blackout = False
2023-02-15 21:08:47 +01:00
2023-02-16 01:14:50 +01:00
while True:
for event in pg.event.get():
2023-02-18 23:16:49 +01:00
if event.type == pg.K_SPACE:
2023-02-16 01:14:50 +01:00
blackout = not blackout
2023-02-18 23:16:49 +01:00
if event.type == pg.QUIT:
2023-02-16 01:14:50 +01:00
pg.quit()
sys.exit()
2023-02-15 21:08:47 +01:00
2023-02-16 01:14:50 +01:00
window.blit(background, (0, 0))
if not blackout:
for e in effects:
e.update()
e.draw(window)
yield
2023-02-15 21:08:47 +01:00
2023-02-16 01:14:50 +01:00
def render_loop_3d(
window: pg.Surface,
2023-02-18 23:16:49 +01:00
background: pg.Surface,
2023-02-16 01:14:50 +01:00
effects: Iterable[Effect],
clock: pg.time.Clock,
2023-02-18 23:16:49 +01:00
) -> Generator[None, None, None]:
2023-02-16 01:14:50 +01:00
stage = pg.Surface(size=window.get_size())
2023-02-17 02:08:21 +01:00
stage.fill(Colors.Black)
2023-02-16 01:14:50 +01:00
full_size = stage.get_size()
scaled_sizes = [
(((full_size[0] * i) // 100), ((full_size[1] * i) // 100)) for i in range(101)
]
scaled_positions = [
((full_size[0] - s[0]) // 2, (full_size[1] - s[1]) // 2) for s in scaled_sizes
]
while True:
for event in pg.event.get():
2023-02-18 23:16:49 +01:00
if event.type == pg.QUIT:
2023-02-16 01:14:50 +01:00
pg.quit()
sys.exit()
stage.blit(background, (0, 0))
for e in effects:
e.update()
e.draw(stage)
2023-02-17 02:08:21 +01:00
window.fill(Colors.Black)
2023-02-16 01:14:50 +01:00
2023-02-17 02:08:21 +01:00
stage.set_colorkey(Colors.Black)
for i in range(0, 101, 4):
2023-02-16 01:21:56 +01:00
stage.set_alpha(192 - 192 * ((i / 100) ** 0.5))
window.blit(
2023-02-17 02:08:21 +01:00
pg.transform.scale(stage, scaled_sizes[int(100 * (i / 100) ** 2)]),
scaled_positions[int(100 * (i / 100) ** 2)],
2023-02-16 01:21:56 +01:00
)
2023-02-16 01:14:50 +01:00
yield
def main() -> None:
argparser = ArgumentParser(
description="beamshow - Render a light show for a video projector"
)
argparser.add_argument(
"--3d",
dest="render3d",
action="store_true",
help="Render a 3D preview instead of the normal output",
)
argparser.add_argument(
"--list-displays", action="store_true", help="Show available displays"
)
argparser.add_argument(
"-w", "--window", action="store_true", help="Display in a window"
)
argparser.add_argument(
"--trails", action="store_true", help="Fade patterns out (trail mode)"
)
2023-02-17 02:08:21 +01:00
argparser.add_argument(
"--randomize",
metavar="N",
type=int,
nargs="?",
default=0,
const=5,
help="Select random effect presets after <N> seconds",
)
2023-02-16 01:14:50 +01:00
argparser.add_argument("--fps", action="store_true", help="Show FPS in console")
argparser.add_argument(
"-d", "--display", type=int, default=0, help="ID of the display to use"
)
# print some nice output after the pygame banner to separate our stuff from theirs
print("")
2023-02-18 23:16:49 +01:00
print("-" * (len(str(argparser.description)) + 4))
2023-02-16 01:14:50 +01:00
print(f" {argparser.description} ")
2023-02-18 23:16:49 +01:00
print("-" * (len(str(argparser.description)) + 4))
2023-02-16 01:14:50 +01:00
print("")
args = argparser.parse_args()
if args.list_displays:
print_displays()
window, background = initialize(
display_id=args.display, windowed=args.window, trails=args.trails
)
2023-02-17 02:08:21 +01:00
presets = Presets(bounds=window.get_rect())
if args.randomize:
effects = presets.randomize()
else:
effects = [
2023-02-18 23:16:49 +01:00
Moonflower(
2023-02-17 02:08:21 +01:00
bounds=window.get_rect(),
2023-02-18 23:16:49 +01:00
# colors=(Colors.Red,Colors.Blue),
colors=(color_wheel(), color_wheel(hue=180)),
size=window.get_height() / 4,
outer=5,
2023-02-17 02:08:21 +01:00
velocity=(1, 1),
2023-02-18 23:16:49 +01:00
rot_speed=1.5,
2023-02-17 02:08:21 +01:00
x_factor=(1, 1),
y_factor=(2.2, 2.2),
),
]
2023-02-16 01:14:50 +01:00
clock = pg.time.Clock()
if args.render3d:
loop = render_loop_3d(window, background, effects, clock)
else:
loop = render_loop_normal(window, background, effects, clock)
2023-02-17 02:08:21 +01:00
framecounter = 0
2023-02-16 01:14:50 +01:00
while True:
next(loop)
pg.display.flip()
clock.tick(60)
2023-02-17 02:08:21 +01:00
framecounter += 1
if args.randomize:
if (framecounter % (args.randomize * 60)) == 0:
effects.clear()
effects.extend(presets.randomize())
2023-02-16 01:14:50 +01:00
if args.fps:
print(clock.get_fps())
if __name__ == "__main__":
main()