From 37ce32712b8d0bd5d68d62810ca88b14b36ddee6 Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Wed, 24 Apr 2024 14:48:45 +0200 Subject: [PATCH] preview-rendering prototype --- Cargo.toml | 16 +++ src/main.rs | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..480e780 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "fragmental-light-controller" +version = "0.1.0" +edition = "2021" + +[dependencies] +winit = "*" +softbuffer = "0.4.2" +cgmath = "*" +prisma = "0.1.1" +angular-units = "*" + +[dependencies.async-std] +version = "1.9.0" +features = ["unstable", "attributes"] + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0c358e9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,278 @@ +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}, + cgmath::{Point2, Vector2}, + std::time::Duration, + angle::Turns +}; + +trait ColorGrid { + fn get(&self, pos: &Vector2) -> Rgb; +} + +struct TestAnimation { + t: Arc> +} + +fn get_angle(p: &Vector2) -> f32 { + let pi=3.1415926; + let pi2 = 2.0*pi; + + if p.x < 0.0 { + (p.y / p.x).atan() / pi2 + 0.75 + } else if p.x == 0.0 && p.y == 0.0 { + 0.0 + } else { + if p.y < 0.0 { + (-p.x / p.y).atan() / pi2 + } else { + (p.y / p.x).atan() / pi2 + 0.25 + } + } +} + +impl ColorGrid for TestAnimation { + fn get(&self, p: &Vector2) -> Rgb { + let millis = self.t.read().unwrap().as_millis(); + + let p1 = p + Vector2::new(0.0,1.0); + 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; + + Rgb::from_color( + &Hsv::>::new( + Turns( ((millis as f32 / 8000.0) + phi*0.2)%1.0 ), + 0.5 + r2 * 0.5, + phi, + ) + ) + } +} + +struct Fixture { + resolution: Vector2, + position: Vector2, + rotation: f32, + scale: f32, + brightness: f32, + buffer: Vec< Rgb > +} + +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() + }; + 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() + }; + fixture.buffer.resize( + (fixture.resolution.x*fixture.resolution.y) as usize, + Rgb::new(0,0,0) + ); + fixture + } + + 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 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; + } + } + } + } + } + } + } +} + +#[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 mut lighting_setup = LightingSetup::new( + vec![ + Fixture::new_matrix(), + Fixture::new_stripe().offset(Vector2::new(-0.5, 0.0)), + Fixture::new_stripe().offset(Vector2::new(-0.4, 0.0)), + Fixture::new_stripe().offset(Vector2::new( 0.4, 0.0)), + Fixture::new_stripe().offset(Vector2::new( 0.5, 0.0)) + ] + ); + + let tbegin = 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) + )); + + *lighting_setup.t.write().unwrap() = tcur - tbegin; + lighting_setup.update_buffers(); + + 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(); + } + _ => {} + } + + 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(); +}