diff --git a/src/fixture.rs b/src/fixture.rs new file mode 100644 index 0000000..075d330 --- /dev/null +++ b/src/fixture.rs @@ -0,0 +1,90 @@ +use { + prisma::{Rgb}, + cgmath::{Vector2}, + std::boxed::Box, + crate::view::ColorGrid +}; + + + +pub trait FixtureDriver { + fn send(&self, pixels: &Vec>); +} + +pub struct Fixture { + pub resolution: Vector2, + pub position: Vector2, + pub rotation: f32, + pub scale: f32, + pub brightness: f32, + pub buffer: Vec< Rgb >, + pub driver: Option> +} + +impl Fixture { + pub fn new_stripe() -> Self { + let mut fixture = Fixture { + resolution: Vector2::new(1, 72), + position: Vector2::new(0.0, 0.0), + rotation: 0.0, + scale: 0.015, + brightness: 0.8, + buffer: Vec::new(), + driver: None + }; + fixture.buffer.resize( + (fixture.resolution.x*fixture.resolution.y) as usize, + Rgb::new(0,0,0)); + fixture + } + + pub fn new_matrix() -> Self { + let mut fixture = Fixture { + resolution: Vector2::new(15, 20), + position: Vector2::new(0.0, 0.0), + rotation: 0.0, + scale: 0.03, + brightness: 0.8, + buffer: Vec::new(), + driver: None + }; + fixture.buffer.resize( + (fixture.resolution.x*fixture.resolution.y) as usize, + Rgb::new(0,0,0) + ); + fixture + } + + pub fn with_driver(mut self, driver: Box) -> Self { + self.driver = Some(driver); + self + } + + pub fn offset(mut self, offset: Vector2) -> Self { + self.position += offset; + self + } + + pub fn get_global_pos(&self, pixel_pos: &Vector2) -> Vector2 { + let centered_pixpos : Vector2 + = pixel_pos.map(|f| f as f32) - (self.resolution.map(|f| f as f32)/2.0); + self.position + centered_pixpos * self.scale + } + + pub fn update_buffer(&mut self, view: &Box) { + for xi in 0 .. self.resolution.x { + for yi in 0 ..self.resolution.y { + let gpos = self.get_global_pos(&Vector2::new(xi, yi)); + let col = view.get(&gpos); + + let index = xi + yi * self.resolution.x; + self.buffer[index as usize] = Rgb::new( + (col.red() * 255.0 * self.brightness) as u8, + (col.green() * 255.0 * self.brightness) as u8, + (col.blue() * 255.0 * self.brightness) as u8 + ); + } + } + } +} + diff --git a/src/main.rs b/src/main.rs index 4fdc283..f3d0974 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,11 +13,19 @@ use { angle::Turns }; -trait ColorGrid { - fn get(&self, pos: &Vector2) -> Rgb; -} +mod fixture; +mod setup; +mod view; +mod stripe_driver; -struct TestAnimation { +use crate::{ + view::ColorGrid, + fixture::Fixture, + setup::LightingSetup, + stripe_driver::StripeDriver, +}; + +struct Breathing { t: Arc> } @@ -38,242 +46,31 @@ fn get_angle(p: &Vector2) -> f32 { } } -impl ColorGrid for TestAnimation { +impl ColorGrid for Breathing { fn get(&self, p: &Vector2) -> Rgb { let millis = self.t.read().unwrap().as_millis(); - let p1 = p + Vector2::new(0.0,1.0); + 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 : f32 = ( - (millis as f32 / 4000.0) + - ( - 100.0 + (millis%20000) as f32 / 300.0 - ) * get_angle(&p1) - ) % 1.0; + let phi = ( get_angle(&p1) ); + let mirrorphi = if phi < 0.5 { phi } else { 1.0-phi }; + let gamma = ( + (30.0) * + mirrorphi * + ( 0.5+ 0.5*f32::sin(millis as f32 / 4000.0) ) + ) % 1.0; Rgb::from_color( &Hsv::>::new( - Turns( ((millis as f32 / 8000.0) + phi*0.2)%1.0 ), + Turns( 0.25+0.25*f32::sin(millis as f32/8000.0) + gamma*0.5 ), 0.5 + r2 * 0.5, - phi, + mirrorphi, ) ) } } -trait FixtureDriver { - fn send(&self, pixels: &Vec>); -} - -struct Fixture { - resolution: Vector2, - position: Vector2, - rotation: f32, - scale: f32, - brightness: f32, - buffer: Vec< Rgb >, - - driver: Option> -} - -impl Fixture { - pub fn new_stripe() -> Self { - let mut fixture = Fixture { - resolution: Vector2::new(1, 72), - position: Vector2::new(0.0, 0.0), - rotation: 0.0, - scale: 0.01, - brightness: 0.8, - buffer: Vec::new(), - driver: None - }; - fixture.buffer.resize( - (fixture.resolution.x*fixture.resolution.y) as usize, - Rgb::new(0,0,0)); - fixture - } - - pub fn new_matrix() -> Self { - let mut fixture = Fixture { - resolution: Vector2::new(15, 20), - position: Vector2::new(0.0, 0.0), - rotation: 0.0, - scale: 0.03, - brightness: 0.8, - buffer: Vec::new(), - driver: None - }; - fixture.buffer.resize( - (fixture.resolution.x*fixture.resolution.y) as usize, - Rgb::new(0,0,0) - ); - fixture - } - - pub fn with_driver(mut self, driver: Box) -> Self { - self.driver = Some(driver); - self - } - - fn offset(mut self, offset: Vector2) -> Self { - self.position += offset; - self - } - - pub fn get_global_pos(&self, pixel_pos: &Vector2) -> Vector2 { - let centered_pixpos : Vector2 - = pixel_pos.map(|f| f as f32) - (self.resolution.map(|f| f as f32)/2.0); - self.position + centered_pixpos * self.scale - } - - pub fn update_buffer(&mut self, view: &Box) { - for xi in 0 .. self.resolution.x { - for yi in 0 ..self.resolution.y { - let gpos = self.get_global_pos(&Vector2::new(xi, yi)); - let col = view.get(&gpos); - - let index = xi + yi * self.resolution.x; - self.buffer[index as usize] = Rgb::new( - (col.red() * 255.0 * self.brightness) as u8, - (col.green() * 255.0 * self.brightness) as u8, - (col.blue() * 255.0 * self.brightness) as u8 - ); - } - } - } -} - -struct LightingSetup { - fixtures: Vec, - t: Arc>, - - view: Box -} - -impl LightingSetup { - fn new(fixtures: Vec) -> Self { - let t = Arc::new(RwLock::new(Duration::from_millis(0))); - LightingSetup { - fixtures, - t: t.clone(), - view: Box::new( - TestAnimation{ t } - ) - } - } - - fn update_buffers(&mut self) { - for fixture in self.fixtures.iter_mut() { - fixture.update_buffer( &self.view ); - } - } - - fn update_outputs(&mut self) { - for fixture in self.fixtures.iter() { - if let Some(driver) = fixture.driver.as_ref() { - driver.send( &fixture.buffer ); - } - } - } - - fn draw_preview( - &self, - buffer: &mut softbuffer::Buffer<'_, Arc, Arc>, - width: u32, - height: u32 - ) { - // pixels per Unit - let mindim = u32::min(width, height); - let midx = width/2; - let midy = height/2; - - // background - for index in 0..(width * height) { - let y = index / width; - let x = index % width; - - let xf = (x as f32 - width as f32/2.0) / mindim as f32; - let yf = -(y as f32 - height as f32/2.0) / mindim as f32; - - let color = self.view.get(&Vector2::new(xf, yf)); - - let red = (color.red() * 32.0) as u32; - let green = (color.green() * 32.0) as u32; - let blue = (color.blue() * 32.0) as u32; - - buffer[index as usize] = blue | (green << 8) | (red << 16); - } - - for fixture in self.fixtures.iter() { - let pixel_width = fixture.scale * mindim as f32; - - for fx in 0..fixture.resolution.x { - for fy in 0..fixture.resolution.y { - // get global position for pixel - let gpos = fixture.get_global_pos(&Vector2::new(fx, fy)); - - // get start coordinates for pixel - let xi = (midx as f32 + gpos.x*mindim as f32) as u32; - let yi = (midy as f32 - gpos.y*mindim as f32) as u32; - - let col = fixture.buffer[(fx+fy*fixture.resolution.x) as usize]; - let bufval = col.blue() as u32 | ((col.green() as u32) << 8) | ((col.red() as u32) << 16); - - // draw pixel - for xl in 0..(pixel_width*0.6) as u32 { - for yl in 0..(pixel_width*0.6) as u32 { - if (xi+xl) < width && (yi+yl) < height { - let index = (xi+xl) + (yi+yl)*width; - buffer[index as usize] = bufval; - } - } - } - } - } - } - } -} - -struct StripeDriver { - socket: Arc>, - addr: String -} - -impl StripeDriver { - fn new(addr: &str, socket: Arc>) -> Self { - StripeDriver { - addr: addr.into(), - socket - } - } -} - -impl FixtureDriver for StripeDriver { - fn send(&self, pixels: &Vec>) { - const STRIPE_LEN : usize = 72; - let mut buf = [0 as u8; STRIPE_LEN*3]; - - for x in 0 .. STRIPE_LEN { - buf[x*3+0] = pixels[x].green(); - buf[x*3+1] = pixels[x].red(); - buf[x*3+2] = pixels[x].blue(); - } - - self.socket.write().unwrap().send_to(&buf, &self.addr); - - let mut rbuf = [0 as u8; 8]; - match - self.socket.write().unwrap().recv(&mut rbuf) { - Ok(_) => {} - Err(_) => { - eprintln!("missing response from stripe"); - } - } - } -} - #[async_std::main] async fn main() { let event_loop = EventLoop::new().unwrap(); @@ -286,6 +83,8 @@ async fn main() { 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 t = Arc::new(RwLock::new(Duration::from_millis(0))); let mut lighting_setup = LightingSetup::new( vec![ @@ -293,21 +92,25 @@ async fn main() { // .with_driver( Box::new(MatrixTcpDriver::new("ip:port")) ), Fixture::new_stripe() - .with_driver( Box::new(StripeDriver::new("192.168.0.111:4210", socket.clone())) ) +// .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())) ) +// .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())) ) +// .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()))) +// .with_driver( Box::new(StripeDriver::new("192.168.0.114:4210", socket.clone()))) .offset(Vector2::new(0.5, 0.0)) - ] + ], + + Box::new( + Breathing{ t: t.clone() } + ) ); let tbegin = std::time::Instant::now(); @@ -317,9 +120,9 @@ async fn main() { tcur + Duration::from_millis(10) )); - *lighting_setup.t.write().unwrap() = tcur - tbegin; + *t.write().unwrap() = tcur - tbegin; lighting_setup.update_buffers(); - lighting_setup.update_outputs(); + //lighting_setup.update_outputs(); match event { Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested } if window_id == window.id() => { diff --git a/src/setup.rs b/src/setup.rs new file mode 100644 index 0000000..1eca73f --- /dev/null +++ b/src/setup.rs @@ -0,0 +1,99 @@ +use { + crate::{ + fixture::Fixture, + view::ColorGrid + }, + cgmath::Vector2, + std::time::Duration, + std::sync::{Arc, RwLock} +}; + +pub struct LightingSetup { + fixtures: Vec, + t: Arc>, + + view: Box +} + +impl LightingSetup { + pub fn new(fixtures: Vec, view: Box) -> Self { + let t = Arc::new(RwLock::new(Duration::from_millis(0))); + LightingSetup { + fixtures, + t: t.clone(), + view + } + } + + pub fn update_buffers(&mut self) { + for fixture in self.fixtures.iter_mut() { + fixture.update_buffer( &self.view ); + } + } + + pub fn update_outputs(&mut self) { + for fixture in self.fixtures.iter() { + if let Some(driver) = fixture.driver.as_ref() { + driver.send( &fixture.buffer ); + } + } + } + + pub fn draw_preview( + &self, + buffer: &mut softbuffer::Buffer<'_, Arc, Arc>, + width: u32, + height: u32 + ) { + // pixels per Unit + let mindim = u32::min(width, height); + let midx = width/2; + let midy = height/2; + + // background + for index in 0..(width * height) { + let y = index / width; + let x = index % width; + + let xf = (x as f32 - width as f32/2.0) / mindim as f32; + let yf = -(y as f32 - height as f32/2.0) / mindim as f32; + + let color = self.view.get(&Vector2::new(xf, yf)); + + let red = (color.red() * 32.0) as u32; + let green = (color.green() * 32.0) as u32; + let blue = (color.blue() * 32.0) as u32; + + buffer[index as usize] = 0;//blue | (green << 8) | (red << 16); + } + + for fixture in self.fixtures.iter() { + let pixel_width = fixture.scale * mindim as f32; + + for fx in 0..fixture.resolution.x { + for fy in 0..fixture.resolution.y { + // get global position for pixel + let gpos = fixture.get_global_pos(&Vector2::new(fx, fy)); + + // get start coordinates for pixel + let xi = (midx as f32 + gpos.x*mindim as f32) as u32; + let yi = (midy as f32 - gpos.y*mindim as f32) as u32; + + let col = fixture.buffer[(fx+fy*fixture.resolution.x) as usize]; + let bufval = col.blue() as u32 | ((col.green() as u32) << 8) | ((col.red() as u32) << 16); + + // draw pixel + for xl in 0..(pixel_width*0.6) as u32 { + for yl in 0..(pixel_width*0.6) as u32 { + if (xi+xl) < width && (yi+yl) < height { + let index = (xi+xl) + (yi+yl)*width; + buffer[index as usize] = bufval; + } + } + } + } + } + } + } +} + diff --git a/src/stripe_driver.rs b/src/stripe_driver.rs new file mode 100644 index 0000000..2e61950 --- /dev/null +++ b/src/stripe_driver.rs @@ -0,0 +1,44 @@ +use { + prisma::{Rgb}, + std::sync::{Arc, RwLock}, + crate::fixture::FixtureDriver +}; + +pub struct StripeDriver { + socket: Arc>, + addr: String +} + +impl StripeDriver { + fn new(addr: &str, socket: Arc>) -> Self { + StripeDriver { + addr: addr.into(), + socket + } + } +} + +impl FixtureDriver for StripeDriver { + fn send(&self, pixels: &Vec>) { + const STRIPE_LEN : usize = 72; + let mut buf = [0 as u8; STRIPE_LEN*3]; + + for x in 0 .. STRIPE_LEN { + buf[x*3+0] = pixels[x].green(); + buf[x*3+1] = pixels[x].red(); + buf[x*3+2] = pixels[x].blue(); + } + + self.socket.write().unwrap().send_to(&buf, &self.addr); + + let mut rbuf = [0 as u8; 8]; + match + self.socket.write().unwrap().recv(&mut rbuf) { + Ok(_) => {} + Err(_) => { + eprintln!("missing response from stripe"); + } + } + } +} + diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000..e8359c2 --- /dev/null +++ b/src/view.rs @@ -0,0 +1,9 @@ +use { + cgmath::Vector2, + prisma::Rgb +}; + +pub trait ColorGrid { + fn get(&self, pos: &Vector2) -> Rgb; +} +