Surface Loft: sheets through open or closed sections (#847)
Surface Loft lofts open or closed curve chains, one section per sketch in pick order, into surface bodies. Adds the SurfaceLoft feature (format 18 note), the surface.loft command, a dialog with Edit Feature support, and kernel and engine tests. Part of #757.
This commit is contained in:
1 parent
7c85970bfc
commit
897b7d93b5
15 files changed
+365
-55
No files matched your search
@@ -325,6 +325,7 @@ impl FeatureKind {
|
||||
| FeatureKind::SurfaceExtrude { .. }
|
||||
| FeatureKind::SurfaceRevolve { .. }
|
||||
| FeatureKind::SurfaceSweep { .. }
|
||||
| FeatureKind::SurfaceLoft { .. }
|
||||
| FeatureKind::Combine { .. }
|
||||
| FeatureKind::Loft { .. }
|
||||
| FeatureKind::Sweep { .. }
|
||||
|
||||
@@ -62,6 +62,7 @@ impl FeatureKind {
|
||||
FeatureKind::Pipe { path_sketch, .. } => sk(path_sketch),
|
||||
FeatureKind::SheetFold { sketch: Some(s), .. } => sk(s),
|
||||
FeatureKind::Loft { sections, .. } => sections.iter_mut().for_each(|s| sk(&mut s.sketch)),
|
||||
FeatureKind::SurfaceLoft { sections } => sections.iter_mut().for_each(|s| sk(&mut s.sketch)),
|
||||
FeatureKind::Hole { points: Some(p), .. } => sk(&mut p.sketch),
|
||||
FeatureKind::Pattern { features, pattern, .. } => {
|
||||
feats(features);
|
||||
|
||||
@@ -982,6 +982,11 @@ pub enum FeatureKind {
|
||||
path_sketch: u64,
|
||||
path: Vec<String>,
|
||||
},
|
||||
/// A surface body lofted through sections in order, each one open or closed chain of
|
||||
/// sketch curves: walls only, no caps.
|
||||
SurfaceLoft {
|
||||
sections: Vec<SheetSection>,
|
||||
},
|
||||
/// A planar surface body extended outward by `distance` past the edges through `edges`.
|
||||
SurfaceExtend {
|
||||
body: String,
|
||||
@@ -1136,6 +1141,17 @@ pub struct LoftSection {
|
||||
pub point: Option<String>,
|
||||
}
|
||||
|
||||
/// A section of a surface loft: one chain from a sketch (one profile's boundary, or curves
|
||||
/// meeting end to end).
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SheetSection {
|
||||
pub sketch: u64,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub profiles: Option<ProfileSel>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub curves: Vec<String>,
|
||||
}
|
||||
|
||||
/// Coil cross-sections.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -1292,6 +1308,7 @@ impl FeatureKind {
|
||||
K::Sweep { sketch, path_sketch, .. } | K::SurfaceSweep { sketch, path_sketch, .. } => vec![*sketch, *path_sketch],
|
||||
K::Pipe { path_sketch, .. } | K::Pattern { pattern: PatternKind::Path { path_sketch, .. }, .. } => vec![*path_sketch],
|
||||
K::Loft { sections, .. } => sections.iter().map(|s| s.sketch).collect(),
|
||||
K::SurfaceLoft { sections } => sections.iter().map(|s| s.sketch).collect(),
|
||||
K::SheetFold { sketch, .. } | K::SplitFace { sketch, .. } | K::Patch { sketch, .. } => sketch.iter().copied().collect(),
|
||||
K::Hole { points, .. } => points.iter().map(|p| p.sketch).collect(),
|
||||
K::Sketch { .. }
|
||||
@@ -1420,6 +1437,7 @@ impl FeatureKind {
|
||||
FeatureKind::SurfaceExtrude { .. } => "SurfaceExtrudeFeature",
|
||||
FeatureKind::SurfaceRevolve { .. } => "SurfaceRevolveFeature",
|
||||
FeatureKind::SurfaceSweep { .. } => "SurfaceSweepFeature",
|
||||
FeatureKind::SurfaceLoft { .. } => "SurfaceLoftFeature",
|
||||
FeatureKind::Stitch { .. } => "StitchFeature",
|
||||
FeatureKind::Thicken { .. } => "ThickenFeature",
|
||||
FeatureKind::SurfaceTrim { .. } => "TrimFeature",
|
||||
@@ -1491,6 +1509,7 @@ impl FeatureKind {
|
||||
FeatureKind::SurfaceExtrude { .. } => "SurfaceExtrude",
|
||||
FeatureKind::SurfaceRevolve { .. } => "SurfaceRevolve",
|
||||
FeatureKind::SurfaceSweep { .. } => "SurfaceSweep",
|
||||
FeatureKind::SurfaceLoft { .. } => "SurfaceLoft",
|
||||
FeatureKind::Stitch { .. } => "Stitch",
|
||||
FeatureKind::Thicken { .. } => "Thicken",
|
||||
FeatureKind::SurfaceTrim { .. } => "Trim",
|
||||
@@ -1520,6 +1539,7 @@ impl FeatureKind {
|
||||
vec![(*sk, profiles)]
|
||||
}
|
||||
FeatureKind::Loft { sections, .. } => sections.iter_mut().filter(|s| s.point.is_none()).map(|s| (s.sketch, &mut s.profiles)).collect(),
|
||||
FeatureKind::SurfaceLoft { sections } => sections.iter_mut().filter_map(|s| Some((s.sketch, s.profiles.as_mut()?))).collect(),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -1541,6 +1561,7 @@ impl FeatureKind {
|
||||
FeatureKind::Loft { sections, .. } => {
|
||||
sections.iter().filter(|section| section.point.is_none()).map(|section| (section.sketch, §ion.profiles)).collect()
|
||||
}
|
||||
FeatureKind::SurfaceLoft { sections } => sections.iter().filter_map(|s| Some((s.sketch, s.profiles.as_ref()?))).collect(),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -1648,7 +1669,7 @@ impl FeatureKind {
|
||||
},
|
||||
FeatureKind::Mirror { plane, .. } => plane_exprs(plane, &mut v),
|
||||
FeatureKind::Shell { thickness, .. } => v.push(thickness),
|
||||
FeatureKind::Loft { .. } | FeatureKind::Sweep { .. } | FeatureKind::SurfaceSweep { .. } => {}
|
||||
FeatureKind::Loft { .. } | FeatureKind::Sweep { .. } | FeatureKind::SurfaceSweep { .. } | FeatureKind::SurfaceLoft { .. } => {}
|
||||
FeatureKind::Draft { angle, neutral, .. } => {
|
||||
v.push(angle);
|
||||
plane_exprs(neutral, &mut v);
|
||||
|
||||
@@ -3637,7 +3637,8 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
| FeatureKind::Unstitch { .. }
|
||||
| FeatureKind::ReverseNormal { .. }
|
||||
| FeatureKind::SurfaceRevolve { .. }
|
||||
| FeatureKind::SurfaceSweep { .. } => surface::eval(doc, vals, f, st),
|
||||
| FeatureKind::SurfaceSweep { .. }
|
||||
| FeatureKind::SurfaceLoft { .. } => surface::eval(doc, vals, f, st),
|
||||
FeatureKind::Emboss { .. }
|
||||
| FeatureKind::Rib { .. }
|
||||
| FeatureKind::ReplaceFace { .. }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Evaluation of the surface features: Patch, Stitch, Thicken, Trim, Extend, Surface Extrude,
|
||||
//! Revolve and Sweep, Unstitch and Reverse Normal (kernel `surfaces`).
|
||||
//! Revolve, Sweep and Loft, Unstitch and Reverse Normal (kernel `surfaces`).
|
||||
|
||||
use super::*;
|
||||
use solidcraft_geom::Seg2;
|
||||
@@ -181,6 +181,32 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
st.bodies.push(ModelBody::new(name, body, f.id));
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::SurfaceLoft { sections } => {
|
||||
let mut secs = Vec::with_capacity(sections.len());
|
||||
for sec in sections {
|
||||
let ss = sketch_in_feature_frame(
|
||||
doc,
|
||||
st.sketch(sec.sketch)
|
||||
.ok_or_else(|| DocError::Unknown(format!("sketch {} (it must come earlier in the timeline)", sec.sketch)))?,
|
||||
f.component,
|
||||
)?;
|
||||
let profiles = match (&sec.profiles, sec.curves.is_empty()) {
|
||||
(None, true) => Some(&ProfileSel::All),
|
||||
(p, _) => p.as_ref(),
|
||||
};
|
||||
let mut chains = sheet_chains(&ss, profiles, &sec.curves)?.into_iter();
|
||||
let (Some(chain), None) = (chains.next(), chains.next()) else {
|
||||
return Err(DocError::Invalid(
|
||||
"each loft section must be one chain: one profile without holes, or curves meeting end to end".into(),
|
||||
));
|
||||
};
|
||||
secs.push((ss.plane, chain));
|
||||
}
|
||||
let body = kernel::loft_sheet(&secs)?;
|
||||
let name = new_name(st, f, 0);
|
||||
st.bodies.push(ModelBody::new(name, body, f.id));
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(DocError::Invalid("not a surface feature".into())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,8 @@
|
||||
//! `axis`, `angle`, optional `angle2` and `symmetric`) is also first saved in 18.
|
||||
//! A `SurfaceSweep` feature (Surface Sweep: `sketch`, optional `profiles` and `curves`,
|
||||
//! `path_sketch`, `path`) is also first saved in 18.
|
||||
//! A `SurfaceLoft` feature (Surface Loft: `sections`, each a `sketch` with optional `profiles`
|
||||
//! and `curves`) is also first saved in 18.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
|
||||
@@ -290,6 +290,7 @@ impl FeatureKind {
|
||||
| FeatureKind::Loft { .. }
|
||||
| FeatureKind::Sweep { .. }
|
||||
| FeatureKind::SurfaceSweep { .. }
|
||||
| FeatureKind::SurfaceLoft { .. }
|
||||
| FeatureKind::Import { .. }
|
||||
| FeatureKind::MeshImport { .. }
|
||||
| FeatureKind::BodyTransfer { .. } => {}
|
||||
|
||||
Reference in new issue
Block a user