photonic_effects/nodes/
alert.rs

1use std::ops::Neg;
2
3use anyhow::Result;
4use palette::Hsv;
5
6use photonic::{math, Attr, BoundAttrDecl, Buffer, FreeAttrDecl, Node, NodeBuilder, NodeDecl, RenderContext};
7
8pub struct Alert<Hue, Block, Speed> {
9    pub hue: Hue,
10    pub block: Block,
11    pub speed: Speed,
12}
13
14pub struct AlertNode<Hue, Block, Speed> {
15    hue: Hue,
16    block: Block,
17    speed: Speed,
18
19    time: f32,
20}
21
22impl<Hue, Block, Speed> NodeDecl for Alert<Hue, Block, Speed>
23where
24    Hue: BoundAttrDecl<f32>,
25    Block: BoundAttrDecl<i64>,
26    Speed: FreeAttrDecl<f32>, // TODO: Make speed an attr of duration
27{
28    const KIND: &'static str = "alert";
29
30    type Node = AlertNode<Hue::Attr, Block::Attr, Speed::Attr>;
31
32    async fn materialize(self, builder: &mut NodeBuilder<'_>) -> Result<Self::Node> {
33        return Ok(Self::Node {
34            hue: builder.bound_attr("hue", self.hue, (0.0, 360.0))?,
35            block: builder.bound_attr("block", self.block, (0, builder.size as i64))?,
36            speed: builder.unbound_attr("speed", self.speed)?,
37
38            time: 0.0,
39        });
40    }
41}
42
43impl<Hue, Block, Speed> Node for AlertNode<Hue, Block, Speed>
44where
45    Hue: Attr<f32>,
46    Block: Attr<i64>,
47    Speed: Attr<f32>,
48{
49    type Element = Hsv;
50
51    fn update(&mut self, ctx: &RenderContext, out: &mut Buffer<Self::Element>) -> Result<()> {
52        let hue = self.hue.update(ctx);
53        let block = self.block.update(ctx);
54        let speed = self.speed.update(ctx);
55
56        self.time += ctx.duration.as_secs_f32() / speed;
57        self.time %= 2.0;
58
59        let value = math::clamp(f32::sin(self.time * std::f32::consts::PI), (-1.0, 1.0));
60
61        out.update(|i, _| {
62            let x = (i / block as usize) % 2 == 0;
63
64            return Hsv::new(hue, 1.0, if x { value } else { value.neg() }.max(0.0));
65        });
66
67        return Ok(());
68    }
69}
70
71#[cfg(feature = "dynamic")]
72pub mod dynamic {
73    use palette::rgb::Rgb;
74    use serde::Deserialize;
75
76    use photonic::boxed::{BoxedBoundAttrDecl, BoxedFreeAttrDecl, DynNodeDecl};
77    use photonic_dynamic::factory::Producible;
78    use photonic_dynamic::registry::Registry;
79    use photonic_dynamic::{builder, config};
80
81    use super::*;
82
83    #[derive(Deserialize, Debug)]
84    pub struct Config {
85        pub hue: config::Attr<f32>,
86        pub block: config::Attr<i64>,
87        pub speed: config::Attr<f32>,
88    }
89
90    impl Producible<dyn DynNodeDecl<Rgb>> for Config {
91        type Product = Alert<BoxedBoundAttrDecl<f32>, BoxedBoundAttrDecl<i64>, BoxedFreeAttrDecl<f32>>;
92
93        fn produce<Reg: Registry>(config: Self, mut builder: builder::NodeBuilder<'_, Reg>) -> Result<Self::Product> {
94            return Ok(Alert {
95                hue: builder.bound_attr("hue", config.hue)?,
96                block: builder.bound_attr("block", config.block)?,
97                speed: builder.free_attr("speed", config.speed)?,
98            });
99        }
100    }
101}