light-control/src/main.rs
2024-04-27 11:30:10 +02:00

408 lines
13 KiB
Rust

extern crate angular_units as angle;
use {
std::num::NonZeroU32,
std::sync::{Arc, RwLock, Mutex},
std::rc::Rc,
winit::event::{Event, WindowEvent},
winit::event_loop::{ControlFlow, EventLoop},
winit::window::WindowBuilder,
prisma::{Rgb,Hsv,FromColor, Lerp},
cgmath::{Point2, Vector2},
std::time::Duration,
angle::Turns
};
mod util;
mod fixture;
mod setup;
mod view;
mod stripe_driver;
mod jack;
use crate::{
view::ColorGrid,
fixture::Fixture,
setup::LightingSetup,
stripe_driver::StripeDriver,
util::get_angle
};
#[derive(Clone)]
pub struct Inputs {
t: Duration,
transition_time: Duration,
scene_select: usize,
intensity: f32,
cycle_len: Duration,
wave_peak: f32
}
struct Breathing { inputs: Arc<RwLock< Inputs >> }
impl ColorGrid for Breathing {
fn get(&self, p: &Vector2<f32>) -> Rgb<f32> {
let inputs = self.inputs.read().unwrap().clone();
let millis = inputs.t.as_millis();
let p1 = p + Vector2::new(0.0,0.5);
let r2 = p1.x*p1.x + p1.y*p1.y;
let v = if r2 > 1.0 { 1.0 } else { r2 };
let phi = ( get_angle(&p1) );
let mirrorphi = if phi < 0.5 { phi } else { 1.0-phi };
let gamma =
(
(5.0) *
mirrorphi *
(
0.5+ 0.5*f32::sin(
inputs.t.as_millis() as f32
/ inputs.cycle_len.as_millis() as f32
)
)
) % 1.0;
Rgb::from_color(
&Hsv::<f32, Turns<f32>>::new(
Turns( 0.25+0.25*f32::sin(millis as f32/8000.0) + gamma*0.5 ),
0.5 + r2 * 0.5,
inputs.wave_peak,
)
)
}
}
struct PastelFade { inputs: Arc<RwLock< Inputs >> }
impl ColorGrid for PastelFade {
fn get(&self, p: &Vector2<f32>) -> Rgb<f32> {
let inputs = self.inputs.read().unwrap().clone();
// let millis = self
let i = ( inputs.t.as_millis() as f32 / (4.0*inputs.cycle_len.as_millis() as f32) ) % 1.0;
Rgb::from_color(
&Hsv::<f32, Turns<f32>>::new(
Turns( i ),
0.5,
(4.0*i) %1.0
)
)
}
}
struct UbootPrüfstandFade { inputs: Arc<RwLock< Inputs >> }
impl ColorGrid for UbootPrüfstandFade {
fn get(&self, p: &Vector2<f32>) -> Rgb<f32> {
let inputs = self.inputs.read().unwrap().clone();
// let millis = self
let i = ( inputs.t.as_millis() as f32 / (4.0*inputs.cycle_len.as_millis() as f32) ) % 1.0;
let pi2 = 2.0 * 3.1415926;
let col1 =
Rgb::from_color(
&Hsv::<f32, Turns<f32>>::new(
Turns( 0.65 ),
0.9,
0.5 + 0.5*f32::sin(i*pi2),//(4.0*i) %1.0
)
);
let col2 = Rgb::new(0.5+0.5*f32::sin(i*pi2), 0.0, 0.0);
let p = ( inputs.t.as_millis() as f32 / (32.0*inputs.cycle_len.as_millis() as f32)) % 1.0;
if p >= 0.7 {
col2
} else {
col1
}
}
}
struct ArcticRain { inputs: Arc<RwLock< Inputs >> }
impl ColorGrid for ArcticRain {
fn get(&self, p: &Vector2<f32>) -> Rgb<f32> {
let inputs = self.inputs.read().unwrap().clone();
// let millis = self
let i = ( inputs.t.as_millis() as f32 / (4.0*inputs.cycle_len.as_millis() as f32) ) % 1.0;
let pi2 = 2.0 * 3.1415926;
let col1 =
Rgb::from_color(
&Hsv::<f32, Turns<f32>>::new(
Turns( 0.65 ),
0.9,
1.0 - ((i + (2.0-p.y/4.0)) * 12.0) % 1.0,
// (f32::sin(-p.y+i) * 12.0) % 1.0,
)
);
col1
}
}
enum Waveform {
Sawtooth{ pulse_width: Duration },
Square{ },
}
struct Strobe {
inputs: Arc<RwLock< Inputs >>,
//waveform: Waveform,
subdivision: u32,
}
impl ColorGrid for Strobe {
fn get(&self, p: &Vector2<f32>) -> Rgb<f32> {
let inputs = self.inputs.read().unwrap().clone();
let t = (self.subdivision as f32 * inputs.t.as_millis() as f32 / inputs.cycle_len.as_millis() as f32) % 1.0;
if t < 0.6 {
Rgb::new(0.6, 0.6, 0.6)
} else {
Rgb::new(0.0, 0.0, 0.0)
}
}
}
pub struct SceneLibrary {
library: Vec< Box<dyn ColorGrid> >,
current_scene: RwLock<usize>,
inputs: Arc<RwLock< Inputs >>,
transition_length: Duration,
// transition_curve: enum { Constant, Linear, Sigmoid, Sinus }
}
impl SceneLibrary {
pub fn new( inputs: Arc<RwLock<Inputs>> ) -> Self {
SceneLibrary {
library: vec![
// 0
Box::new( Breathing{ inputs: inputs.clone() } ),
// 1 - 4
Box::new( Strobe{ inputs: inputs.clone(), subdivision: 4 } ),
Box::new( Strobe{ inputs: inputs.clone(), subdivision: 8 } ),
Box::new( Strobe{ inputs: inputs.clone(), subdivision: 12 } ),
Box::new( Strobe{ inputs: inputs.clone(), subdivision: 16 } ),
// others
Box::new( PastelFade{ inputs: inputs.clone() } ),
Box::new( UbootPrüfstandFade{ inputs: inputs.clone() } ),
Box::new( ArcticRain{ inputs: inputs.clone() } ),
],
current_scene: RwLock::new(0),
inputs,
transition_length: Duration::from_millis(200),
}
}
}
impl ColorGrid for SceneLibrary {
fn get(&self, p: &Vector2<f32>) -> Rgb<f32> {
let inputs = self.inputs.read().unwrap().clone();
let sc = *self.current_scene.read().unwrap();
if inputs.scene_select == sc {
// display only one animation
self.library[ sc ].get(p)
} else {
// display transition
let cur_scene = &self.library[ sc ];
let nxt_scene = &self.library[ inputs.scene_select ];
// crossfade param
let w = ( inputs.transition_time.as_millis() as f32 / self.transition_length.as_millis() as f32 );
if w >= 1.0 {
// change own state:
*self.current_scene.write().unwrap() = inputs.scene_select;
}
cur_scene.get(p)
.lerp(
&nxt_scene.get(p),
w
)
}
}
}
#[async_std::main]
async fn main() {
let event_loop = EventLoop::new().unwrap();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
let context = softbuffer::Context::new(window.clone()).unwrap();
let mut surface = Arc::new(Mutex::new(softbuffer::Surface::new(&context, window.clone()).unwrap()));
let dim = Arc::new(Mutex::new((1 as u32,1 as u32)));
let socket = Arc::new(RwLock::new(std::net::UdpSocket::bind("0.0.0.0:4210").expect("failed to bind UDP socket")));
socket.write().unwrap().set_read_timeout(Some(std::time::Duration::from_millis(500)));
socket.write().unwrap().set_write_timeout(Some(std::time::Duration::from_millis(50)));
let inputs = Arc::new(RwLock::new(
Inputs {
t: Duration::from_millis(0),
transition_time: Duration::from_millis(0),
intensity: 0.5,
cycle_len: Duration::from_millis(300),
wave_peak: 0.5,
scene_select: 0
}
));
let mut lighting_setup = LightingSetup::new(
vec![
Fixture::new_matrix(),
// .with_driver( Box::new(MatrixTcpDriver::new("ip:port")) ),
Fixture::new_stripe()
.with_driver( Box::new(StripeDriver::new("192.168.0.111:4210", socket.clone())) )
.offset(Vector2::new(-0.5, 0.0)),
Fixture::new_stripe()
// .with_driver( Box::new(StripeDriver::new("192.168.0.112:4210", socket.clone())) )
.offset(Vector2::new(-0.4, 0.0)),
Fixture::new_stripe()
.with_driver( Box::new(StripeDriver::new("192.168.0.113:4210", socket.clone())) )
.offset(Vector2::new(0.4, 0.0)),
Fixture::new_stripe()
// .with_driver( Box::new(StripeDriver::new("192.168.0.114:4210", socket.clone())))
.offset(Vector2::new(0.5, 0.0))
],
Box::new( SceneLibrary::new(inputs.clone()) )
);
let mut tbegin = std::time::Instant::now();
let mut transition_begin = std::time::Instant::now();
let mut last_tap = std::time::Instant::now();
event_loop.run(move |event, elwt| {
let tcur = std::time::Instant::now();
elwt.set_control_flow(ControlFlow::WaitUntil(
tcur + Duration::from_millis(10)
));
inputs.write().unwrap().t = tcur - tbegin;
inputs.write().unwrap().transition_time = tcur - transition_begin;
lighting_setup.update_buffers();
lighting_setup.update_outputs();
match event {
Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested } if window_id == window.id() => {
let (width, height) = {
let size = window.inner_size();
(size.width, size.height)
};
*dim.lock().unwrap() = (width, height);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => {
elwt.exit();
}
winit::event::Event::WindowEvent{
window_id: _,
event: winit::event::WindowEvent::KeyboardInput{ device_id, event, is_synthetic }
} => {
if event.state == winit::event::ElementState::Pressed {
match event.logical_key {
winit::keyboard::Key::Character(c) => {
eprintln!("pressed {}", c);
match &c[0..1] {
"x" => {
// tap tempo
let old_tap = last_tap;
last_tap = std::time::Instant::now();
if (last_tap - old_tap) < Duration::from_millis(20000) {
inputs.write().unwrap().cycle_len = last_tap - old_tap;
}
}
"q" => {
// sync
inputs.write().unwrap().t = Duration::from_millis(0);
tbegin = std::time::Instant::now();
},
"0" => {
inputs.write().unwrap().scene_select = 0;
transition_begin = std::time::Instant::now();
}
"1" => {
inputs.write().unwrap().scene_select = 1;
transition_begin = std::time::Instant::now();
}
"2" => {
inputs.write().unwrap().scene_select = 2;
transition_begin = std::time::Instant::now();
}
"3" => {
inputs.write().unwrap().scene_select = 3;
transition_begin = std::time::Instant::now();
}
"4" => {
inputs.write().unwrap().scene_select = 4;
transition_begin = std::time::Instant::now();
}
"5" => {
inputs.write().unwrap().scene_select = 5;
transition_begin = std::time::Instant::now();
},
"6" => {
inputs.write().unwrap().scene_select = 6;
transition_begin = std::time::Instant::now();
}
"7" => {
inputs.write().unwrap().scene_select = 7;
transition_begin = std::time::Instant::now();
}
_=>{}
}
}
_ => {}
}
}
}
_ => {}
}
let (width, height) = *dim.lock().unwrap();
surface.lock().unwrap()
.resize(
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
)
.unwrap();
if let Ok(mut buf) = surface.lock().unwrap().buffer_mut() {
lighting_setup.draw_preview( &mut buf, width, height );
buf.present().unwrap();
}
}).unwrap();
}