mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
125 lines
3.5 KiB
Python
125 lines
3.5 KiB
Python
"""Easing, palette and per-frame timing helpers shared by the scenes."""
|
|
import math
|
|
from engine import hexc, mix, with_alpha, scale_rgb
|
|
import score as S
|
|
|
|
# --- palette (sampled from the MobileGlues icon, pushed for contrast) --------------------------------
|
|
INK = hexc('#06110a') # near-black green: dark plates
|
|
INK2 = hexc('#0d2213') # panels on dark
|
|
MOSS = hexc('#1d4a25') # deep forest
|
|
LEAF = hexc('#4e9a3e') # icon outline green
|
|
GLUE = hexc('#6edb55') # icon highlight lime: the signal colour
|
|
NEON = hexc('#8cff62', 2.6) # emissive lime (blooms)
|
|
MINT = hexc('#a0e28c') # icon background: light plates
|
|
PALE = hexc('#d4f0c9') # pale top face
|
|
BONE = hexc('#f1f7ec') # type on dark
|
|
CORAL = hexc('#ff6a55') # rare accent: errors / device lost
|
|
CORAL_HOT = hexc('#ff6a55', 2.2)
|
|
AMBER = hexc('#e7a94f') # speckle tan, tiny accents
|
|
CLEAR = (0.0, 0.0, 0.0, 0.0)
|
|
|
|
|
|
def a(c, al):
|
|
return with_alpha(c, al)
|
|
|
|
|
|
# --- easing ----------------------------------------------------------------------------------------
|
|
def clamp01(x):
|
|
return 0.0 if x < 0 else 1.0 if x > 1 else x
|
|
|
|
|
|
def lin(t, t0, t1):
|
|
return clamp01((t - t0) / (t1 - t0)) if t1 != t0 else float(t >= t0)
|
|
|
|
|
|
def out_expo(u):
|
|
u = clamp01(u)
|
|
return 1.0 if u >= 1 else 1 - 2 ** (-10 * u)
|
|
|
|
|
|
def in_expo(u):
|
|
u = clamp01(u)
|
|
return 0.0 if u <= 0 else 2 ** (10 * u - 10)
|
|
|
|
|
|
def out_cubic(u):
|
|
u = clamp01(u)
|
|
return 1 - (1 - u) ** 3
|
|
|
|
|
|
def in_cubic(u):
|
|
u = clamp01(u)
|
|
return u ** 3
|
|
|
|
|
|
def in_out_cubic(u):
|
|
u = clamp01(u)
|
|
return 4 * u ** 3 if u < 0.5 else 1 - (-2 * u + 2) ** 3 / 2
|
|
|
|
|
|
def out_back(u, k=1.7):
|
|
u = clamp01(u)
|
|
return 1 + (k + 1) * (u - 1) ** 3 + k * (u - 1) ** 2
|
|
|
|
|
|
def spring(u, freq=3.0, damp=6.0):
|
|
"""0 -> 1 with overshoot; u in seconds-ish units (unclamped above)."""
|
|
if u <= 0:
|
|
return 0.0
|
|
return 1 - math.exp(-damp * u) * math.cos(2 * math.pi * freq * u)
|
|
|
|
|
|
def bump(u):
|
|
"""0 -> 1 -> 0 over u in [0, 1]."""
|
|
u = clamp01(u)
|
|
return math.sin(math.pi * u)
|
|
|
|
|
|
def lerp(a_, b, u):
|
|
return a_ + (b - a_) * u
|
|
|
|
|
|
def rnd(*k):
|
|
"""Deterministic hash -> [0, 1)."""
|
|
h = 2166136261
|
|
for v in k:
|
|
for ch in repr(v):
|
|
h = ((h ^ ord(ch)) * 16777619) & 0xffffffff
|
|
h ^= h >> 13
|
|
h = (h * 1274126177) & 0xffffffff
|
|
return (h & 0xffffff) / float(0x1000000)
|
|
|
|
|
|
# --- frame timing ----------------------------------------------------------------------------------
|
|
class Frame:
|
|
def __init__(self, t, entry_start_bar=0):
|
|
self.t = t
|
|
self.bar = t / S.BAR
|
|
self.beat = t / S.BEAT
|
|
self.b0 = entry_start_bar
|
|
self.lt = t - entry_start_bar * S.BAR # local seconds in entry
|
|
self.lbeat = self.lt / S.BEAT # local beats
|
|
self.lbar = self.lt / S.BAR
|
|
self.kick = S.pulse(t, 'kick', 0.11)
|
|
self.clap = S.pulse(t, 'clap', 0.10)
|
|
self.hat = S.pulse(t, 'hat', 0.05)
|
|
self.crash = S.pulse(t, 'crash', 0.6)
|
|
self.impact = S.pulse(t, 'impact', 0.35)
|
|
self.roll = S.pulse(t, 'roll', 0.08)
|
|
|
|
def at(self, beat):
|
|
"""Seconds since local beat `beat` (negative before)."""
|
|
return self.lt - beat * S.BEAT
|
|
|
|
def u(self, b0, b1):
|
|
"""Progress through local beats [b0, b1]."""
|
|
return clamp01((self.lbeat - b0) / (b1 - b0))
|
|
|
|
def step(self, div=1.0):
|
|
"""Index of the current local beat subdivision (div=2 -> 8ths)."""
|
|
return int(math.floor(self.lbeat * div + 1e-6))
|
|
|
|
|
|
def frame_idx(t, fps=60):
|
|
return int(math.floor(t * fps + 1e-6))
|