From 0d53e46c362617e35af572cbf1c8f0d7d60f0f70 Mon Sep 17 00:00:00 2001 From: Connor Slade Date: Wed, 3 Jul 2024 16:27:25 -0400 Subject: [PATCH] Image runs iterator Adds an iterator through the runs in an Image. This is faster than collecting all runs into a vector and looping through that. --- TODO.md | 2 +- common/src/image.rs | 102 ++++++++++++++++++++----------------------- slicer/src/slicer.rs | 23 ++++++---- 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/TODO.md b/TODO.md index a8dafa0..f54f740 100644 --- a/TODO.md +++ b/TODO.md @@ -12,7 +12,7 @@ - [ ] Allow saving / loading projects - [x] Allow deleting objects - [ ] Use instancing both for object mesh storage and rendering -- [ ] Less internal dependence on GOO format +- [x] Less internal dependence on GOO format - [ ] Anti-aliasing - [ ] Cache transformed points? - [x] Rename `ui` module to `mslicer` diff --git a/common/src/image.rs b/common/src/image.rs index 39e5b51..08bcc34 100644 --- a/common/src/image.rs +++ b/common/src/image.rs @@ -9,6 +9,14 @@ pub struct Image { idx: usize, } +pub struct ImageRuns<'a> { + inner: &'a [u8], + + size: u64, + last_value: u8, + last_idx: u64, +} + impl Image { pub fn blank(width: usize, height: usize) -> Self { Self { @@ -34,64 +42,29 @@ impl Image { } pub fn blur(&mut self, sigma: f32) { - // Generate kernel - let kernel_size = (4.0 * sigma) as usize; - let half_kernel_size = kernel_size / 2; - - let mut kernel = vec![0.0; kernel_size]; - for (i, e) in kernel.iter_mut().enumerate().take(kernel_size) { - *e = gaussian((i - half_kernel_size) as f32, sigma); - } - - // Blur image horizontally - for y in 0..self.size.y { - for x in 0..self.size.x { - let sum = (x.saturating_sub(half_kernel_size) - ..(x + half_kernel_size).min(self.size.x)) - .map(|i| self.get_pixel(i, y) as f32 * kernel[i + half_kernel_size]) - .sum::(); - self.set_pixel(x, y, (sum / kernel_size as f32) as u8); - } - } - - // Blur image vertically + let sigma = sigma as usize; for x in 0..self.size.x { for y in 0..self.size.y { - let sum = (y.saturating_sub(half_kernel_size) - ..(y + half_kernel_size).min(self.size.y)) - .map(|i| self.get_pixel(i, y) as f32 * kernel[i + half_kernel_size]) - .sum::(); - self.set_pixel(x, y, (sum / kernel_size as f32) as u8); + let mut sum = 0; + for xp in x.saturating_sub(sigma)..(x + sigma).min(self.size.x) { + for yp in y.saturating_sub(sigma)..(y + sigma).min(self.size.y) { + sum += self.get_pixel(xp, yp); + } + } + + let avg = sum as f32 / (sigma * sigma) as f32; + self.set_pixel(x, y, avg as u8); } } } - // TODO: Turn into iterator - pub fn runs(&self) -> Vec { - let mut last = (self.data[0], 0); - let mut runs = Vec::new(); - - let size = (self.size.x * self.size.y) as u64; - for i in 0..size { - let val = self.data[i as usize]; - - if val != last.0 { - runs.push(Run { - length: i - last.1, - value: last.0, - }); - last = (val, i); - } + pub fn runs(&self) -> ImageRuns { + ImageRuns { + inner: &self.data, + size: (self.size.x * self.size.y) as u64, + last_value: 0, + last_idx: 0, } - - if last.1 + 1 != size { - runs.push(Run { - length: size - last.1, - value: 0, - }); - } - - runs } pub fn finish(&self) -> &[u8] { @@ -103,7 +76,28 @@ impl Image { } } -fn gaussian(x: f32, sigma: f32) -> f32 { - const ROOT_TWO_PI: f32 = 2.506_628_3; - (x.powi(2) / (2.0 * sigma.powi(2))).exp() / (sigma * ROOT_TWO_PI) +impl<'a> Iterator for ImageRuns<'a> { + type Item = Run; + + fn next(&mut self) -> Option { + if self.last_idx >= self.size { + return None; + } + + for i in 0.. { + let idx = self.last_idx + i; + + if idx >= self.size as u64 || self.inner[idx as usize] != self.last_value { + let out = Run { + length: i, + value: self.last_value, + }; + self.last_value = *self.inner.get(idx as usize).unwrap_or(&0); + self.last_idx += i; + return Some(out); + } + } + + unreachable!() + } } diff --git a/slicer/src/slicer.rs b/slicer/src/slicer.rs index 4680017..78c3b04 100644 --- a/slicer/src/slicer.rs +++ b/slicer/src/slicer.rs @@ -8,7 +8,8 @@ use std::{ use common::{ config::SliceConfig, - misc::{EncodableLayer, SliceResult}, + image::Image, + misc::{EncodableLayer, Run, SliceResult}, }; use ordered_float::OrderedFloat; use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -101,24 +102,28 @@ impl Slicer { } } - // let mut image = Image::blank( - // self.slice_config.platform_resolution.x as usize, - // self.slice_config.platform_resolution.y as usize, - // ); - - let mut encoder = Layer::new(); + let mut image = Image::blank( + self.slice_config.platform_resolution.x as usize, + self.slice_config.platform_resolution.y as usize, + ); let mut last = 0; for (start, end) in out { if start > last { - encoder.add_run(start - last, 0); + image.add_run((start - last) as usize, 0); } assert!(end >= start, "End precedes start in layer {layer}"); - encoder.add_run(end - start, 255); + image.add_run((end - start) as usize, 255); last = end; } + let mut encoder = Layer::new(); + + for Run { length, value } in image.runs() { + encoder.add_run(length, value); + } + encoder.finish(layer as usize, slice_config) }) .collect::>();