Convert slice operation window to a egui_dock panel
This commit is contained in:
1
TODO.md
1
TODO.md
@@ -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
|
||||
|
@@ -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 || {
|
||||
|
@@ -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();
|
||||
|
@@ -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>>,
|
||||
|
@@ -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)
|
||||
|
@@ -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())
|
||||
|
@@ -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,
|
||||
},
|
||||
|
Reference in New Issue
Block a user