Cleanup slice operations window

This commit is contained in:
Connor Slade
2024-07-24 01:10:30 -04:00
parent 555191d6b5
commit 8ad1bbe025
7 changed files with 197 additions and 172 deletions

View File

@@ -1,3 +1,5 @@
use std::time::Duration;
use crate::config::SliceConfig;
pub struct SliceResult<'a, Layer> {
@@ -17,3 +19,16 @@ pub trait EncodableLayer {
fn add_run(&mut self, length: u64, value: u8);
fn finish(self, layer: usize, config: &SliceConfig) -> Self::Output;
}
pub fn human_duration(duration: Duration) -> String {
let ms = duration.as_millis() as f32;
if ms < 1000.0 {
format!("{:}ms", ms)
} else if ms < 60_000.0 {
format!("{:.2}s", ms / 1000.0)
} else {
let minutes = ms / 60_000.0;
let seconds = (minutes - minutes.floor()) * 60.0;
format!("{:.0}m {:.2}s", minutes.floor(), seconds)
}
}

View File

@@ -3,7 +3,7 @@ use std::f32::consts::PI;
use egui_wgpu::ScreenDescriptor;
use image::{Rgba, RgbaImage};
use nalgebra::{Vector2, Vector3};
use tracing::{debug, info};
use tracing::info;
use wgpu::{
BufferAddress, BufferDescriptor, BufferUsages, Color, CommandEncoder, CommandEncoderDescriptor,
Device, Extent3d, ImageCopyBuffer, ImageCopyTexture, ImageDataLayout, LoadOp, Maintain,
@@ -62,10 +62,8 @@ pub fn render_preview_image(
.view_projection_matrix(size.0 as f32 / size.1 as f32),
..old_workspace
};
debug!("Preparing");
model_pipeline.prepare(device, queue, screen_descriptor, encoder, &workspace);
debug!("Rendering");
render_preview(
device,
queue,
@@ -76,7 +74,6 @@ pub fn render_preview_image(
&depth_texture_view,
);
debug!("Downloading");
download_preview(device, queue, &resolved_texture)
}

View File

@@ -1,8 +1,12 @@
use std::{
sync::Arc,
sync::{
atomic::{AtomicU32, Ordering},
Arc,
},
time::{Duration, Instant},
};
use common::misc::human_duration;
use goo_format::File as GooFile;
use image::RgbaImage;
use nalgebra::Vector2;
@@ -13,6 +17,7 @@ use tracing::info;
#[derive(Clone)]
pub struct SliceOperation {
start_time: Instant,
completion: Arc<AtomicU32>,
pub progress: SliceProgress,
pub result: Arc<Mutex<Option<SliceResult>>>,
@@ -34,6 +39,7 @@ impl SliceOperation {
pub fn new(progress: SliceProgress) -> Self {
Self {
start_time: Instant::now(),
completion: Arc::new(AtomicU32::new(0)),
progress,
result: Arc::new(Mutex::new(None)),
preview_image: Arc::new(Mutex::new(None)),
@@ -41,8 +47,9 @@ impl SliceOperation {
}
}
pub fn elapsed(&self) -> Duration {
self.start_time.elapsed()
pub fn completion(&self) -> Option<String> {
let time = self.completion.load(Ordering::Relaxed);
(time != 0).then(|| human_duration(Duration::from_millis(time as u64)))
}
pub fn needs_preview_image(&self) -> bool {
@@ -64,7 +71,11 @@ impl SliceOperation {
}
pub fn add_result(&self, result: SliceResult) {
info!("Slice operation completed in {:?}", self.elapsed());
let elapsed = self.start_time.elapsed();
self.completion
.store(elapsed.as_millis() as u32, Ordering::Relaxed);
info!("Slice operation completed in {:?}", elapsed);
self.result.lock().replace(result);
}

View File

@@ -6,8 +6,7 @@ use crate::app::App;
mod about;
mod models;
mod slice_config;
mod slice_preview;
mod slice_progress;
mod slice_operation;
mod stats;
mod top_bar;
mod workspace;
@@ -27,8 +26,7 @@ pub fn ui(app: &mut App, ctx: &Context, frame: &mut Frame) {
stats::ui(app, ctx, frame);
models::ui(app, ctx, frame);
about::ui(app, ctx, frame);
slice_progress::ui(app, ctx, frame);
slice_preview::ui(app, ctx, frame);
slice_operation::ui(app, ctx, frame);
}
impl Default for Windows {

View File

@@ -0,0 +1,164 @@
use std::{fs::File, io::Write};
use eframe::Frame;
use egui::{
style::HandleShape, Align, Context, DragValue, Layout, ProgressBar, RichText, Sense, Slider,
Vec2, Window,
};
use egui_wgpu::Callback;
use goo_format::LayerDecoder;
use nalgebra::Vector2;
use rfd::FileDialog;
use crate::{
app::App, components::vec2_dragger, render::slice_preview::SlicePreviewRenderCallback,
slice_operation::SliceResult,
};
use common::serde::DynamicSerializer;
pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
let mut window_open = true;
let mut save_complete = false;
if let Some(slice_operation) = &app.slice_operation {
let progress = &slice_operation.progress;
let (current, total) = (progress.completed(), progress.total());
let mut window = Window::new("Slice Operation");
if current >= total {
window = window.open(&mut window_open);
}
window.show(ctx, |ui| {
let completion = slice_operation.completion();
if completion.is_none() {
ui.add(
ProgressBar::new(current as f32 / total as f32)
.text(format!("{:.2}%", current as f32 / total as f32 * 100.0)),
);
ui.label(format!("Slicing... {}/{}", current, total));
ctx.request_repaint();
} else {
ui.horizontal(|ui| {
ui.label(format!("Slicing completed in {}!", completion.unwrap()));
ui.with_layout(Layout::default().with_cross_align(Align::Max), |ui| {
if ui.button("Save").clicked() {
let result = app.slice_operation.as_ref().unwrap().result();
let result = result.as_ref().unwrap();
if let Some(path) = FileDialog::new().save_file() {
let mut file = File::create(path).unwrap();
let mut serializer = DynamicSerializer::new();
result.goo.serialize(&mut serializer);
file.write_all(&serializer.into_inner()).unwrap();
save_complete = true;
}
}
});
});
let mut result = slice_operation.result();
let Some(result) = result.as_mut() else {
return;
};
slice_preview(ui, result);
ui.horizontal(|ui| {
ui.add(
DragValue::new(&mut result.slice_preview_layer)
.clamp_range(1..=result.goo.layers.len())
.custom_formatter(|n, _| format!("{}/{}", n, result.goo.layers.len())),
);
result.slice_preview_layer +=
ui.button(RichText::new("+").monospace()).clicked() as usize;
result.slice_preview_layer -=
ui.button(RichText::new("-").monospace()).clicked() as usize;
ui.separator();
ui.label("Offset");
vec2_dragger(ui, result.preview_offset.as_mut(), |x| x);
ui.separator();
ui.label("Scale");
ui.add(DragValue::new(&mut result.preview_scale));
});
}
});
}
if !window_open || save_complete {
app.slice_operation = None;
}
}
fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
ui.horizontal(|ui| {
ui.spacing_mut().slider_width = ui.available_size().x
/ result.goo.header.x_resolution as f32
* result.goo.header.y_resolution as f32
- 10.0;
ui.add(
Slider::new(&mut result.slice_preview_layer, 1..=result.goo.layers.len())
.vertical()
.handle_shape(HandleShape::Rect { aspect_ratio: 1.0 })
.show_value(false),
);
result.slice_preview_layer = result.slice_preview_layer.clamp(1, result.goo.layers.len());
let new_preview = if result.last_preview_layer != result.slice_preview_layer {
result.last_preview_layer = result.slice_preview_layer;
let (width, height) = (
result.goo.header.x_resolution as u32,
result.goo.header.y_resolution as u32,
);
let layer_data = &result.goo.layers[result.slice_preview_layer - 1].data;
let decoder = LayerDecoder::new(layer_data);
let mut image = vec![0; (width * height) as usize];
let mut pixel = 0;
for run in decoder {
for _ in 0..run.length {
image[pixel] = run.value;
pixel += 1;
}
}
Some(image)
} else {
None
};
result.preview_scale = result.preview_scale.max(0.1);
egui::Frame::canvas(ui.style()).show(ui, |ui| {
let available_size = ui.available_size();
let (rect, _response) = ui.allocate_exact_size(
Vec2::new(
available_size.x,
available_size.x / result.goo.header.x_resolution as f32
* result.goo.header.y_resolution as f32,
),
Sense::drag(),
);
let callback = Callback::new_paint_callback(
rect,
SlicePreviewRenderCallback {
dimensions: Vector2::new(
result.goo.header.x_resolution as u32,
result.goo.header.y_resolution as u32,
),
offset: result.preview_offset,
scale: result.preview_scale,
new_preview,
},
);
ui.painter().add(callback);
});
});
}

View File

@@ -1,105 +0,0 @@
use eframe::Frame;
use egui::{style::HandleShape, Context, DragValue, RichText, Sense, Slider, Vec2, Window};
use egui_wgpu::Callback;
use goo_format::LayerDecoder;
use nalgebra::Vector2;
use crate::{
app::App, components::vec2_dragger, render::slice_preview::SlicePreviewRenderCallback,
};
pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
if let Some(slice_operation) = &app.slice_operation {
let mut result = slice_operation.result();
let Some(result) = result.as_mut() else {
return;
};
Window::new("Slice Preview").show(ctx, move |ui| {
ui.horizontal(|ui| {
ui.spacing_mut().slider_width = ui.available_size().x
/ result.goo.header.x_resolution as f32
* result.goo.header.y_resolution as f32
- 10.0;
ui.add(
Slider::new(&mut result.slice_preview_layer, 1..=result.goo.layers.len())
.vertical()
.handle_shape(HandleShape::Rect { aspect_ratio: 1.0 })
.show_value(false),
);
result.slice_preview_layer =
result.slice_preview_layer.clamp(1, result.goo.layers.len());
let new_preview = if result.last_preview_layer != result.slice_preview_layer {
result.last_preview_layer = result.slice_preview_layer;
let (width, height) = (
result.goo.header.x_resolution as u32,
result.goo.header.y_resolution as u32,
);
let layer_data = &result.goo.layers[result.slice_preview_layer - 1].data;
let decoder = LayerDecoder::new(layer_data);
let mut image = vec![0; (width * height) as usize];
let mut pixel = 0;
for run in decoder {
for _ in 0..run.length {
image[pixel] = run.value;
pixel += 1;
}
}
Some(image)
} else {
None
};
result.preview_scale = result.preview_scale.max(0.1);
egui::Frame::canvas(ui.style()).show(ui, |ui| {
let available_size = ui.available_size();
let (rect, _response) = ui.allocate_exact_size(
Vec2::new(
available_size.x,
available_size.x / result.goo.header.x_resolution as f32
* result.goo.header.y_resolution as f32,
),
Sense::drag(),
);
let callback = Callback::new_paint_callback(
rect,
SlicePreviewRenderCallback {
dimensions: Vector2::new(
result.goo.header.x_resolution as u32,
result.goo.header.y_resolution as u32,
),
offset: result.preview_offset,
scale: result.preview_scale,
new_preview,
},
);
ui.painter().add(callback);
});
});
ui.horizontal(|ui| {
ui.add(
DragValue::new(&mut result.slice_preview_layer)
.clamp_range(1..=result.goo.layers.len())
.custom_formatter(|n, _| format!("{}/{}", n, result.goo.layers.len())),
);
result.slice_preview_layer +=
ui.button(RichText::new("+").monospace()).clicked() as usize;
result.slice_preview_layer -=
ui.button(RichText::new("-").monospace()).clicked() as usize;
ui.separator();
ui.label("Offset");
vec2_dragger(ui, result.preview_offset.as_mut(), |x| x);
ui.separator();
ui.label("Scale");
ui.add(DragValue::new(&mut result.preview_scale));
});
});
}
}

View File

@@ -1,55 +0,0 @@
use std::{fs::File, io::Write};
use common::serde::DynamicSerializer;
use eframe::Frame;
use egui::{Context, ProgressBar, Window};
use rfd::FileDialog;
use crate::app::App;
pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
let mut window_open = true;
let mut save_complete = false;
if let Some(slice_operation) = &app.slice_operation {
let progress = &slice_operation.progress;
let (current, total) = (progress.completed(), progress.total());
let mut window = Window::new("Slice Progress");
if current >= total {
window = window.open(&mut window_open);
}
window.show(ctx, |ui| {
ui.add(
ProgressBar::new(current as f32 / total as f32)
.text(format!("{:.2}%", current as f32 / total as f32 * 100.0)),
);
if current < total {
ui.label(format!("Slicing... {}/{}", current, total));
ctx.request_repaint();
} else {
ui.label("Slicing complete!");
if ui.button("Save").clicked() {
let result = app.slice_operation.as_ref().unwrap().result();
let result = result.as_ref().unwrap();
if let Some(path) = FileDialog::new().save_file() {
let mut file = File::create(path).unwrap();
let mut serializer = DynamicSerializer::new();
result.goo.serialize(&mut serializer);
file.write_all(&serializer.into_inner()).unwrap();
save_complete = true;
}
}
}
});
}
if !window_open || save_complete {
app.slice_operation = None;
}
}