408 lines
13 KiB
Rust
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();
|
|
}
|