@@ 1,5 1,5 @@
use byteorder::{ByteOrder, LittleEndian};
-use palette::{FromColor, Hsv, RgbHue, Srgb};
+use palette::{FromColor, Hsv, Hue, RgbHue, Shade, Srgb};
use std::{
collections::HashMap,
f32::consts as f32,
@@ 27,13 27,21 @@ struct QueryRgb {
}
#[derive(Clone, Copy, Debug)]
-enum Cmd {
+enum QueryCmd {
All(Srgb),
Off,
Cycle,
Breathing,
}
+#[derive(Clone, Copy)]
+enum Cmd {
+ All,
+ Off,
+ Cycle,
+ Breathing,
+}
+
#[async_std::main]
async fn main() -> tide::Result<()> {
let (heartbeat_tx, heartbeat_rx) = channel();
@@ 72,29 80,30 @@ async fn main() -> tide::Result<()> {
Ok(())
}
-async fn all_handler(req: Request<()>, tx: SyncSender<Cmd>) -> tide::Result {
+async fn all_handler(req: Request<()>, tx: SyncSender<QueryCmd>) -> tide::Result {
let c: QueryRgb = req.query()?;
- tx.send(Cmd::All(Srgb::new(c.r, c.g, c.b))).unwrap();
+ tx.send(QueryCmd::All(Srgb::new(c.r, c.g, c.b))).unwrap();
Ok("cool, ty".into())
}
-async fn proc_handler(req: Request<()>, tx: SyncSender<Cmd>) -> tide::Result {
+async fn proc_handler(req: Request<()>, tx: SyncSender<QueryCmd>) -> tide::Result {
let s = req.param("name")?;
match s {
- "off" => tx.send(Cmd::Off).unwrap(),
- "cycle" => tx.send(Cmd::Cycle).unwrap(),
- "breathing" => tx.send(Cmd::Breathing).unwrap(),
+ "off" => tx.send(QueryCmd::Off).unwrap(),
+ "cycle" => tx.send(QueryCmd::Cycle).unwrap(),
+ "breathing" => tx.send(QueryCmd::Breathing).unwrap(),
_ => {}
}
Ok("cool, ty".into())
}
-fn controller(heartbeat_rx: Receiver<Heartbeat>, cmd_rx: Receiver<Cmd>) {
- let t0 = Instant::now();
+fn controller(heartbeat_rx: Receiver<Heartbeat>, cmd_rx: Receiver<QueryCmd>) {
let sender = Arc::new(UdpSocket::bind("192.168.0.41:7018").unwrap());
let mut clients = HashMap::<SocketAddr, (Instant, Sender<_>)>::new();
let mut tprev = Instant::now();
- let mut cmd = Cmd::All(Srgb::new(0.0, 0.0, 0.0));
+ let mut tcmd = Instant::now();
+ let mut base = Hsv::new(0.0, 0.0, 0.0);
+ let mut cmd = Cmd::Off;
loop {
while let Ok((addr, nleds)) = heartbeat_rx.try_recv() {
match clients.get_mut(&addr) {
@@ 116,11 125,20 @@ fn controller(heartbeat_rx: Receiver<Heartbeat>, cmd_rx: Receiver<Cmd>) {
});
while let Some(new_cmd) = cmd_rx.try_iter().last() {
println!("New command: {:?}", new_cmd);
- cmd = new_cmd;
+ tcmd = Instant::now();
+ match new_cmd {
+ QueryCmd::All(c) => {
+ base = Hsv::from_color(c);
+ cmd = Cmd::All;
+ }
+ QueryCmd::Off => cmd = Cmd::Off,
+ QueryCmd::Cycle => cmd = Cmd::Cycle,
+ QueryCmd::Breathing => cmd = Cmd::Breathing,
+ }
}
for (_, tx) in clients.values() {
- let t = t0.elapsed().as_secs_f64();
- tx.send((cmd, t)).ok();
+ let t = tcmd.elapsed().as_secs_f64();
+ tx.send((cmd, base, t)).ok();
}
let dt = tprev.elapsed();
tprev = Instant::now();
@@ 131,14 149,14 @@ fn controller(heartbeat_rx: Receiver<Heartbeat>, cmd_rx: Receiver<Cmd>) {
}
}
-fn setup_client(addr: SocketAddr, nleds: u16, sender: Arc<UdpSocket>) -> Sender<(Cmd, f64)> {
- let (tx, rx) = channel::<(Cmd, f64)>();
+fn setup_client(addr: SocketAddr, nleds: u16, sender: Arc<UdpSocket>) -> Sender<(Cmd, Hsv, f64)> {
+ let (tx, rx) = channel();
thread::spawn(move || loop {
let magic_cookie = b"satan";
- let (cmd, t) = rx
+ let (cmd, base, t) = rx
.recv()
.expect("Client channel closed. Time for this thread to die!");
- let color_data = lightf(cmd, t, nleds)
+ let color_data = lightf(cmd, base, t, nleds)
.into_iter()
.flat_map(|c| once(c.red).chain(once(c.green)).chain(once(c.blue)))
.map(|x| (x * 256.0).min(256.0).max(0.0) as u8);
@@ 162,25 180,25 @@ fn parse_heartbeat(msg: &[u8; 13], addr: SocketAddr) -> Option<Heartbeat> {
}
}
-fn lightf(cmd: Cmd, t: f64, nleds: u16) -> Vec<Srgb> {
+fn lightf(cmd: Cmd, base: Hsv, t: f64, nleds: u16) -> Vec<Srgb> {
match cmd {
- Cmd::All(c) => vec![c; nleds as usize],
- Cmd::Off => vec![Srgb::new(0.0, 0.0, 0.0); nleds as usize],
+ Cmd::All => vec![Srgb::from_color(base); nleds as usize],
+ Cmd::Off => vec![Srgb::from_color(base.darken(t.min(1.0) as f32)); nleds as usize],
Cmd::Cycle => {
vec![
- Srgb::from_color(Hsv::new(RgbHue::from_radians(t / 2.0), 1.0, 1.0)).into_format();
+ Srgb::from_color(base.shift_hue(hue_f64_to_f32(RgbHue::from_radians(t / 6.0))));
nleds as usize
]
}
Cmd::Breathing => {
vec![
- Srgb::from_color(Hsv::new(
- RgbHue::from_radians(1.9 * f32::PI),
- 1.0,
- 0.5 + 0.4 * (t * 0.8).sin() as f32
- ));
+ Srgb::from_color(base.darken(0.9 * (t / 2.0).sin().powi(2) as f32));
nleds as usize
]
- }
+ } // Cmd::AudioVis => {}
}
}
+
+fn hue_f64_to_f32(h: RgbHue<f64>) -> RgbHue<f32> {
+ RgbHue::from_radians(h.to_radians() as f32)
+}