feat(sim): Right axis = deterministic real-time painterly dream
Replace the pre-baked SD restyle variants (too fluid — the painting re-rolled every keyframe and the flow-warp melted frames) with a deterministic, real-time dream that holds STILL across the loop; only the footage's own motion moves. Design: docs/superpowers/specs/2026-06-22-right-axis-deterministic-dream-design.md Engine (player/alteration.py): Restyle(variant) -> Dream(strength, intensity); Calibration.right_variant_map -> dream_gain. player/state.py: a Right change is a LIVE_UPDATE now, not a crossfade (only a clip swap crossfades). Plan dict restyle -> dream. Tests migrated; 233 green. (No new Python behavior for the client-render swap below.) Renderer (simulator front-end): the Right dream is a WebGL2 Kuwahara shader on a <canvas> over the live video — edge-preserving, so motion/structure stay crisp; the dream is the same footage, just stylized (no blur). Ramped by the knob; max goes "trippy" (vivid saturation + ~45deg hue drift + posterize, concentrated near max via t=intensity^2). Phase-1 luminous-haze (CSS+bloom) was built then retired by eye (blur read as out-of-focus video). Mood grade rides as a CSS filter on the canvas; bloom layer removed. Dev ergonomics that fell out of the iteration: - /dev/version + a 1s client poll = live-reload (open tab never runs stale renderer code; reloads on my edits AND server restarts). - no-cache on the dev server; an on-screen error banner + crash-proof render so a silent throw can't masquerade as "nothing is changing". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,8 @@ from player.alteration import (
|
||||
AnalyticalOverlay,
|
||||
Calibration,
|
||||
ColorGrade,
|
||||
Dream,
|
||||
RenderPlan,
|
||||
Restyle,
|
||||
plan_alteration,
|
||||
render_plan_to_dict,
|
||||
)
|
||||
@@ -23,7 +23,8 @@ def test_all_zero_knobs_is_the_unaltered_base():
|
||||
assert plan.is_identity
|
||||
assert plan.overlay.level == 0
|
||||
assert plan.overlay.intensity == 0.0
|
||||
assert plan.restyle.variant == 0
|
||||
assert plan.dream.strength == 0
|
||||
assert plan.dream.intensity == 0.0
|
||||
assert plan.grade.tone == 0.0
|
||||
assert plan.grade.is_identity
|
||||
|
||||
@@ -32,21 +33,26 @@ def test_left_drives_the_analytical_overlay_only():
|
||||
plan = plan_alteration(_coord(left=4))
|
||||
assert plan.overlay.level == 4
|
||||
assert plan.overlay.intensity == 1.0
|
||||
assert plan.restyle.variant == 0 # Left does not touch the substrate
|
||||
assert plan.dream.strength == 0 # Left does not touch the substrate
|
||||
assert plan.dream.intensity == 0.0
|
||||
assert plan.grade.tone == 0.0
|
||||
|
||||
|
||||
def test_right_selects_a_discrete_restyle_variant_only():
|
||||
def test_right_drives_the_deterministic_dream_only():
|
||||
# Right-axis dream reframe (session 0013): Right is a live deterministic
|
||||
# haze, carried as strength (0..4) + intensity (0..1), not a baked variant.
|
||||
plan = plan_alteration(_coord(right=2))
|
||||
assert plan.restyle.variant == 2
|
||||
assert plan.dream.strength == 2
|
||||
assert plan.dream.intensity == pytest.approx(0.5)
|
||||
assert plan.overlay.level == 0 # Right does not add overlay
|
||||
|
||||
|
||||
def test_left_and_right_stack_not_cancel():
|
||||
# design §4.2: whole-brain corner = dreamlike substrate WITH labels on top
|
||||
# design §4.2: whole-brain corner = dream substrate WITH labels on top
|
||||
plan = plan_alteration(_coord(left=4, right=4))
|
||||
assert plan.overlay.level == 4
|
||||
assert plan.restyle.variant == 4
|
||||
assert plan.dream.strength == 4
|
||||
assert plan.dream.intensity == 1.0
|
||||
|
||||
|
||||
def test_affect_needs_both_knobs_up():
|
||||
@@ -112,7 +118,8 @@ def test_dark_minus_light_sets_intermediate_tone():
|
||||
def test_whole_brain_dark_corner_stacks_grade_substrate_and_overlay():
|
||||
plan = plan_alteration(_coord(left=4, right=2, dark=4, light=0))
|
||||
assert plan.overlay.level == 4
|
||||
assert plan.restyle.variant == 2
|
||||
assert plan.dream.strength == 2
|
||||
assert plan.dream.intensity == pytest.approx(0.5)
|
||||
assert plan.grade.tone == -1.0
|
||||
assert not plan.is_identity
|
||||
|
||||
@@ -124,32 +131,34 @@ def test_default_calibration_is_locked():
|
||||
# locked feel is a deliberate edit here + in alteration.py.
|
||||
assert DEFAULT_CALIBRATION.mood_gain == 1.0
|
||||
assert DEFAULT_CALIBRATION.overlay_gain == 1.0
|
||||
assert DEFAULT_CALIBRATION.right_variant_map == (0, 1, 2, 3, 4)
|
||||
assert DEFAULT_CALIBRATION.dream_gain == 1.0
|
||||
|
||||
|
||||
def test_default_calibration_is_behavior_preserving():
|
||||
# DEFAULT_CALIBRATION must reproduce the original three helpers exactly.
|
||||
# DEFAULT_CALIBRATION reproduces the per-axis helpers exactly.
|
||||
for left in range(5):
|
||||
assert plan_alteration(_coord(left=left)).overlay.intensity == pytest.approx(left / 4)
|
||||
for right in range(5):
|
||||
assert plan_alteration(_coord(right=right)).restyle.variant == right
|
||||
assert plan_alteration(_coord(right=right)).dream.strength == right
|
||||
assert plan_alteration(_coord(right=right)).dream.intensity == pytest.approx(right / 4)
|
||||
for dark in range(5):
|
||||
for light in range(5):
|
||||
expected = (light - dark) / 4
|
||||
assert plan_alteration(_coord(dark=dark, light=light)).grade.tone == pytest.approx(expected)
|
||||
|
||||
|
||||
def test_custom_calibration_scales_mood_and_overlay():
|
||||
cal = Calibration(mood_gain=0.5, overlay_gain=0.5, right_variant_map=(0, 0, 1, 1, 2))
|
||||
def test_custom_calibration_scales_mood_overlay_and_dream():
|
||||
cal = Calibration(mood_gain=0.5, overlay_gain=0.5, dream_gain=0.5)
|
||||
assert plan_alteration(_coord(light=4), cal).grade.tone == pytest.approx(0.5)
|
||||
assert plan_alteration(_coord(left=4), cal).overlay.intensity == pytest.approx(0.5)
|
||||
assert plan_alteration(_coord(right=3), cal).restyle.variant == 1
|
||||
assert plan_alteration(_coord(right=4), cal).dream.intensity == pytest.approx(0.5)
|
||||
|
||||
|
||||
def test_calibration_gain_is_clamped_to_unit_range():
|
||||
cal = Calibration(mood_gain=10.0, overlay_gain=10.0)
|
||||
cal = Calibration(mood_gain=10.0, overlay_gain=10.0, dream_gain=10.0)
|
||||
assert plan_alteration(_coord(light=4), cal).grade.tone == 1.0 # clamped, not 10
|
||||
assert plan_alteration(_coord(left=4), cal).overlay.intensity == 1.0
|
||||
assert plan_alteration(_coord(right=4), cal).dream.intensity == 1.0
|
||||
|
||||
|
||||
def test_render_plan_to_dict_round_trips_the_numbers():
|
||||
@@ -158,7 +167,7 @@ def test_render_plan_to_dict_round_trips_the_numbers():
|
||||
"grade": {"tone": -1.0},
|
||||
"overlay": {"level": 4, "intensity": 1.0},
|
||||
"affect": {"strength": 2, "intensity": 0.5},
|
||||
"restyle": {"variant": 2},
|
||||
"dream": {"strength": 2, "intensity": 0.5},
|
||||
"is_identity": False,
|
||||
}
|
||||
|
||||
|
||||
@@ -59,13 +59,15 @@ def test_overlay_change_is_a_live_update():
|
||||
assert t.playback.plan.overlay.level == 4
|
||||
|
||||
|
||||
def test_restyle_change_crossfades_the_substrate():
|
||||
# design §4.3: the Right v2v substrate is a pre-baked variant swap
|
||||
def test_dream_change_is_a_live_update_not_a_crossfade():
|
||||
# Right-axis dream reframe (session 0013): the Right dream is a deterministic
|
||||
# LIVE filter now, so changing it no longer crossfades — only a clip swap does.
|
||||
p = Player(LIB)
|
||||
p.update(_controls(content="video", right=0))
|
||||
t = p.update(_controls(content="video", right=4))
|
||||
assert t.kind == TransitionKind.CROSSFADE
|
||||
assert t.playback.plan.restyle.variant == 4
|
||||
assert t.kind == TransitionKind.LIVE_UPDATE
|
||||
assert t.playback.plan.dream.strength == 4
|
||||
assert t.playback.plan.dream.intensity == 1.0
|
||||
|
||||
|
||||
def test_volume_only_change_is_a_live_update():
|
||||
|
||||
@@ -38,7 +38,8 @@ def test_alteration_returns_the_engine_plan(client):
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["plan"]["overlay"]["level"] == 4
|
||||
assert data["plan"]["restyle"]["variant"] == 2
|
||||
assert data["plan"]["dream"]["strength"] == 2
|
||||
assert data["plan"]["dream"]["intensity"] == 0.5
|
||||
assert data["plan"]["grade"]["tone"] == -1.0
|
||||
assert data["content"]["video"] is True
|
||||
|
||||
@@ -51,7 +52,7 @@ def test_alteration_honors_off_as_black(client):
|
||||
|
||||
def test_alteration_accepts_calibration(client):
|
||||
body = {"controls": _controls(light=4),
|
||||
"calibration": {"mood_gain": 0.5, "overlay_gain": 1.0, "right_variant_map": [0, 1, 2, 3, 4]}}
|
||||
"calibration": {"mood_gain": 0.5, "overlay_gain": 1.0, "dream_gain": 1.0}}
|
||||
resp = client.post("/api/alteration", json=body)
|
||||
assert resp.json()["plan"]["grade"]["tone"] == 0.5
|
||||
|
||||
|
||||
Reference in New Issue
Block a user