Convert slice operation window to a egui_dock panel

This commit is contained in:
Connor Slade
2025-02-14 14:32:31 -05:00
parent b269f4c11b
commit 8f73af1e5a
7 changed files with 99 additions and 100 deletions

View File

@@ -92,3 +92,4 @@
- [x] Cleanup self intersection resolution
- [ ] Dont fail to load an stl without normals
- [ ] GPU accalration for post processing effect?
- [ ] Fix requiring viewport to be visible to render preview image

View File

@@ -9,7 +9,7 @@ use std::{
use clone_macro::clone;
use const_format::concatcp;
use eframe::Theme;
use egui::Visuals;
use egui::{Vec2, Visuals};
use egui_dock::{DockState, NodeIndex};
use egui_phosphor::regular::CARET_RIGHT;
use egui_tracing::EventCollector;
@@ -159,6 +159,13 @@ impl App {
self.slice_operation
.replace(SliceOperation::new(slicer.progress()));
if self.dock_state.find_tab(&Tab::SliceOperation).is_none() {
let window_id = self.dock_state.add_window(vec![Tab::SliceOperation]);
let window = self.dock_state.get_window_state_mut(window_id).unwrap();
window.set_size(Vec2::new(700.0, 400.0));
// todo: or focus it
}
thread::spawn(clone!(
[{ self.slice_operation } as slice_operation],
move || {

View File

@@ -34,6 +34,7 @@ pub struct SlicePreviewPipeline {
struct SlicePreviewUniforms {
dimensions: Vector2<u32>,
offset: Vector2<f32>,
aspect: f32,
scale: f32,
}
@@ -182,6 +183,7 @@ impl SlicePreviewPipeline {
.write(&SlicePreviewUniforms {
dimensions: resources.dimensions,
offset: resources.offset,
aspect: resources.aspect,
scale: resources.scale.recip(),
})
.unwrap();

View File

@@ -11,6 +11,7 @@ pub struct SlicePreviewRenderResources {
pub struct SlicePreviewRenderCallback {
pub dimensions: Vector2<u32>,
pub offset: Vector2<f32>,
pub aspect: f32,
pub scale: f32,
pub new_preview: Option<Vec<u8>>,

View File

@@ -4,7 +4,8 @@
struct Context {
dimensions: vec2<u32>,
offset: vec2<f32>,
scale: f32
aspect: f32, // width / height
scale: f32,
}
struct VertexOutput {
@@ -33,7 +34,12 @@ fn index(x: u32, y: u32) -> f32 {
@fragment
fn frag(in: VertexOutput) -> @location(0) vec4<f32> {
let pos = vec2(in.position.x * context.scale / 2.0 + 0.5, in.position.y * context.scale / 2.0 + 0.5);
let aspect = context.aspect * f32(context.dimensions.y) / f32(context.dimensions.x);
let pos = vec2(
in.position.x * context.scale * aspect,
in.position.y * context.scale
) / 2.0 + 0.5;
let value = index(
u32(pos.x * f32(context.dimensions.x) + context.offset.x),
u32(pos.y * f32(context.dimensions.y) + context.offset.y)

View File

@@ -28,6 +28,7 @@ pub enum Tab {
Models,
RemotePrint,
SliceConfig,
SliceOperation,
Stats,
Supports,
Viewport,
@@ -42,6 +43,7 @@ impl Tab {
Tab::Models => "Models",
Tab::RemotePrint => "Remote Print",
Tab::SliceConfig => "Slice Config",
Tab::SliceOperation => "Slice Operation",
Tab::Stats => "Stats",
Tab::Supports => "Supports",
Tab::Viewport => "Viewport",
@@ -64,6 +66,7 @@ impl TabViewer for Tabs<'_> {
Tab::Models => models::ui(self.app, ui, self.ctx),
Tab::RemotePrint => remote_print::ui(self.app, ui, self.ctx),
Tab::SliceConfig => slice_config::ui(self.app, ui, self.ctx),
Tab::SliceOperation => slice_operation::ui(self.app, ui, self.ctx),
Tab::Stats => stats::ui(self.app, ui, self.ctx),
Tab::Supports => supports::ui(self.app, ui, self.ctx),
Tab::Viewport => viewport(self.app, ui, self.ctx),
@@ -81,16 +84,17 @@ impl TabViewer for Tabs<'_> {
Tab::Models,
Tab::RemotePrint,
Tab::SliceConfig,
Tab::SliceOperation,
Tab::Stats,
Tab::Supports,
Tab::Workspace,
] {
let already_open = self.app.dock_state.find_tab(&tab).is_some();
ui.add_enabled_ui(!already_open, |ui| {
if !already_open {
ui.button(tab.name())
.clicked()
.then(|| self.app.dock_state.add_window(vec![tab]));
});
}
}
}
@@ -113,7 +117,6 @@ impl TabViewer for Tabs<'_> {
pub fn ui(app: &mut App, ctx: &Context) {
top_bar::ui(app, ctx);
slice_operation::ui(app, ctx);
CentralPanel::default()
.frame(Frame::none())

View File

@@ -3,7 +3,7 @@ use std::{fs::File, io::Write, mem, sync::Arc};
use const_format::concatcp;
use egui::{
style::HandleShape, text::LayoutJob, Align, Button, Context, DragValue, FontSelection, Grid,
Id, Layout, ProgressBar, RichText, Sense, Slider, Style, Vec2, Window,
Id, Layout, ProgressBar, RichText, Sense, Slider, Style, Ui, Vec2,
};
use egui_phosphor::regular::{FLOPPY_DISK_BACK, PAPER_PLANE_TILT};
use egui_wgpu::Callback;
@@ -20,101 +20,87 @@ use common::serde::DynamicSerializer;
const FILENAME_POPUP_TEXT: &str =
"To ensure the file name is unique, some extra random characters will be added on the end.";
pub fn ui(app: &mut App, ctx: &Context) {
let mut window_open = true;
pub fn ui(app: &mut App, ui: &mut Ui, ctx: &Context) {
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");
let completion = slice_operation.completion();
if current >= total {
window = window.open(&mut window_open);
}
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)),
);
window.show(ctx, |ui| {
let completion = slice_operation.completion();
ui.label(format!("Slicing... {}/{}", current, total));
ctx.request_repaint();
} else {
ui.horizontal(|ui| {
ui.label(format!("Slicing completed in {}!", completion.unwrap()));
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.with_layout(Layout::default().with_cross_align(Align::Max), |ui| {
ui.horizontal(|ui| {
ui.add_enabled_ui(app.remote_print.is_initialized(), |ui| {
ui.menu_button(concatcp!(PAPER_PLANE_TILT, " Send to Printer"), |ui| {
let mqtt = app.remote_print.mqtt();
for printer in app.remote_print.printers().iter() {
let client = mqtt.get_client(&printer.mainboard_id);
ui.label(format!("Slicing... {}/{}", current, total));
ctx.request_repaint();
} else {
ui.horizontal(|ui| {
ui.label(format!("Slicing completed in {}!", completion.unwrap()));
let mut layout_job = LayoutJob::default();
RichText::new(format!("{} ", client.attributes.name))
.append_to(
&mut layout_job,
&Style::default(),
FontSelection::Default,
Align::LEFT,
);
RichText::new(&client.attributes.mainboard_id)
.monospace()
.append_to(
&mut layout_job,
&Style::default(),
FontSelection::Default,
Align::LEFT,
);
ui.with_layout(Layout::default().with_cross_align(Align::Max), |ui| {
ui.horizontal(|ui| {
ui.add_enabled_ui(app.remote_print.is_initialized(), |ui| {
ui.menu_button(
concatcp!(PAPER_PLANE_TILT, " Send to Printer"),
|ui| {
let mqtt = app.remote_print.mqtt();
for printer in app.remote_print.printers().iter() {
let client = mqtt.get_client(&printer.mainboard_id);
let result = app.slice_operation.as_ref().unwrap().result();
let result = result.as_ref().unwrap();
let mut layout_job = LayoutJob::default();
RichText::new(format!("{} ", client.attributes.name))
.append_to(
&mut layout_job,
&Style::default(),
FontSelection::Default,
Align::LEFT,
);
RichText::new(&client.attributes.mainboard_id)
.monospace()
.append_to(
&mut layout_job,
&Style::default(),
FontSelection::Default,
Align::LEFT,
);
let result =
app.slice_operation.as_ref().unwrap().result();
let result = result.as_ref().unwrap();
let mut serializer = DynamicSerializer::new();
result.file.serialize(&mut serializer);
let data = Arc::new(serializer.into_inner());
let mainboard_id = printer.mainboard_id.clone();
if ui.button(layout_job).clicked() {
app.popup.open(name_popup(mainboard_id, data));
}
}
},
);
});
if ui.button(concatcp!(FLOPPY_DISK_BACK, " 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.file.serialize(&mut serializer);
file.write_all(&serializer.into_inner()).unwrap();
let data = Arc::new(serializer.into_inner());
let mainboard_id = printer.mainboard_id.clone();
if ui.button(layout_job).clicked() {
app.popup.open(name_popup(mainboard_id, data));
}
}
});
});
if ui.button(concatcp!(FLOPPY_DISK_BACK, " 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.file.serialize(&mut serializer);
file.write_all(&serializer.into_inner()).unwrap();
}
})
});
}
})
});
});
let mut result = slice_operation.result();
let Some(result) = result.as_mut() else {
return;
};
slice_preview(ui, result);
let mut result = slice_operation.result();
let Some(result) = result.as_mut() else {
return;
};
ui.with_layout(Layout::bottom_up(Align::Min), |ui| {
ui.horizontal(|ui| {
let layer_digits = result.layer_count.1 as usize;
ui.add(
@@ -141,21 +127,18 @@ pub fn ui(app: &mut App, ctx: &Context) {
.speed(0.1),
);
});
}
});
}
if !window_open {
app.slice_operation = None;
slice_preview(ui, result);
});
}
}
}
fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
let info = result.file.info();
ui.horizontal(|ui| {
ui.spacing_mut().slider_width =
ui.available_size().x / info.resolution.x as f32 * info.resolution.y as f32 - 10.0;
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
ui.spacing_mut().slider_width = ui.available_size().y;
ui.add(
Slider::new(&mut result.slice_preview_layer, 1..=info.layers as usize)
.vertical()
@@ -163,6 +146,7 @@ fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
.show_value(false),
);
let available_size = ui.available_size() - Vec2::new(5.0, 5.0);
let (width, height) = (info.resolution.x, info.resolution.y);
result.slice_preview_layer = result.slice_preview_layer.clamp(1, info.layers as usize);
@@ -179,14 +163,8 @@ fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
};
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 / info.resolution.x as f32 * info.resolution.y as f32,
),
Sense::drag(),
);
let (rect, response) = ui
.allocate_exact_size(Vec2::new(available_size.x, available_size.y), Sense::drag());
let preview_scale = result.preview_scale.exp2();
let drag = response.drag_delta();
@@ -204,6 +182,7 @@ fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
SlicePreviewRenderCallback {
dimensions: Vector2::new(info.resolution.x, info.resolution.y),
offset: result.preview_offset,
aspect: rect.width() / rect.height(),
scale: preview_scale,
new_preview,
},