gled-core 2.30.2

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! At least one message for every OSC address gled understands, for tests:
//! each must parse, and a running engine must take all of them in a row.

use rosc::{OscMessage, OscType};

/// Stands in for asset ids; handlers ignore ids they do not know.
const FAKE_UUID: &str = "00000000-0000-0000-0000-000000000001";
/// The three ways to address a scene.
const SCENES: [&str; 3] = ["/scene/grid/0/0", "/scene/quick/0", "/scene/selected"];

fn f(value: f32) -> OscType {
    OscType::Float(value)
}

fn b(value: bool) -> OscType {
    OscType::Bool(value)
}

fn i(value: i32) -> OscType {
    OscType::Int(value)
}

fn s(value: &str) -> OscType {
    OscType::String(value.to_owned())
}

/// The messages, in an order that works on a real project: scenes are
/// changed before they are deleted, subscriptions end again.
pub fn sample_messages() -> Vec<OscMessage> {
    let mut messages = Vec::new();
    let mut send = |addr: &str, args: Vec<OscType>| {
        messages.push(OscMessage {
            addr: addr.to_owned(),
            args,
        });
    };

    // Project and timing.
    for value in [0.5, 0.0, 1.0] {
        send("/project/main_dimmer", vec![f(value)]);
    }
    send("/project/blackout", vec![b(true)]);
    send("/project/blackout", vec![i(0)]);
    send("/project/blackout", vec![i(1)]);
    for _ in 0..5 {
        send("/timing/tap", vec![]);
    }
    send("/timing/speed/add", vec![f(0.1)]);
    send("/timing/speed/add", vec![f(-0.2)]);
    for factor in [0.5, 1.0, 1.5, 2.0, 0.75] {
        send("/timing/speed/multiply", vec![f(factor)]);
    }

    // State feedback for OSC controllers.
    send("/osc/state/subscribe", vec![]);
    for row in 0..3 {
        send(&format!("/scene/grid/{row}/0/active"), vec![b(true)]);
    }
    for row in 0..3 {
        send(&format!("/scene/grid/{row}/0/active"), vec![b(false)]);
    }
    send("/osc/state/unsubscribe", vec![]);

    // Scene properties, on all three ways to address a scene.
    for slot in 0..8 {
        send(&format!("/scene/quick/{slot}/opacity"), vec![f(0.5)]);
    }
    send("/scene/grid/7/4/active", vec![b(true)]);
    send("/scene/grid/1/2/toggle", vec![]);
    for scene in SCENES {
        send(&format!("{scene}/opacity"), vec![f(0.3)]);
        send(&format!("{scene}/color"), vec![s("red")]);
        send(&format!("{scene}/name"), vec![s("Sunset")]);
        send(&format!("{scene}/input_dimmer"), vec![f(0.75)]);
        send(&format!("{scene}/ignore_main_dimmer"), vec![b(true)]);
        send(&format!("{scene}/ignore_main_dimmer"), vec![b(false)]);
        send(&format!("{scene}/beat_offset"), vec![f(0.25)]);
        send(&format!("{scene}/active"), vec![b(true)]);
        send(&format!("{scene}/active"), vec![b(false)]);
        send(&format!("{scene}/toggle"), vec![]);
        send(&format!("{scene}/set_offset_on_flash"), vec![b(true)]);
        send(&format!("{scene}/set_offset_on_flash"), vec![b(false)]);
        for field in ["opacity", "beat_offset"] {
            send(&format!("{scene}/{field}/value"), vec![f(0.75)]);
            send(&format!("{scene}/{field}/static"), vec![f(0.65)]);
            send(&format!("{scene}/{field}/curve"), vec![s(FAKE_UUID)]);
            send(&format!("{scene}/{field}/curve"), vec![s("none")]);
        }
        for input in ["activation_input", "flash_input", "dimmer_input"] {
            send(&format!("{scene}/{input}/artnet"), vec![i(42)]);
            send(&format!("{scene}/{input}/clear"), vec![]);
        }
        for palette in ["inherit", "none", FAKE_UUID] {
            send(&format!("{scene}/palette_overwrite"), vec![s(palette)]);
        }
        send(
            &format!("{scene}/palette_overwrite/primary"),
            vec![f(0.1), f(0.2), f(0.3)],
        );
        send(
            &format!("{scene}/palette_overwrite/secondary"),
            vec![f(0.4), f(0.5), f(0.6)],
        );
        send(
            &format!("{scene}/palette_overwrite/gradient/2"),
            vec![f(0.7), f(0.8), f(0.9)],
        );
        send(&format!("{scene}/groups_overwrite/0"), vec![s("wash")]);
        send(&format!("{scene}/groups_overwrite/1"), vec![s("spot")]);
        send(&format!("{scene}/groups_overwrite/remove/1"), vec![]);
        send(&format!("{scene}/groups_overwrite/clear"), vec![]);
    }
    for color in ["green", "blue", "orange", "yellow", "purple", "pink"] {
        send("/scene/quick/1/color", vec![s(color)]);
    }

    // Effects: add one everywhere, change every property, then clone and
    // delete.
    for scene in SCENES {
        send(&format!("{scene}/effect/add"), vec![]);
        let effect = format!("{scene}/effect/0");
        for (field, value) in [
            ("opacity", 0.5),
            ("color_shift", 0.25),
            ("beat_progression", 0.1),
            ("beat_offset", 0.5),
        ] {
            send(&format!("{effect}/{field}"), vec![f(value)]);
            send(&format!("{effect}/{field}/value"), vec![f(value)]);
            send(&format!("{effect}/{field}/static"), vec![f(value)]);
            send(&format!("{effect}/{field}/curve"), vec![s(FAKE_UUID)]);
            send(&format!("{effect}/{field}/curve"), vec![s("none")]);
        }
        send(&format!("{effect}/speed_exponent"), vec![i(1)]);
        send(&format!("{effect}/speed_exponent"), vec![i(-1)]);
        send(&format!("{effect}/group_index"), vec![i(0)]);
        send(&format!("{effect}/animation"), vec![s("none")]);
        send(&format!("{effect}/animation"), vec![s(FAKE_UUID)]);
        send(&format!("{effect}/config/u32/0"), vec![i(3)]);
        send(&format!("{effect}/config/f32/0"), vec![f(0.3)]);
        send(&format!("{effect}/clone"), vec![]);
        send(&format!("{effect}/delete"), vec![]);
    }

    // Project settings.
    // Only scenes inherit a palette.
    for palette in ["none", FAKE_UUID] {
        send("/project/palette", vec![s(palette)]);
    }
    send("/project/palette/primary", vec![f(0.9), f(0.1), f(0.1)]);
    send("/project/palette/secondary", vec![f(0.1), f(0.9), f(0.1)]);
    for index in 0..4 {
        send(
            &format!("/project/palette/gradient/{index}"),
            vec![f(0.5), f(0.5), f(0.5)],
        );
    }
    send("/project/artnet_control/active", vec![b(false)]);
    send("/project/artnet_control/active", vec![b(true)]);
    send("/project/artnet_control/universe", vec![i(0)]);
    send("/project/groups/0", vec![s("wash")]);
    send("/project/groups/1", vec![s("spot")]);
    send("/project/groups/remove/1", vec![]);
    send("/project/groups/clear", vec![]);
    for event in ["tap", "blackout", "blackout_hold", "half", "double"] {
        send(&format!("/project/input/{event}/add/artnet"), vec![i(10)]);
        send(
            &format!("/project/input/{event}/remove/artnet"),
            vec![i(10)],
        );
        send(&format!("/project/input/{event}/clear"), vec![]);
    }

    // Animation assets (an unknown one, so nothing changes).
    let animation = format!("/asset/animation/{FAKE_UUID}");
    send(
        &format!("{animation}/shader_code"),
        vec![s("void main() {}")],
    );
    send(&format!("{animation}/argument/add"), vec![]);
    for kind in [
        "center",
        "selection",
        "slider",
        "checkbox",
        "percentage",
        "degrees",
    ] {
        send(&format!("{animation}/argument/0/kind"), vec![s(kind)]);
    }
    send(&format!("{animation}/argument/0/name"), vec![s("speed")]);
    send(&format!("{animation}/argument/0/remove"), vec![]);

    // Scene management last: selection, copies, a new scene, reordering.
    send("/scene/grid/1/1/select", vec![]);
    send("/scene/quick/2/select", vec![]);
    send("/scene/grid/6/4/clone", vec![]);
    send("/scene/grid/6/4/delete", vec![]);
    send("/scene/grid/7/4/add", vec![s(FAKE_UUID)]);
    send("/scene/reorder/0/0/1/0", vec![]);
    send("/scene/reorder/1/0/0/0", vec![]);

    messages
}