Accelerate current-view DXF export and keep desktop exports responsive (#1738)
Thanks @crono2250.
This commit is contained in:
1 parent
4bacd0cae0
commit
bba03803b7
20 files changed
+1319
-53
No files matched your search
@@ -3,10 +3,14 @@ use crate::{SolveApp, dialogs::Kind, selection::SelInput};
|
||||
use serde_json::json;
|
||||
|
||||
pub fn start(app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
(id == "file.export_view_dxf").then(|| (Kind::ViewDxf { dashed: app.preferences.view_dxf_dashed }, Vec::new()))
|
||||
(id == "file.export_view_dxf").then(|| {
|
||||
let tolerance = app.preferences.view_dxf_tolerance;
|
||||
let tolerance = if tolerance.is_finite() && (0.0001..=1.0).contains(&tolerance) { tolerance } else { 0.01 };
|
||||
(Kind::ViewDxf { dashed: app.preferences.view_dxf_dashed, tolerance }, Vec::new())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rows(app: &SolveApp, ui: &mut egui::Ui, dashed: &mut bool) {
|
||||
pub fn rows(app: &SolveApp, ui: &mut egui::Ui, dashed: &mut bool, tolerance: &mut f64) {
|
||||
crate::dialogs::row_label(ui, "Hidden edges");
|
||||
ui.vertical(|ui| {
|
||||
let r = ui.radio_value(dashed, false, app.tr_label("Do not export hidden edges"));
|
||||
@@ -15,34 +19,54 @@ pub fn rows(app: &SolveApp, ui: &mut egui::Ui, dashed: &mut bool) {
|
||||
crate::scenario::publish_handle("dialog:dxf:dashed", r.rect.center());
|
||||
});
|
||||
ui.end_row();
|
||||
crate::dialogs::row_label(ui, "dxf.tolerance");
|
||||
let r = ui.add(egui::DragValue::new(tolerance).range(0.0001..=1.0).speed(0.001).max_decimals(4).suffix(" mm"));
|
||||
crate::scenario::publish_handle("dialog:dxf:tolerance", r.rect.center());
|
||||
ui.end_row();
|
||||
crate::dialogs::row_label(ui, "Projection");
|
||||
ui.label(app.tr_label(if app.cam.fov > 0.0 { "Current perspective view" } else { "Current orthographic view (1:1 mm)" }));
|
||||
ui.end_row();
|
||||
crate::dialogs::row_label(ui, "");
|
||||
ui.label(app.tr_label("Visible bodies only. Curves use line segments (0.01 mm tolerance)."));
|
||||
ui.add(egui::Label::new(app.tr_label("dxf.tolerance_help")).wrap());
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
pub fn run(app: &mut SolveApp, dashed: bool) -> Result<(), String> {
|
||||
pub fn run(app: &mut SolveApp, dashed: bool, tolerance: f64) -> Result<(), String> {
|
||||
if crate::viewport::section_plane(app).is_some() {
|
||||
return Err(app.tr_label("Turn off section analysis before exporting the view.").into());
|
||||
}
|
||||
if !app.session.world_state().bodies.iter().any(|b| app.session.body_visible(&b.name)) {
|
||||
if app.session.model.state().bodies.is_empty() {
|
||||
return Err(app.tr_label("No visible bodies to export.").into());
|
||||
}
|
||||
if !tolerance.is_finite() || !(0.0001..=1.0).contains(&tolerance) {
|
||||
return Err(app.tr_label("dxf.invalid_tolerance").into());
|
||||
}
|
||||
app.preferences.view_dxf_dashed = dashed;
|
||||
app.preferences.view_dxf_tolerance = tolerance;
|
||||
let camera = app.cam;
|
||||
app.pick_save(&format!("{}-view.dxf", app.session.doc.name), &["dxf"], move |app, path| {
|
||||
if let Some(path) = path {
|
||||
let params = json!({"camera":camera,"hidden":if dashed {"dashed"} else {"omit"},"path":path});
|
||||
if app.run("file.export_view_dxf", params).is_ok() {
|
||||
app.set_status(crate::i18n::format("ui.saved_to", &[("path", &path)]), false);
|
||||
}
|
||||
let params = json!({"camera":camera,"hidden":if dashed {"dashed"} else {"omit"},"tolerance":tolerance,"path":path});
|
||||
let mut request = crate::file_load::Request::new(&path, crate::file_load::Kind::ExportDxf);
|
||||
request.options = params.as_object().cloned().unwrap_or_default();
|
||||
let _ = crate::file_load::begin_request(app, request);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn progress_text(work: Option<(&str, usize, usize)>) -> String {
|
||||
let (stage, done, total) = work.unwrap_or(("dxf_meshes", 0, 1));
|
||||
let label = match stage {
|
||||
"dxf_index" => "dxf.progress.index",
|
||||
"dxf_projection" => "dxf.progress.projection",
|
||||
"dxf_occlusion" => "dxf.progress.occlusion",
|
||||
"dxf_write" => "dxf.progress.write",
|
||||
_ => "dxf.progress.meshes",
|
||||
};
|
||||
format!("{}: {done}/{total}", crate::i18n::tr_label(label))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -58,7 +82,11 @@ mod tests {
|
||||
h.frames(3);
|
||||
h.step(&json!({"click":{"handle":"appbar:folder"}})).unwrap();
|
||||
h.step(&json!({"widget":"Export Current View as DXF…"})).unwrap();
|
||||
assert!(matches!(h.app.dialog.as_ref().map(|d| &d.kind), Some(Kind::ViewDxf { dashed: false })));
|
||||
assert!(matches!(h.app.dialog.as_ref().map(|d| &d.kind), Some(Kind::ViewDxf { dashed: false, .. })));
|
||||
h.step(&json!({"click":{"handle":"dialog:dxf:tolerance"},"double":true})).unwrap();
|
||||
h.step(&json!({"key":"A","cmd":true})).unwrap();
|
||||
h.step(&json!({"text":"0.1"})).unwrap();
|
||||
h.step(&json!({"key":"Tab"})).unwrap();
|
||||
h.step(&json!({"click":{"handle":"dialog:dxf:dashed"}})).unwrap();
|
||||
let dir = h.dir.clone();
|
||||
h.app.services.pick_save = Some(Box::new(move |name, extensions| {
|
||||
@@ -67,7 +95,7 @@ mod tests {
|
||||
Box::pin(std::future::ready(Some(dir.join("view.dxf").to_string_lossy().into_owned())))
|
||||
}));
|
||||
h.step(&json!({"click":{"handle":"dialog:Export DXF…"}})).unwrap();
|
||||
h.frames(3);
|
||||
wait_for_export(&mut h);
|
||||
let text = std::fs::read_to_string(h.dir.join("view.dxf")).unwrap();
|
||||
assert!(text.contains("LWPOLYLINE") && text.contains("HIDDEN"));
|
||||
assert!(h.app.dialog.is_none() && h.app.preferences.view_dxf_dashed);
|
||||
@@ -75,14 +103,27 @@ mod tests {
|
||||
let mut restored = SolveApp::new(Default::default(), Default::default());
|
||||
restored.load_prefs(&saved);
|
||||
assert!(restored.preferences.view_dxf_dashed);
|
||||
assert_eq!(restored.preferences.view_dxf_tolerance, 0.1);
|
||||
h.app.start("file.export_view_dxf");
|
||||
h.frames(3);
|
||||
assert!(matches!(h.app.dialog.as_ref().map(|d| &d.kind), Some(Kind::ViewDxf { dashed: true })));
|
||||
assert!(matches!(h.app.dialog.as_ref().map(|d| &d.kind), Some(Kind::ViewDxf { dashed: true, tolerance: 0.1 })));
|
||||
h.step(&json!({"click":{"handle":"dialog:dxf:omit"}})).unwrap();
|
||||
h.step(&json!({"click":{"handle":"dialog:Export DXF…"}})).unwrap();
|
||||
h.frames(3);
|
||||
wait_for_export(&mut h);
|
||||
let text = std::fs::read_to_string(h.dir.join("view.dxf")).unwrap();
|
||||
assert!(!text.contains("LWPOLYLINE") && !text.contains("HIDDEN"));
|
||||
assert!(!h.app.preferences.view_dxf_dashed);
|
||||
}
|
||||
|
||||
fn wait_for_export(h: &mut crate::scenario::Harness) {
|
||||
let began = std::time::Instant::now();
|
||||
loop {
|
||||
h.frames(1);
|
||||
if h.app.file_load.idle() && !h.app.file_picker_busy() {
|
||||
break;
|
||||
}
|
||||
assert!(began.elapsed().as_secs() < 10, "export did not finish");
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user