]> git.mar77i.info Git - zenbook_conf/commitdiff
add ease.py
authormar77i <mar77i@protonmail.ch>
Mon, 16 Dec 2024 01:26:01 +0000 (02:26 +0100)
committermar77i <mar77i@protonmail.ch>
Mon, 16 Dec 2024 01:26:01 +0000 (02:26 +0100)
ease.py [new file with mode: 0755]

diff --git a/ease.py b/ease.py
new file mode 100755 (executable)
index 0000000..a18527a
--- /dev/null
+++ b/ease.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+
+from math import asin, atan2, ceil, cos, floor, pi, sin
+
+import pygame
+
+tau = 2 * pi
+FPS = 60
+
+
+def make_line_shape(p1, p2, n, shape_r):
+    angle = atan2(p1[1] - p2[1], p1[0] - p2[0])
+    if angle < 0:
+        angle += tau
+    # 0 <= angle < tau
+    initial_corner = ceil((angle + tau * 3 / 4) * n / tau) % n
+    opposing_corner = (floor((angle + tau / 4) * n / tau) - initial_corner) % n
+    shape = [
+        (cos(a) * shape_r, sin(a) * shape_r)
+        for a in ((i + initial_corner) * tau / n for i in range(n))
+    ]
+    left = (cos(angle + tau * 3 / 4) * shape_r, sin(angle + tau * 3 / 4) * shape_r)
+    right = (cos(angle + tau / 4) * shape_r, sin(angle + tau / 4) * shape_r)
+    yield (p2[0] + left[0], p2[1] + left[1])
+    yield (p1[0] + left[0], p1[1] + left[1])
+    p = p1
+    for i in range(n):
+        s = shape[i]
+        yield (p[0] + s[0], p[1] + s[1])
+        if i == opposing_corner and p == p1 and p1 != p2:
+            yield (p[0] + right[0], p[1] + right[1])
+            p = p2
+            yield (p[0] + right[0], p[1] + right[1])
+
+
+class EaseGraph:
+    CYCLE = {"length": 4, "pause": 1}
+    STEPS = 256
+    WIDTH = 2
+    LINES_COLOR = "blue"
+    GRAPH_COLOR = "lightblue"
+
+    def __init__(self, rect, line, func):
+        self.rect = rect
+        self.line = line
+        self.current_frame = 0
+        self.current_setting = 0
+        self.func = func
+
+    def draw(self, surf):
+        pygame.draw.rect(surf, self.LINES_COLOR, self.rect, 1)
+        pygame.draw.line(surf, self.LINES_COLOR, *self.line)
+        pos = (
+            self.line[0][0],
+            self.line[0][1] + (
+                self.line[1][1] - self.line[0][1]
+            ) * (1 - self.func(self.current_setting)),
+        )
+        pygame.draw.circle(surf, self.GRAPH_COLOR, pos, 32)
+        prev = self.rect.bottomleft
+        for i in range(1, self.STEPS + 1):
+            i_fract = i / self.STEPS
+            pos = (
+                self.rect.left + (i_fract * self.rect.width),
+                self.rect.bottom - self.func(i_fract) * self.rect.height,
+            )
+            pygame.draw.polygon(
+                surf, self.GRAPH_COLOR, list(make_line_shape(prev, pos, 8, self.WIDTH))
+            )
+            prev = pos
+
+    def update(self):
+        self.current_frame += 1
+        full_cycle = sum(self.CYCLE.values())
+        elapsed = self.current_frame / FPS % (full_cycle * 2)
+        if elapsed > full_cycle:
+            elapsed = 2 * full_cycle - elapsed
+        start = self.CYCLE["pause"] / 2
+        end = start + self.CYCLE["length"]
+        if elapsed < start:
+            current_setting = 0
+        elif elapsed > end:
+            current_setting = 1
+        else:
+            current_setting = (elapsed - start) / self.CYCLE["length"]
+        if current_setting == self.current_setting:
+            return False
+        self.current_setting = current_setting
+        return True
+
+
+def ease_in_out_elastic(mag):
+    p = 1 - mag
+    s = p / tau * asin(1)
+    def inner(x):
+        if x == 0:
+            return 0
+        elif x == 1:
+            return 1
+        elif x < 0 or x > 1:
+            raise ValueError(f"x must be between 0 and 1: got {x}")
+        st = x * 2
+        st1 = st - 1
+        sgn = (st >= 1) * 2 - 1
+        return 2 ** (-sgn * 10 * st1 - 1) * sin((st1 - s) * tau / p) * sgn + (sgn > 0)
+    return inner
+
+
+def main():
+    pygame.init()
+    surf = pygame.display.set_mode((800, 600))
+    clock = pygame.time.Clock()
+    running = True
+    dirty = False
+    #lerp = lambda start, end, x: start + (end - start) * x
+    ease_in = lambda x: x * x
+    flip = lambda x: 1 - x
+    mirror = lambda f, x: (f(2 * x) if x < 0.5 else flip(f(2 * x - 2)) + 1) / 2
+    #ease_out = lambda x: flip(ease_in(flip(x)))
+    #ease_in_out = lambda x: lerp(ease_in(x), ease_out(x), x)
+    #ease_in_out = lambda x: ease_in(x) if x < .5 else ease_out(x)
+    ease_in_out = lambda x: mirror(ease_in, x)
+    #one_minus_cos = lambda x: .5 - cos(tau * x / 2) / 2
+    eg = EaseGraph(
+        pygame.Rect((64, 64), (472, 472)),
+        ((600, 64), (600, 536)),
+        ease_in_out_elastic(5 ** .5 / 2 - 0.5)
+    )
+    frame = 0
+    while True:
+        for ev in pygame.event.get():
+            if ev.type == pygame.QUIT:
+                running = False
+                break
+            elif ev.type == pygame.WINDOWEXPOSED:
+                dirty = True
+            elif ev.type == pygame.KEYDOWN:
+                if ev.key == pygame.K_ESCAPE:
+                    running = False
+                    break
+        if not running:
+            break
+        dirty |= eg.update()
+        dirty = True
+        if dirty:
+            surf.fill("black")
+            angle = frame / (tau * 30)
+            a = (300 + cos(angle) * 200, 300 + sin(angle) * 200)
+            b = (300 + cos(angle + pi) * 200, 300 + sin(angle + pi) * 200)
+            width = 100
+            n = 5
+            pygame.draw.polygon(
+                surf, "purple", list(make_line_shape(a, b, n, width / 2)), 1
+            )
+            shape = [
+                (cos(a) * width / 2, sin(a) * width / 2)
+                for a in (i * tau / n for i in range(n))
+            ]
+            pygame.draw.polygon(surf, "yellow", [(s[0] + a[0], s[1] + a[1]) for s in shape], 1)
+            pygame.draw.polygon(surf, "yellow", [(s[0] + b[0], s[1] + b[1]) for s in shape], 1)
+            frame += 1
+            eg.draw(surf)
+            pygame.display.update()
+            dirty = False
+        clock.tick(FPS)
+
+
+if __name__ == "__main__":
+    main()