- Fix TerminalDisplay: add screen clear each frame (cursor home + erase down) - Fix CameraStage: use set_canvas_size instead of read-only viewport properties - Fix Glitch effect: preserve visible line lengths, remove cursor positioning - Fix Fade effect: return original line when fade=0 instead of empty string - Fix Noise effect: use input line length instead of terminal_width - Remove HUD effect from all presets (redundant with border FPS display) - Add regression tests for effect dimension stability - Add docs/ARCHITECTURE.md with Mermaid diagrams - Add mise tasks: diagram-ascii, diagram-validate, diagram-check - Move markdown docs to docs/ (ARCHITECTURE, Refactor, hardware specs) - Remove redundant requirements files (use pyproject.toml) - Add *.dot and *.png to .gitignore Closes #25
146 lines
4.7 KiB
Python
146 lines
4.7 KiB
Python
"""
|
|
Pipeline parameters - Runtime configuration layer for animation control.
|
|
|
|
PipelineParams is the target for AnimationController - animation events
|
|
modify these params, which the pipeline then applies to its stages.
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
|
|
@dataclass
|
|
class PipelineParams:
|
|
"""Runtime configuration for the pipeline.
|
|
|
|
This is the canonical config object that AnimationController modifies.
|
|
Stages read from these params to adjust their behavior.
|
|
"""
|
|
|
|
# Source config
|
|
source: str = "headlines"
|
|
source_refresh_interval: float = 60.0
|
|
|
|
# Display config
|
|
display: str = "terminal"
|
|
border: bool = False
|
|
|
|
# Camera config
|
|
camera_mode: str = "vertical"
|
|
camera_speed: float = 1.0
|
|
camera_x: int = 0 # For horizontal scrolling
|
|
|
|
# Effect config
|
|
effect_order: list[str] = field(
|
|
default_factory=lambda: ["noise", "fade", "glitch", "firehose"]
|
|
)
|
|
effect_enabled: dict[str, bool] = field(default_factory=dict)
|
|
effect_intensity: dict[str, float] = field(default_factory=dict)
|
|
|
|
# Animation-driven state (set by AnimationController)
|
|
pulse: float = 0.0
|
|
current_effect: str | None = None
|
|
path_progress: float = 0.0
|
|
|
|
# Viewport
|
|
viewport_width: int = 80
|
|
viewport_height: int = 24
|
|
|
|
# Firehose
|
|
firehose_enabled: bool = False
|
|
|
|
# Runtime state
|
|
frame_number: int = 0
|
|
fps: float = 60.0
|
|
|
|
def get_effect_config(self, name: str) -> tuple[bool, float]:
|
|
"""Get (enabled, intensity) for an effect."""
|
|
enabled = self.effect_enabled.get(name, True)
|
|
intensity = self.effect_intensity.get(name, 1.0)
|
|
return enabled, intensity
|
|
|
|
def set_effect_config(self, name: str, enabled: bool, intensity: float) -> None:
|
|
"""Set effect configuration."""
|
|
self.effect_enabled[name] = enabled
|
|
self.effect_intensity[name] = intensity
|
|
|
|
def is_effect_enabled(self, name: str) -> bool:
|
|
"""Check if an effect is enabled."""
|
|
if name not in self.effect_enabled:
|
|
return True # Default to enabled
|
|
return self.effect_enabled.get(name, True)
|
|
|
|
def get_effect_intensity(self, name: str) -> float:
|
|
"""Get effect intensity (0.0 to 1.0)."""
|
|
return self.effect_intensity.get(name, 1.0)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
"""Convert to dictionary for serialization."""
|
|
return {
|
|
"source": self.source,
|
|
"display": self.display,
|
|
"camera_mode": self.camera_mode,
|
|
"camera_speed": self.camera_speed,
|
|
"effect_order": self.effect_order,
|
|
"effect_enabled": self.effect_enabled.copy(),
|
|
"effect_intensity": self.effect_intensity.copy(),
|
|
"pulse": self.pulse,
|
|
"current_effect": self.current_effect,
|
|
"viewport_width": self.viewport_width,
|
|
"viewport_height": self.viewport_height,
|
|
"firehose_enabled": self.firehose_enabled,
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict[str, Any]) -> "PipelineParams":
|
|
"""Create from dictionary."""
|
|
params = cls()
|
|
for key, value in data.items():
|
|
if hasattr(params, key):
|
|
setattr(params, key, value)
|
|
return params
|
|
|
|
def copy(self) -> "PipelineParams":
|
|
"""Create a copy of this params object."""
|
|
params = PipelineParams()
|
|
params.source = self.source
|
|
params.display = self.display
|
|
params.camera_mode = self.camera_mode
|
|
params.camera_speed = self.camera_speed
|
|
params.camera_x = self.camera_x
|
|
params.effect_order = self.effect_order.copy()
|
|
params.effect_enabled = self.effect_enabled.copy()
|
|
params.effect_intensity = self.effect_intensity.copy()
|
|
params.pulse = self.pulse
|
|
params.current_effect = self.current_effect
|
|
params.path_progress = self.path_progress
|
|
params.viewport_width = self.viewport_width
|
|
params.viewport_height = self.viewport_height
|
|
params.firehose_enabled = self.firehose_enabled
|
|
params.frame_number = self.frame_number
|
|
params.fps = self.fps
|
|
return params
|
|
|
|
|
|
# Default params for different modes
|
|
DEFAULT_HEADLINE_PARAMS = PipelineParams(
|
|
source="headlines",
|
|
display="terminal",
|
|
camera_mode="vertical",
|
|
effect_order=["noise", "fade", "glitch", "firehose"],
|
|
)
|
|
|
|
DEFAULT_PYGAME_PARAMS = PipelineParams(
|
|
source="headlines",
|
|
display="pygame",
|
|
camera_mode="vertical",
|
|
effect_order=["noise", "fade", "glitch", "firehose"],
|
|
)
|
|
|
|
DEFAULT_PIPELINE_PARAMS = PipelineParams(
|
|
source="pipeline",
|
|
display="pygame",
|
|
camera_mode="trace",
|
|
effect_order=[], # No effects for pipeline viz
|
|
)
|