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:
145
engine/app.py
145
engine/app.py
@@ -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
|
||||
|
||||
@@ -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
123
engine/pipeline_viz.py
Normal 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
|
||||
@@ -40,6 +40,7 @@ run-both = { run = "uv run mainline.py --display both", depends = ["sync-all"] }
|
||||
run-client = { run = "mise run run-both & sleep 2 && $(open http://localhost:8766 2>/dev/null || xdg-open http://localhost:8766 2>/dev/null || echo 'Open http://localhost:8766 manually'); wait", depends = ["sync-all"] }
|
||||
run-demo = { run = "uv run mainline.py --demo --display pygame", depends = ["sync-all"] }
|
||||
run-pipeline = "uv run mainline.py --pipeline-diagram"
|
||||
run-pipeline-demo = { run = "uv run mainline.py --pipeline-demo --display pygame", depends = ["sync-all"] }
|
||||
|
||||
# =====================
|
||||
# Command & Control
|
||||
|
||||
Reference in New Issue
Block a user