Source code for mini_arcade_core.engine.render.effects.base
"""
Screen-space post effects base classes and protocols.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Protocol, runtime_checkable
from mini_arcade_core.backend import Backend
from mini_arcade_core.engine.render.context import RenderContext
[docs]
@runtime_checkable
class Effect(Protocol):
"""
Screen-space post effect.
IMPORTANT: Effects should draw ONLY using ctx.viewport (screen-space),
and must not assume anything about world-space transforms.
"""
effect_id: str
[docs]
def apply(self, backend: Backend, ctx: RenderContext):
"""
Apply the effect to the current framebuffer.
:param backend: Backend to use for rendering.
:type backend: Backend
:param ctx: Render context with viewport info.
:type ctx: RenderContext
"""
[docs]
@dataclass
class EffectParams:
"""
Shared params (Material-ish controls) for v1.
:ivar intensity (float): Effect intensity.
:ivar wobble_speed (float): Speed factor for animated distortion.
:ivar tint (tuple[int, int, int, int] | None): Optional RGBA tint.
"""
intensity: float = 1.0
wobble_speed: float = 1.0
tint: tuple[int, int, int, int] | None = None
[docs]
@dataclass
class EffectStack:
"""
Runtime state: what effects are enabled + their params.
Zero-overhead path:
- if enabled=False OR active is empty => PostFXPass returns immediately.
:ivar enabled (bool): Master toggle for post effects.
:ivar active (list[str]): List of active effect IDs.
:ivar params (dict[str, EffectParams]): Per-effect parameters.
"""
enabled: bool = False
active: list[str] = field(default_factory=list)
params: dict[str, EffectParams] = field(default_factory=dict)
[docs]
def is_active(self) -> bool:
"""
Check if any effects are active.
:return: True if effects are enabled and at least one is active.
:rtype: bool
"""
return self.enabled and bool(self.active)
[docs]
def toggle(self, effect_id: str):
"""
Toggle an effect on/off.
:param effect_id: ID of the effect to toggle.
:type effect_id: str
"""
if effect_id in self.active:
self.active.remove(effect_id)
else:
self.active.append(effect_id)