From 29d5218f976373f690cf4fe583978486a5af88be Mon Sep 17 00:00:00 2001
From: Patrick Moessler <pub@asaril.de>
Date: Wed, 15 Feb 2023 21:08:47 +0100
Subject: [PATCH] initial

---
 .gitignore              |  3 ++
 .vscode/launch.json     | 16 ++++++++
 beamshow.py             | 46 +++++++++++++++++++++++
 effects/bouncingspot.py | 83 +++++++++++++++++++++++++++++++++++++++++
 effects/effect.py       | 14 +++++++
 5 files changed, 162 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .vscode/launch.json
 create mode 100644 beamshow.py
 create mode 100644 effects/bouncingspot.py
 create mode 100644 effects/effect.py

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a68c77e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.pyc
+__pycache__
+.mypy_cache
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..389baf2
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,16 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Python: beamshow",
+            "type": "python",
+            "request": "launch",
+            "program": "beamshow.py",
+            "console": "integratedTerminal",
+            "justMyCode": true
+        }
+    ]
+}
\ No newline at end of file
diff --git a/beamshow.py b/beamshow.py
new file mode 100644
index 0000000..c189dc4
--- /dev/null
+++ b/beamshow.py
@@ -0,0 +1,46 @@
+from pygame.locals import *
+import math
+import pygame as pg
+import random
+import sys
+import time
+
+from effects.bouncingspot import BouncingSpot
+
+# pg.init()
+
+win = pg.display.set_mode(size=(1600, 1200), flags=pg.RESIZABLE)
+
+background = pg.Surface(win.get_size())
+background.fill(pg.Color(0, 0, 0))
+
+
+effects = [
+    BouncingSpot(
+        bounds=win.get_rect(),
+        colored=False,
+        sizes=(300, 300),
+        velocity=(1, 1),
+        x_factor=(1, 1),
+        y_factor=(2.2, 2.2),
+    ),
+    # BouncingSpot(bounds=win.get_rect())
+]
+
+
+FPS = pg.time.Clock()
+
+while True:
+    for event in pg.event.get():
+        if event.type == QUIT:
+            pg.quit()
+            sys.exit()
+
+    win.blit(background, (0, 0))
+    for e in effects:
+        e.update()
+        e.draw(win)
+
+    pg.display.flip()
+    FPS.tick(60)
+    # print(FPS.get_fps())
diff --git a/effects/bouncingspot.py b/effects/bouncingspot.py
new file mode 100644
index 0000000..4762390
--- /dev/null
+++ b/effects/bouncingspot.py
@@ -0,0 +1,83 @@
+from typing import Any
+import pygame as pg
+from effects.effect import Effect
+import random
+import math
+
+
+class BouncingSpot(Effect):
+    # MIN_SIZE = 10
+    # max_size = 100
+    # min_velocity = 1
+    # max_velocity = 10
+    # min_xy_factor = 0.1
+    # max_xy_factor = 1
+
+    def __init__(
+        self,
+        bounds: pg.Rect,
+        colored=True,
+        sizes=(10, 100),
+        velocity=(1, 10),
+        x_factor=(0.1, 1),
+        y_factor=(0.1, 1),
+        *groups: pg.sprite.Group
+    ) -> None:
+        self.colored = colored
+        self.min_size = sizes[0]
+        self.max_size = sizes[1]
+        self.min_velocity = velocity[0]
+        self.max_velocity = velocity[1]
+
+        self.velocity = random.randint(self.min_velocity, self.max_velocity)
+        self.ticks = random.randint(0, 360)
+        self.x_factor = random.uniform(x_factor[0], x_factor[1])
+        self.y_factor = random.uniform(y_factor[0], y_factor[1])
+        self.color = pg.Color(
+            255,
+            0 if colored else 255,
+            0 if colored else 255,
+            255,
+        )
+        size = (math.sin(self.ticks) / 2 + 0.5) * (
+            self.max_size - self.min_size
+        ) + self.min_size
+        image = pg.Surface((self.max_size, self.max_size), flags=pg.SRCALPHA, depth=32)
+        super().__init__(
+            image=image,
+            rect=pg.Rect(
+                bounds.centerx - size / 2,
+                bounds.centery - size / 2,
+                size,
+                size,
+            ),
+            *groups
+        )
+        self.bounds = bounds
+        self.update()
+
+    def update(self, *args: Any, **kwargs: Any) -> None:
+        new_size = (math.sin(self.ticks) / 2 + 0.5) * (
+            self.max_size - self.min_size
+        ) + self.min_size
+
+        new_scale = new_size - self.rect.width
+        self.rect.inflate_ip(new_scale, new_scale)
+        self.rect.centerx = (
+            0.4 * math.cos(self.x_factor * self.ticks) * self.bounds.width
+            + self.bounds.centerx
+        )
+        self.rect.centery = (
+            0.4 * math.sin(self.y_factor * self.ticks) * self.bounds.height
+            + self.bounds.centery
+        )
+
+        self.image.fill(pg.Color(255, 255, 0, 0))
+        pg.draw.ellipse(self.image, self.color, ((0, 0), self.rect.size))
+
+        self.ticks += self.velocity / 180 * math.pi
+        self.velocity = random.randint(self.min_velocity, self.max_velocity)
+        if self.colored:
+            h, s, l, a = self.color.hsla
+            h = (h + 1) % 256
+            self.color.hsla = h, s, l, a
diff --git a/effects/effect.py b/effects/effect.py
new file mode 100644
index 0000000..940efa4
--- /dev/null
+++ b/effects/effect.py
@@ -0,0 +1,14 @@
+import pygame as pg
+from abc import abstractmethod
+
+
+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)