diff --git a/Cargo.lock b/Cargo.lock index 5249834..0323744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,7 +139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cc836ad7a40dc9d8049574e2a29979f5dc77deeea4d7ebcd29773452f0e9694" dependencies = [ "approx 0.3.2", - "libm", + "libm 0.1.4", "num-complex 0.2.4", "num-traits", ] @@ -2044,8 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -2399,6 +2401,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "imageproc" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2393fb7808960751a52e8a154f67e7dd3f8a2ef9bd80d1553078a7b4e8ed3f0d" +dependencies = [ + "ab_glyph", + "approx 0.5.1", + "getrandom", + "image 0.25.1", + "itertools 0.12.1", + "nalgebra 0.32.6", + "num 0.4.3", + "rand 0.8.5", + "rand_distr", + "rayon", +] + [[package]] name = "imgref" version = "1.10.1" @@ -2633,6 +2653,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.0.2" @@ -2844,6 +2870,7 @@ dependencies = [ "encase", "goo_format", "image 0.25.1", + "imageproc", "nalgebra 0.32.6", "notify-rust", "open", @@ -3052,6 +3079,20 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.5", + "num-complex 0.4.6", + "num-integer", + "num-iter", + "num-rational 0.4.2", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -3159,6 +3200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg 1.3.0", + "libm 0.2.8", ] [[package]] @@ -3544,7 +3586,7 @@ dependencies = [ "fool", "itertools 0.8.2", "nalgebra 0.17.3", - "num", + "num 0.2.1", "smallvec 0.6.14", "typenum", ] @@ -3841,6 +3883,16 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "rand_hc" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f1df105..68062e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ egui-phosphor = { git = "https://github.com/connorslade/egui-phosphor" } egui-wgpu = "0.27.2" encase = { version = "0.8.0", features = ["nalgebra"] } image = "0.25.1" +imageproc = "0.25.0" md5 = "0.7.0" nalgebra = "0.32.6" notify-rust = "4.11.0" @@ -40,4 +41,4 @@ stl_io = "0.7.0" toml = "0.8.16" tracing = "0.1.40" tracing-subscriber = "0.3.18" -wgpu = "0.19.4" \ No newline at end of file +wgpu = "0.19.4" diff --git a/common/src/image.rs b/common/src/image.rs index 886ed0e..a2ba83e 100644 --- a/common/src/image.rs +++ b/common/src/image.rs @@ -33,6 +33,14 @@ impl Image { image } + pub fn from_raw(width: usize, height: usize, data: Vec) -> Self { + Self { + size: Vector2::new(width, height), + data, + idx: 0, + } + } + pub fn add_run(&mut self, length: usize, value: u8) { self.data[self.idx..self.idx + length].fill(value); self.idx += length; diff --git a/common/src/lib.rs b/common/src/lib.rs index 39f7f51..978b4db 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -3,3 +3,4 @@ pub mod image; pub mod misc; pub mod oklab; pub mod serde; +pub mod rle_image; diff --git a/common/src/rle_image.rs b/common/src/rle_image.rs new file mode 100644 index 0000000..76b5209 --- /dev/null +++ b/common/src/rle_image.rs @@ -0,0 +1,88 @@ +use crate::misc::Run; + +pub struct RleImage { + runs: Vec, + width: usize, + height: usize, +} + +pub struct RleImageIterator<'a> { + image: &'a RleImage, + run_index: usize, + last_idx: usize, +} + +pub struct ImageRun { + color: u8, + + row: usize, + start: usize, + end: usize, +} + +impl RleImage { + pub fn from_decoder(width: usize, height: usize, decoder: impl Iterator) -> Self { + let mut runs = Vec::new(); + + let mut pixel = 0; + for run in decoder { + let length = run.length as usize; + if run.value != 0 { + let (y, x) = (pixel / width, pixel % width); + runs.push(ImageRun { + color: run.value, + row: y, + start: x, + end: x + length, + }); + } + pixel += length; + } + + Self { + runs, + width, + height, + } + } + + pub fn to_runs(&self) -> RleImageIterator { + RleImageIterator { + image: self, + run_index: 0, + last_idx: 0, + } + } +} + +impl<'a> Iterator for RleImageIterator<'a> { + type Item = Run; + + fn next(&mut self) -> Option { + if self.run_index >= self.image.runs.len() { + return None; + } + + let run = &self.image.runs[self.run_index]; + let row_offset = run.row * self.image.width; + let start_idx = row_offset + run.start; + + if self.last_idx < start_idx { + let length = start_idx - self.last_idx; + self.last_idx = start_idx; + return Some(Run { + length: length as u64, + value: 0, + }); + } + + let length = run.end - run.start; + self.last_idx = row_offset + run.end; + self.run_index += 1; + + Some(Run { + length: length as u64, + value: run.color, + }) + } +} diff --git a/mslicer/Cargo.toml b/mslicer/Cargo.toml index 1f66f9f..6782522 100644 --- a/mslicer/Cargo.toml +++ b/mslicer/Cargo.toml @@ -18,6 +18,7 @@ egui-wgpu.workspace = true egui.workspace = true encase.workspace = true image.workspace = true +imageproc.workspace = true nalgebra.workspace = true notify-rust.workspace = true open.workspace = true diff --git a/mslicer/src/plugins/elephant_foot_fixer.rs b/mslicer/src/plugins/elephant_foot_fixer.rs index da2c7e9..d534341 100644 --- a/mslicer/src/plugins/elephant_foot_fixer.rs +++ b/mslicer/src/plugins/elephant_foot_fixer.rs @@ -1,7 +1,14 @@ -use common::image::Image; +use std::{ + collections::{HashSet, VecDeque}, + time::Instant, +}; + +use common::{image::Image, rle_image::RleImage}; use egui::{Context, Ui}; +use image::{GrayImage, Luma}; +use imageproc::{distance_transform::Norm, morphology::Mask}; use rayon::iter::{ParallelBridge, ParallelIterator}; -use tracing::info; +use tracing::{info, trace}; use crate::{app::App, ui::components::dragger_tip}; use goo_format::{File as GooFile, LayerDecoder, LayerEncoder}; @@ -60,19 +67,29 @@ impl Plugin for ElephantFootFixerPlugin { x_radius, y_radius ); + let intensity = self.intensity_multiplier / 100.0; + let darken = |value: u8| (value as f32 * intensity).round() as u8; + goo.layers .iter_mut() .take(goo.header.bottom_layers as usize) .par_bridge() .for_each(|layer| { let decoder = LayerDecoder::new(&layer.data); - let mut image = Image::from_decoder(width, height, decoder); + let raw_image = Image::from_decoder(width, height, decoder).take(); + let mut image = + GrayImage::from_raw(width as u32, height as u32, raw_image).unwrap(); - let intensity = self.intensity_multiplier / 100.0; - apply(&mut image, intensity, x_radius, y_radius); + let erode = imageproc::morphology::grayscale_erode(&image, &Mask::square(10)); + for (x, y, pixel) in image.enumerate_pixels_mut() { + if erode.get_pixel(x, y)[0] == 0 && pixel[0] != 0 { + *pixel = Luma([darken(pixel[0])]); + } + } let mut new_layer = LayerEncoder::new(); - for run in image.runs() { + let raw_image = Image::from_raw(width, height, image.into_raw()); + for run in raw_image.runs() { new_layer.add_run(run.length, run.value) } @@ -90,67 +107,3 @@ pub fn get_plugin() -> Box { intensity_multiplier: 30.0, }) } - -fn apply(image: &mut Image, intensity: f32, x_radius: usize, y_radius: usize) { - let mut x_distances = vec![u16::MAX; image.size.x * image.size.y]; - let mut y_distances = vec![u16::MAX; image.size.x * image.size.y]; - - #[inline(always)] - fn update_distance( - image: &Image, - distances: &mut [u16], - distance: &mut u16, - x: usize, - y: usize, - ) { - *distance += 1; - - let pixel = image.get_pixel(x, y); - (pixel == 0).then(|| *distance = 0); - - let idx = y * image.size.x + x; - let old = distances[idx]; - (*distance < old).then(|| distances[idx] = *distance); - } - - for x in 0..image.size.x { - let mut distance = 0; - for y in 0..image.size.y { - update_distance(&image, &mut y_distances, &mut distance, x, y); - } - } - - for y in 0..image.size.y { - let mut distance = 0; - for x in 0..image.size.x { - update_distance(&image, &mut x_distances, &mut distance, x, y); - } - } - - for x in (0..image.size.x).rev() { - let mut distance = 0; - for y in (0..image.size.y).rev() { - update_distance(&image, &mut y_distances, &mut distance, x, y); - } - } - - for y in (0..image.size.y).rev() { - let mut distance = 0; - for x in (0..image.size.x).rev() { - update_distance(&image, &mut x_distances, &mut distance, x, y); - } - } - - for x in 0..image.size.x { - for y in 0..image.size.y { - let pixel = image.get_pixel(x, y); - - let x_distance = x_distances[y * image.size.x + x]; - let y_distance = y_distances[y * image.size.x + x]; - - if x_distance < x_radius as u16 || y_distance < y_radius as u16 { - image.set_pixel(x, y, (pixel as f32 * intensity).round() as u8); - } - } - } -} diff --git a/slicer/src/main.rs b/slicer/src/main.rs index f8b301b..f31dd2c 100644 --- a/slicer/src/main.rs +++ b/slicer/src/main.rs @@ -33,6 +33,7 @@ fn main() -> Result<()> { ..Default::default() }, first_layers: 10, + transition_layers: 10, }; let file = File::open(FILE_PATH)?;