feat(demo): add pipeline visualization demo mode

- Add --pipeline-demo flag for ASCII pipeline animation
- Create engine/pipeline_viz.py with animated pipeline graphics
- Shows data flow, camera modes, FPS counter
- Run with: python mainline.py --pipeline-demo --display pygame
This commit is contained in:
2026-03-16 02:04:53 -07:00
parent c2d77ee358
commit a1dcceac47
4 changed files with 270 additions and 0 deletions

View File

@@ -558,6 +558,147 @@ def run_demo_mode():
print("\n \033[38;5;245mDemo ended\033[0m")
def run_pipeline_demo():
"""Run pipeline visualization demo mode - shows ASCII pipeline animation."""
import time
from engine import config
from engine.camera import Camera, CameraMode
from engine.display import DisplayRegistry
from engine.effects import (
EffectContext,
PerformanceMonitor,
get_effect_chain,
get_registry,
set_monitor,
)
from engine.pipeline_viz import generate_animated_pipeline
print(" \033[1;38;5;46mMAINLINE PIPELINE DEMO\033[0m")
print(" \033[38;5;245mInitializing...\033[0m")
import effects_plugins
effects_plugins.discover_plugins()
registry = get_registry()
chain = get_effect_chain()
chain.set_order(["noise", "fade", "glitch", "firehose", "hud"])
monitor = PerformanceMonitor()
set_monitor(monitor)
chain._monitor = monitor
display = DisplayRegistry.create("pygame")
if not display:
print(" \033[38;5;196mFailed to create pygame display\033[0m")
sys.exit(1)
w, h = 80, 24
display.init(w, h)
display.clear()
camera = Camera.vertical(speed=1.0)
effects_to_demo = ["noise", "fade", "glitch", "firehose"]
effect_idx = 0
effect_name = effects_to_demo[effect_idx]
effect_start_time = time.time()
current_intensity = 0.0
ramping_up = True
camera_modes = [
(CameraMode.VERTICAL, "vertical"),
(CameraMode.HORIZONTAL, "horizontal"),
(CameraMode.OMNI, "omni"),
(CameraMode.FLOATING, "floating"),
]
camera_mode_idx = 0
camera_start_time = time.time()
frame_number = 0
print(" \033[38;5;82mStarting pipeline visualization...\033[0m")
print(" \033[38;5;245mPress Ctrl+C to exit\033[0m\n")
try:
while True:
elapsed = time.time() - effect_start_time
camera_elapsed = time.time() - camera_start_time
duration = config.DEMO_EFFECT_DURATION
if elapsed >= duration:
effect_idx = (effect_idx + 1) % len(effects_to_demo)
effect_name = effects_to_demo[effect_idx]
effect_start_time = time.time()
elapsed = 0
current_intensity = 0.0
ramping_up = True
if camera_elapsed >= duration * 2:
camera_mode_idx = (camera_mode_idx + 1) % len(camera_modes)
mode, mode_name = camera_modes[camera_mode_idx]
camera = Camera(mode=mode, speed=1.0)
camera_start_time = time.time()
camera_elapsed = 0
progress = elapsed / duration
if ramping_up:
current_intensity = progress
if progress >= 1.0:
ramping_up = False
else:
current_intensity = 1.0 - progress
for effect in registry.list_all().values():
if effect.name == effect_name:
effect.config.enabled = True
effect.config.intensity = current_intensity
elif effect.name not in ("hud",):
effect.config.enabled = False
hud_effect = registry.get("hud")
if hud_effect:
mode_name = camera_modes[camera_mode_idx][1]
hud_effect.config.params["display_effect"] = (
f"{effect_name} / {mode_name}"
)
hud_effect.config.params["display_intensity"] = current_intensity
camera.update(config.FRAME_DT)
buf = generate_animated_pipeline(w, frame_number)
ctx = EffectContext(
terminal_width=w,
terminal_height=h,
scroll_cam=camera.y,
ticker_height=h,
camera_x=camera.x,
mic_excess=0.0,
grad_offset=0.0,
frame_number=frame_number,
has_message=False,
items=[],
)
result = chain.process(buf, ctx)
display.show(result)
new_w, new_h = display.get_dimensions()
if new_w != w or new_h != h:
w, h = new_w, new_h
frame_number += 1
time.sleep(1 / 60)
except KeyboardInterrupt:
pass
finally:
display.cleanup()
print("\n \033[38;5;245mPipeline demo ended\033[0m")
def main():
from engine import config
from engine.pipeline import generate_pipeline_diagram
@@ -566,6 +707,10 @@ def main():
print(generate_pipeline_diagram())
return
if config.PIPELINE_DEMO:
run_pipeline_demo()
return
if config.DEMO:
run_demo_mode()
return

