Erosion elephant foot fixer impl

This commit is contained in:
Connor Slade
2024-08-28 21:35:07 -04:00
parent e97bce1615
commit 34a3f3d2a0
3 changed files with 124 additions and 40 deletions

View File

@@ -3,8 +3,9 @@ use nalgebra::Vector2;
use crate::misc::Run;
/// A fast grayscale image buffer
#[derive(Clone)]
pub struct Image {
size: Vector2<usize>,
pub size: Vector2<usize>,
data: Vec<u8>,
idx: usize,
}
@@ -26,6 +27,12 @@ impl Image {
}
}
pub fn from_decoder(width: usize, height: usize, decoder: impl Iterator<Item = Run>) -> Self {
let mut image = Self::blank(width, height);
decoder.for_each(|run| image.add_run(run.length as usize, run.value));
image
}
pub fn add_run(&mut self, length: usize, value: u8) {
self.data[self.idx..self.idx + length].fill(value);
self.idx += length;
@@ -41,23 +48,6 @@ impl Image {
self.data[idx] = val;
}
pub fn blur(&mut self, sigma: f32) {
let sigma = sigma as usize;
for x in 0..self.size.x {
for y in 0..self.size.y {
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);
}
}
}
pub fn runs(&self) -> ImageRuns {
ImageRuns {
inner: &self.data,
@@ -76,6 +66,25 @@ impl Image {
}
}
impl Image {
pub fn blur(&mut self, sigma: f32) {
let sigma = sigma as usize;
for x in 0..self.size.x {
for y in 0..self.size.y {
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);
}
}
}
}
impl<'a> Iterator for ImageRuns<'a> {
type Item = Run;

View File

@@ -1,14 +1,16 @@
use common::image::Image;
use egui::{Context, Ui};
use tracing::info;
use crate::{app::App, ui::components::dragger};
use goo_format::File as GooFile;
use crate::{app::App, ui::components::dragger_tip};
use goo_format::{File as GooFile, LayerDecoder, LayerEncoder};
use super::Plugin;
pub struct ElephantFootFixerPlugin {
enabled: bool,
rest_time: f32,
rest_layers: u32,
inset_distance: f32,
intensity_multiplier: f32,
}
impl Plugin for ElephantFootFixerPlugin {
@@ -17,34 +19,64 @@ impl Plugin for ElephantFootFixerPlugin {
}
fn ui(&mut self, _app: &mut App, ui: &mut Ui, _ctx: &Context) {
ui.label("Fixes the 'Elephant Foot' effect by adding rest times before and after each bottom layer.");
ui.label("Fixes the 'Elephant Foot' effect by exposing the edges of the bottom layers at a lower intensity. You may have to make a few test prints to find the right settings for your printer and resin.");
ui.checkbox(&mut self.enabled, "Enabled");
ui.add_space(8.0);
ui.label("How long to wait before and after each exposure with the build plate in place.");
dragger(ui, "Rest Time", &mut self.rest_time, |x| {
x.speed(0.1).suffix("s")
});
dragger_tip(
ui,
"Inset Distance",
"The distance in from the edges that will have a reduced intensity.",
&mut self.inset_distance,
|x| x.speed(0.1).suffix("mm"),
);
ui.add_space(8.0);
ui.label("How many layers to apply the rest time to, after the bottom layers.");
dragger(ui, "Rest Layers", &mut self.rest_layers, |x| {
x.speed(1).suffix(" layers")
});
dragger_tip(
ui,
"Intensity",
"This percent will be multiplied by the pixel values of the edge pixels.",
&mut self.intensity_multiplier,
|x| x.clamp_range(0.0..=100.0).speed(1).suffix("%"),
);
}
fn post_slice(&self, _app: &App, goo: &mut GooFile) {
goo.header.advance_mode = true;
goo.header.exposure_delay_mode = true;
if !self.enabled {
return;
}
let (width, height) = (
goo.header.x_resolution as usize,
goo.header.y_resolution as usize,
);
let (x_radius, y_radius) = (
(self.inset_distance * (width as f32 / goo.header.x_size)) as usize,
(self.inset_distance * (height as f32 / goo.header.y_size)) as usize,
);
info!(
"Eroding bottom layers with radius ({}, {})",
x_radius, y_radius
);
// All bottom layers should have rest time added
for layer in goo
.layers
.iter_mut()
.take((goo.header.bottom_layers + self.rest_layers) as usize)
.take(goo.header.bottom_layers as usize)
{
layer.before_lift_time = self.rest_time;
layer.after_retract_time = self.rest_time;
let decoder = LayerDecoder::new(&layer.data);
let image = Image::from_decoder(width, height, decoder);
let image = erode(image, self.intensity_multiplier / 100.0, x_radius, y_radius);
let mut new_layer = LayerEncoder::new();
for run in image.runs() {
new_layer.add_run(run.length, run.value)
}
let (data, checksum) = new_layer.finish();
layer.data = data;
layer.checksum = checksum;
}
}
}
@@ -52,7 +84,32 @@ impl Plugin for ElephantFootFixerPlugin {
pub fn get_plugin() -> Box<dyn Plugin> {
Box::new(ElephantFootFixerPlugin {
enabled: false,
rest_time: 20.0,
rest_layers: 20,
inset_distance: 2.0,
intensity_multiplier: 30.0,
})
}
pub fn erode(image: Image, intensity: f32, x_radius: usize, y_radius: usize) -> Image {
let mut new_layer = image.clone();
for x in 0..image.size.x {
'outer: for y in 0..image.size.y {
let pixel = image.get_pixel(x, y);
if pixel == 0 {
continue;
}
// if there are any black pixels in the radius, multiply the pixel by the intensity multiplier
for xp in x.saturating_sub(x_radius)..(x + x_radius).min(image.size.x) {
for yp in y.saturating_sub(y_radius)..(y + y_radius).min(image.size.y) {
if image.get_pixel(xp, yp) == 0 {
new_layer.set_pixel(x, y, (pixel as f32 * intensity) as u8);
continue 'outer;
}
}
}
}
}
new_layer
}

View File

@@ -1,4 +1,5 @@
use egui::{emath::Numeric, DragValue, Ui};
use egui::{emath::Numeric, Align, DragValue, Layout, Ui};
use egui_phosphor::regular::INFO;
pub fn dragger<Num: Numeric>(
ui: &mut Ui,
@@ -12,6 +13,23 @@ pub fn dragger<Num: Numeric>(
});
}
pub fn dragger_tip<Num: Numeric>(
ui: &mut Ui,
label: &str,
tip: &str,
value: &mut Num,
func: fn(DragValue) -> DragValue,
) {
ui.horizontal(|ui| {
ui.add(func(DragValue::new(value)));
ui.label(label);
ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
ui.label(INFO).on_hover_text(tip);
ui.add_space(ui.available_width());
})
});
}
pub fn vec2_dragger<Num: Numeric>(
ui: &mut Ui,
val: &mut [Num; 2],