From 37ce32712b8d0bd5d68d62810ca88b14b36ddee6 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
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<f32>) -> Rgb<f32>;
+}
+
+struct TestAnimation {
+    t: Arc<RwLock<Duration>>
+}
+
+fn get_angle(p: &Vector2<f32>) -> 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<f32>) -> Rgb<f32> {
+        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::<f32, Turns<f32>>::new(
+                Turns( ((millis as f32 / 8000.0) + phi*0.2)%1.0 ),
+                0.5 + r2 * 0.5,
+                phi,
+            )
+        )
+    }
+}
+
+struct Fixture {
+    resolution: Vector2<u32>,
+    position: Vector2<f32>,
+    rotation: f32,
+    scale: f32,
+    brightness: f32,
+    buffer: Vec< Rgb<u8> >
+}
+
+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<f32>) -> Self {
+        self.position += offset;
+        self
+    }
+
+    pub fn get_global_pos(&self, pixel_pos: &Vector2<u32>) -> Vector2<f32> {
+        let centered_pixpos : Vector2<f32>
+            = 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<dyn ColorGrid>) {
+        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<Fixture>,
+    t: Arc<RwLock<Duration>>,
+
+    view: Box<dyn ColorGrid>
+}
+
+impl LightingSetup {
+    fn new(fixtures: Vec<Fixture>) -> 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<winit::window::Window>, Arc<winit::window::Window>>,
+        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();
+}