feat: fillet radius tapers along an edge to the room there (#749)
Adds `fit: taper` to Fillet: where a constant radius doesn't fit along a straight edge, it follows the room there as a smooth spline, down to zero where a face closes to a point. Taper to Fit is in the Fillet dialog, and the feature's warning says how far it tapered.
This commit is contained in:
1 parent
1a5e961362
commit
1d30e4e1d2
8 files changed
+576
-197
No files matched your search
@@ -997,13 +997,16 @@ pub enum FilletStyle {
|
||||
}
|
||||
|
||||
/// A fillet radius too large for the geometry around its edges: refused (`Exact`, the default),
|
||||
/// or reduced to the largest that works for all of them (`Reduce`), with a warning.
|
||||
/// reduced to the largest that works for all of them (`Reduce`), or tapered along each edge it
|
||||
/// doesn't fit to the room there, down to zero where a face closes to a point (`Taper`); a
|
||||
/// warning says what was done.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum FilletFit {
|
||||
#[default]
|
||||
Exact,
|
||||
Reduce,
|
||||
Taper,
|
||||
}
|
||||
|
||||
impl FilletFit {
|
||||
|
||||
+19
-1
@@ -265,7 +265,8 @@ fn thread_groove(t: &ThreadInfo, radius: f64, open: (bool, bool)) -> Result<soli
|
||||
|
||||
/// A constant fillet of `edges`; with [`FilletFit::Reduce`](crate::FilletFit::Reduce), a radius too
|
||||
/// large for the geometry around them is reduced where it doesn't fit, to the largest that works
|
||||
/// for each group of edges sharing a face; the radius per edge is returned.
|
||||
/// for each group of edges sharing a face; the radius per edge is returned. With
|
||||
/// [`FilletFit::Taper`](crate::FilletFit::Taper) it tapers along each edge it doesn't fit.
|
||||
fn fillet_fitted(
|
||||
body: &kernel::Body,
|
||||
edges: &[Vec3],
|
||||
@@ -278,6 +279,7 @@ fn fillet_fitted(
|
||||
Some((used, b)) => Ok((b, used.iter().any(|u| *u < r).then_some(used))),
|
||||
None => Err(kernel::KernelError::TooLarge(why)),
|
||||
},
|
||||
Err(kernel::KernelError::TooLarge(_)) if fit == crate::FilletFit::Taper => kernel::fillet_tapered(body, edges, r).map(|(b, _)| (b, None)),
|
||||
other => other.map(|b| (b, None)),
|
||||
}
|
||||
}
|
||||
@@ -296,6 +298,15 @@ fn reduced_note(used: &[f64], asked: f64) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// The warning of a fillet that tapered on some of its edges (a taper per edge, None where the
|
||||
/// radius is constant), naming the smallest radius a taper ends at.
|
||||
fn tapered_note(tapers: &[Option<kernel::Taper>]) -> Option<String> {
|
||||
let ends: Vec<f64> = tapers.iter().flatten().flat_map(|t| [t.spline.radii.first(), t.spline.radii.last()]).flatten().copied().collect();
|
||||
let low = ends.into_iter().reduce(f64::min)?;
|
||||
let n = tapers.iter().flatten().count();
|
||||
Some(format!("the radius tapers to the room on {n} of {} edges, down to {} mm", tapers.len(), show_mm(low)))
|
||||
}
|
||||
|
||||
/// Add `note` to a feature's warning.
|
||||
fn append_warning(warning: &mut Option<String>, note: String) {
|
||||
*warning = Some(match warning.take() {
|
||||
@@ -3883,6 +3894,13 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
None => return Err(DocError::Invalid("no edges to fillet".into())),
|
||||
}
|
||||
}
|
||||
FeatureKind::Fillet { fit: crate::FilletFit::Taper, .. } => {
|
||||
let (b, tapers) = kernel::fillet_tapered(&mb.body, &edges, r)?;
|
||||
if let Some(note) = tapered_note(&tapers) {
|
||||
append_warning(warning, note);
|
||||
}
|
||||
(b, input)
|
||||
}
|
||||
FeatureKind::Fillet { fit, .. } => {
|
||||
let (b, used) = fillet_fitted(&mb.body, &edges, r, *fit)?;
|
||||
if let Some(used) = used {
|
||||
|
||||
@@ -85,6 +85,8 @@
|
||||
//! edges of a solid or shelled body that become bends). A conversion without `bends` is the
|
||||
//! plate conversion it always was and saves as before; 0.8.0 would drop the bend edges and
|
||||
//! refuse the body as no plate.
|
||||
//! A fillet's `fit` can be `taper` (the radius follows the room along each edge it doesn't
|
||||
//! fit). Fillets with `exact` or `reduce` save as before; 0.8.0 would refuse the unknown fit.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
|
||||
Reference in new issue
Block a user