Fillets and chamfers along open chains of curved edges that end at planar faces square to them (the round edge of a D-shaped block): the end faces take the blend's end arcs and their edges stop at the contacts; the curved blend is tried only for curved edges or edges between curved faces, and refuses a blend larger than its neighbouring faces

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 08:32:45 +00:00
1 parent ae4570ed39
commit f7703ca720
4 files changed
+216 -58

No files matched your search

+38 -22
View File
@@ -341,7 +341,8 @@ fn blend(body: &Body, edges: &[Vec3], r: f64, shape: Shape, what: &str) -> Resul
let edge = Body::unique_edges(&solid).into_iter().nth(idx).ok_or_else(|| KernelError::Invalid("edge index".into()))?;
let out = match guard(what, || blend_one(&solid, &edge, size, r, shape)) {
Ok(o) => o,
// A curved edge: roll a ball along its smooth closed chain.
// A curved edge, or one between curved faces: roll a ball along its smooth chain.
Err(e) if !curved_case(&solid, &edge) => return Err(e),
Err(e) => match smooth_chain(&solid, &edge) {
Some(ids) => guard(what, || crate::curveblend::curve_blend(&solid, &ids, size, r, shape == Shape::Round))
.map_err(|e2| KernelError::Failed(format!("{e2} ({e})")))?,
@@ -353,7 +354,16 @@ fn blend(body: &Body, edges: &[Vec3], r: f64, shape: Shape, what: &str) -> Resul
Ok(cur)
}
/// The closed chain of edges continuing `edge` smoothly at both ends (its ids), if any.
/// Is the edge curved, or does it lie between faces that aren't both planar?
fn curved_case(solid: &Solid, edge: &mt::Edge) -> bool {
if !matches!(edge.curve(), mt::Curve::Line(_)) {
return true;
}
solid.face_iter().filter(|f| f.edge_iter().any(|e| e.id() == edge.id())).any(|f| !matches!(f.oriented_surface(), mt::Surface::Plane(_)))
}
/// The chain of edges continuing `edge` smoothly at its ends (its ids): closed, or open where
/// the next edge turns sharply.
fn smooth_chain(solid: &Solid, edge: &mt::Edge) -> Option<Vec<mt::EdgeID>> {
use mt::{BoundedCurve, ParametricCurve};
let all = Body::unique_edges(solid);
@@ -364,28 +374,34 @@ fn smooth_chain(solid: &Solid, edge: &mt::Edge) -> Option<Vec<mt::EdgeID>> {
Vec3::new(d.x, d.y, d.z).normalized()
};
let mut ids = vec![edge.id()];
let start = edge.front().clone();
let (mut v, mut d) = (edge.back().clone(), dir(edge, true)?);
for _ in 0..200 {
if v == start {
return Some(ids);
}
let next = all.iter().filter(|e| !ids.contains(&e.id()) && (*e.front() == v || *e.back() == v)).find_map(|e| {
let fwd = *e.front() == v;
let t = if fwd { dir(e, false)? } else { -dir(e, true)? };
(t.dot(d) > 1.0 - 1e-4).then(|| (e.clone(), fwd))
});
let (e, fwd) = next?;
ids.push(e.id());
if fwd {
v = e.back().clone();
d = dir(&e, true)?;
} else {
v = e.front().clone();
d = -dir(&e, false)?;
// Walk on from one end of the edge (leaving vertex `v` in direction `d`).
let walk = |mut v: mt::Vertex, mut d: Vec3, ids: &mut Vec<mt::EdgeID>, stop: &mt::Vertex| -> Option<bool> {
for _ in 0..200 {
if v == *stop {
return Some(true);
}
let next = all.iter().filter(|e| !ids.contains(&e.id()) && (*e.front() == v || *e.back() == v)).find_map(|e| {
let fwd = *e.front() == v;
let t = if fwd { dir(e, false)? } else { -dir(e, true)? };
(t.dot(d) > 1.0 - 1e-4).then(|| (e.clone(), fwd))
});
let Some((e, fwd)) = next else { return Some(false) };
ids.push(e.id());
if fwd {
v = e.back().clone();
d = dir(&e, true)?;
} else {
v = e.front().clone();
d = -dir(&e, false)?;
}
}
None
};
let closed = walk(edge.back().clone(), dir(edge, true)?, &mut ids, edge.front())?;
if !closed {
walk(edge.front().clone(), -dir(edge, false)?, &mut ids, edge.back())?;
}
None
Some(ids)
}
fn edge_mid(e: &mt::Edge) -> Option<Vec3> {