Boundary Fill (SurfaceSculpt): solids from the cells tool bodies enclose (inside some tools, outside the rest), each picked by a point, as new bodies or joined/cut/intersected, tools optionally removed
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
0daeb0c9fc
commit
1e515547d8
6 files changed
+81
-1
No files matched your search
@@ -216,6 +216,7 @@ impl FeatureKind {
|
||||
dir(direction);
|
||||
}
|
||||
FeatureKind::Lip { face, .. } => pt(face),
|
||||
FeatureKind::BoundaryFill { cells, .. } => cells.iter_mut().for_each(pt),
|
||||
FeatureKind::Rest { position, direction, along, .. } => {
|
||||
pt(position);
|
||||
dir(direction);
|
||||
|
||||
@@ -633,6 +633,17 @@ pub enum FeatureKind {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
body: Option<String>,
|
||||
},
|
||||
/// Boundary Fill: solids from the cells the tool bodies enclose (a cell: inside some
|
||||
/// tools and outside the rest), each picked by a point inside it.
|
||||
BoundaryFill {
|
||||
tools: Vec<String>,
|
||||
cells: Vec<Vec3>,
|
||||
#[serde(default)]
|
||||
operation: Operation,
|
||||
/// Remove the tool bodies afterwards.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
remove_tools: bool,
|
||||
},
|
||||
/// Remove bodies from the model (from here on in the timeline).
|
||||
Remove {
|
||||
bodies: Vec<String>,
|
||||
@@ -925,6 +936,7 @@ impl FeatureKind {
|
||||
FeatureKind::Lip { .. } => "GrooveFeature",
|
||||
FeatureKind::SnapFit { .. } => "SnapFitFeature",
|
||||
FeatureKind::Rest { .. } => "RestFeature",
|
||||
FeatureKind::BoundaryFill { .. } => "BoundaryFillFeature",
|
||||
FeatureKind::SheetBase { .. } => "BaseFlangeFeature",
|
||||
FeatureKind::SheetContour { .. } => "ContourFlangeFeature",
|
||||
FeatureKind::SheetFlange { .. } => "EdgeFlangeFeature",
|
||||
@@ -984,6 +996,7 @@ impl FeatureKind {
|
||||
FeatureKind::Lip { .. } => "Groove",
|
||||
FeatureKind::SnapFit { .. } => "SnapFit",
|
||||
FeatureKind::Rest { .. } => "Rest",
|
||||
FeatureKind::BoundaryFill { .. } => "BoundaryFill",
|
||||
FeatureKind::SheetBase { .. } => "BaseFlange",
|
||||
FeatureKind::SheetContour { .. } => "ContourFlange",
|
||||
FeatureKind::SheetFlange { .. } => "EdgeFlange",
|
||||
@@ -1151,7 +1164,7 @@ impl FeatureKind {
|
||||
v.extend(depth.iter().map(String::as_str));
|
||||
}
|
||||
FeatureKind::ReplaceFace { target, .. } => plane_exprs(target, &mut v),
|
||||
FeatureKind::Align { .. } | FeatureKind::Remove { .. } => {}
|
||||
FeatureKind::Align { .. } | FeatureKind::Remove { .. } | FeatureKind::BoundaryFill { .. } => {}
|
||||
FeatureKind::Stitch { tolerance, .. } => v.push(tolerance),
|
||||
FeatureKind::Thicken { thickness, .. } => v.push(thickness),
|
||||
FeatureKind::SurfaceTrim { plane, .. } => plane_exprs(plane, &mut v),
|
||||
|
||||
@@ -2241,6 +2241,36 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
doc.resolve_plane(vals, plane, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::BoundaryFill { tools, cells, operation, remove_tools } => {
|
||||
if tools.is_empty() || tools.len() > 64 || cells.is_empty() || cells.len() > 1000 {
|
||||
return Err(DocError::Invalid("Boundary Fill needs 1…64 tool bodies and 1…1000 cells".into()));
|
||||
}
|
||||
let tb: Vec<ModelBody> =
|
||||
tools.iter().map(|t| st.body(t).cloned().ok_or_else(|| DocError::Unknown(format!("body `{t}`")))).collect::<Result<_>>()?;
|
||||
let mut made = Vec::new();
|
||||
for p in cells {
|
||||
// The cell: inside the tools that hold the point, outside the others.
|
||||
let inside: Vec<bool> = tb.iter().map(|b| b.mesh().contains(*p)).collect();
|
||||
let mut acc: Option<Body> = None;
|
||||
for (b, _) in tb.iter().zip(&inside).filter(|(_, i)| **i) {
|
||||
acc = match acc {
|
||||
None => Some(b.body.clone()),
|
||||
Some(a) => kernel::boolean(&a, &b.body, BoolOp::Intersect)?,
|
||||
};
|
||||
}
|
||||
let Some(mut cell) = acc else {
|
||||
return Err(DocError::Invalid(format!("the point {p:?} is outside every tool: no enclosed cell there")));
|
||||
};
|
||||
for (b, _) in tb.iter().zip(&inside).filter(|(_, i)| !**i) {
|
||||
cell = kernel::boolean(&cell, &b.body, BoolOp::Cut)?.ok_or_else(|| DocError::Invalid("a cell came out empty".into()))?;
|
||||
}
|
||||
made.push(cell);
|
||||
}
|
||||
if *remove_tools {
|
||||
st.bodies.retain(|b| !tools.contains(&b.name));
|
||||
}
|
||||
apply_op(st, f, made, *operation, &[])
|
||||
}
|
||||
FeatureKind::Split { body, plane, tool } => {
|
||||
let i = st.bodies.iter().position(|b| &b.name == body).ok_or_else(|| DocError::Unknown(format!("body `{body}`")))?;
|
||||
let Some(mb) = st.bodies.get(i).cloned() else { return Err(DocError::Unknown(format!("body `{body}`"))) };
|
||||
|
||||
@@ -246,6 +246,7 @@ impl FeatureKind {
|
||||
| FeatureKind::SheetConvert { .. }
|
||||
| FeatureKind::Align { .. }
|
||||
| FeatureKind::Remove { .. }
|
||||
| FeatureKind::BoundaryFill { .. }
|
||||
| FeatureKind::Loft { .. }
|
||||
| FeatureKind::Sweep { .. }
|
||||
| FeatureKind::Import { .. }
|
||||
|
||||
Reference in new issue
Block a user