Save as mesh: one dialog for STL and 3MF, with Send to slicer (#1272)

One docked Save as mesh dialog replaces Export STL / 3MF / 3MF with face tags, with an optional Send to slicer that lists the slicers installed on this machine. Engine: file.save_mesh and file.slicers; the Convert Mesh dialog goes (mesh.convert and the MeshConvert feature stay, so existing designs open). Strings in all catalogs; screenshots in the PR. Checked on main + this branch: cargo check --workspace, the engine slicer/mesh/file tests, the UI catalog and menu tests and scenario s14 (the one local colour-token failure is epaint's TexturesDelta panic, which main shows on this machine too).
This commit is contained in:
froglemonade2 authored and GitHub committed 2026-10-15 03:51:28 -04:00
1 parent 3e13eb6392
commit 0872eb959a
28 files changed
+1483 -100

No files matched your search

+1 -15
View File
@@ -45,9 +45,6 @@ pub enum Surf {
Stitch {
tolerance: String,
},
ConvertMesh {
tolerance: String,
},
Thicken {
thickness: String,
symmetric: bool,
@@ -79,7 +76,6 @@ impl Surf {
Surf::Offset { .. } => "OFFSET SURFACE",
Surf::Ruled { .. } => "RULED SURFACE",
Surf::Stitch { .. } => "STITCH",
Surf::ConvertMesh { .. } => "CONVERT MESH",
Surf::Thicken { .. } => "THICKEN",
Surf::Extend { .. } => "EXTEND",
Surf::Trim => "TRIM",
@@ -124,7 +120,6 @@ fn inputs(k: &Surf) -> Vec<SelInput> {
vec![SelInput::new("Edges", EDGES, true), SelInput::new("Reference face", FACES, false), SelInput::new("Direction", AXES | EDGES, false)]
}
Surf::Stitch { .. } => vec![SelInput::new("Surfaces", BODIES, true)],
Surf::ConvertMesh { .. } => vec![SelInput::new("Mesh body", BODIES, false)],
Surf::Thicken { .. } => vec![SelInput::new("Surfaces", BODIES, true)],
Surf::Extend { .. } => vec![SelInput::new("Edges", EDGES, true)],
Surf::Trim => vec![
@@ -150,7 +145,6 @@ pub fn start(_app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
"surface.offset" => Surf::Offset { distance: "2 mm".into(), chain: false },
"surface.ruled" => Surf::Ruled { distance: "10 mm".into(), mode: 0, angle: "5 deg".into(), flip: false, round: false },
"surface.stitch" => Surf::Stitch { tolerance: String::new() },
"mesh.convert" => Surf::ConvertMesh { tolerance: "0.05 mm".into() },
"surface.thicken" => Surf::Thicken { thickness: "2 mm".into(), symmetric: false, operation: 0 },
"surface.extend" => Surf::Extend { distance: "10 mm".into() },
"surface.trim" => Surf::Trim,
@@ -166,7 +160,7 @@ pub fn start(_app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
pub fn rows(ui: &mut egui::Ui, k: &mut Surf) -> bool {
let mut enter = false;
match k {
Surf::Stitch { tolerance } | Surf::ConvertMesh { tolerance } => {
Surf::Stitch { tolerance } => {
row_label(ui, "Tolerance");
let r = ui.add(egui::TextEdit::singleline(tolerance).hint_text("tight"));
crate::params_dialog::complete(ui, &r, tolerance);
@@ -469,11 +463,6 @@ pub fn commands(app: &SolveApp, k: &Surf, inputs: &[SelInput], extra: &Map<Strin
}
("surface.stitch", p)
}
Surf::ConvertMesh { tolerance } => {
need(0, "a closed mesh body")?;
let body = bodies(inputs, 0).into_iter().next().ok_or("select a mesh body")?;
("mesh.convert", json!({"body":body,"tolerance":tolerance}))
}
Surf::Thicken { thickness, symmetric, operation } => {
need(0, "the surfaces to thicken")?;
(
@@ -661,9 +650,6 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
Surf::Stitch { tolerance: if tolerance.trim() == "0" { String::new() } else { tolerance.clone() } },
vec![bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect()],
),
FeatureKind::MeshConvert { body, tolerance } => {
(Surf::ConvertMesh { tolerance: tolerance.clone() }, vec![vec![Sel::Body { name: body.clone() }]])
}
FeatureKind::Thicken { bodies, thickness, symmetric, operation, .. } => (
Surf::Thicken { thickness: thickness.clone(), symmetric: *symmetric, operation: op_index(operation) },
vec![bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect()],