View File

@@ -244,6 +244,7 @@ WEBSOCKET_PORT = _arg_int("--websocket-port", 8765)
# ─── DEMO MODE ────────────────────────────────────────────
DEMO = "--demo" in sys.argv
DEMO_EFFECT_DURATION = 5.0 # seconds per effect
PIPELINE_DEMO = "--pipeline-demo" in sys.argv
# ─── PIPELINE DIAGRAM ────────────────────────────────────
PIPELINE_DIAGRAM = "--pipeline-diagram" in sys.argv

123
engine/pipeline_viz.py Normal file
View File

@@ -0,0 +1,123 @@
"""
Pipeline visualization - ASCII text graphics showing the render pipeline.
"""
def generate_pipeline_visualization(width: int = 80, height: int = 24) -> list[str]:
"""Generate ASCII visualization of the pipeline.
Args:
width: Width of the visualization in characters
height: Height in lines
Returns:
List of formatted strings representing the pipeline
"""
lines = []
for y in range(height):
line = ""
if y == 1:
line = "" + "" * (width - 2) + ""
elif y == 2:
line = "" + " RENDER PIPELINE ".center(width - 2) + ""
elif y == 3:
line = "" + "" * (width - 2) + ""
elif y == 5:
line = "║ SOURCES ══════════════> FETCH ═════════> SCROLL ═══> EFFECTS ═> DISPLAY"
elif y == 6:
line = "║ │ │ │ │"
elif y == 7:
line = "║ RSS Poetry Camera Terminal"
elif y == 8:
line = "║ Ntfy Cache Noise WebSocket"
elif y == 9:
line = "║ Mic Fade Pygame"
elif y == 10:
line = "║ Glitch Sixel"
elif y == 11:
line = "║ Firehose Kitty"
elif y == 12:
line = "║ Hud"
elif y == 14:
line = "" + "" * (width - 2) + ""
elif y == 15:
line = "║ CAMERA MODES "
remaining = width - len(line) - 1
line += (
"" * (remaining // 2 - 7)
+ " VERTICAL "
+ "" * (remaining // 2 - 6)
+ ""
)
elif y == 16:
line = (
""
+ "".center(8)
+ " "
+ "".center(8)
+ " "
+ "".center(8)
+ " "
+ "".center(8)
+ " " * 20
+ ""
)
elif y == 17:
line = (
"║ scroll up scroll left diagonal bobbing "
+ " " * 16
+ ""
)
elif y == 19:
line = "" + "" * (width - 2) + ""
elif y == 20:
fps = "60"
line = (
f"║ FPS: {fps} │ Frame: 16.7ms │ Effects: 5 active │ Camera: VERTICAL "
+ " " * (width - len(line) - 2)
+ ""
)
elif y == 21:
line = "" + "" * (width - 2) + ""
else:
line = " " * width
lines.append(line)
return lines
def generate_animated_pipeline(width: int = 80, frame: int = 0) -> list[str]:
"""Generate animated ASCII visualization.
Args:
width: Width of the visualization
frame: Animation frame number
Returns:
List of formatted strings
"""
lines = generate_pipeline_visualization(width, 20)
anim_chars = ["", "", "", " ", "", "", ""]
char = anim_chars[frame % len(anim_chars)]
for i, line in enumerate(lines):
if "Effects" in line:
lines[i] = line.replace("" * 5, char * 5)
if "FPS:" in line:
lines[i] = (
f"║ FPS: {60 - frame % 10} │ Frame: {16 + frame % 5:.1f}ms │ Effects: {5 - (frame % 3)} active │ Camera: {['VERTICAL', 'HORIZONTAL', 'OMNI', 'FLOATING'][frame % 4]} "
+ " " * (80 - len(lines[i]) - 2)
+ ""
)
return lines