Curvature-continuous (G2) fillets for straight planar edges (#862)

Fillet gets a Curvature (G2) radius type (type: "curvature", alias "g2"): a quintic blend whose size is the equivalent circular setback, for convex and concave straight edges between planar faces with a perpendicular planar end face at each end. Unsupported cases are refused rather than falling back to a circular fillet, and a blend that would cross a loop in its end face is refused. Persisted as a fillet style under format 19; labels in en/de/ja/ko/zh.

Co-authored-by: midasdf <midasdf@users.noreply.github.com>
This commit is contained in:
midasdfandmidasdf authored and GitHub committed 2026-10-14 04:26:53 -04:00
1 parent 3669d4b3d0
commit 6e84c03946
18 files changed
+323 -13

No files matched your search

+22 -3
View File
@@ -689,7 +689,16 @@ impl Dialog {
Kind::Extrude { to_offset, .. } if to_object_on(&self.inputs) => ("To Offset", ValueKind::Length, to_offset),
Kind::Extrude { distance, .. } => ("Distance", ValueKind::Length, distance),
Kind::Revolve { angle, .. } => ("Angle", ValueKind::Angle, angle),
Kind::Fillet { radius, chamfer, .. } => (if *chamfer { "Distance" } else { "Radius" }, ValueKind::Length, radius),
Kind::Fillet { radius, chamfer, .. } => {
let label = if *chamfer {
"Distance"
} else if matches!(self.extra.get("type").and_then(Value::as_str), Some("curvature" | "g2")) {
"Equivalent Radius"
} else {
"Radius"
};
(label, ValueKind::Length, radius)
}
Kind::Shell { thickness } => ("Thickness", ValueKind::Length, thickness),
Kind::Draft { angle } => ("Angle", ValueKind::Angle, angle),
Kind::Hole { diameter, .. } => ("Diameter", ValueKind::Length, diameter),
@@ -2059,7 +2068,13 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
&mut d.extra,
"fi_type",
"type",
&[("Constant", "constant"), ("Chord Length", "chord"), ("Variable", "variable"), ("Spline", "spline")],
&[
("Constant", "constant"),
("Chord Length", "chord"),
("Variable", "variable"),
("Spline", "spline"),
("Curvature (G2)", "curvature"),
],
);
ui.end_row();
}
@@ -2069,6 +2084,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
match rtype {
1 => "Chord Length",
2 | 3 => "Start Radius",
4 => "Equivalent Radius",
_ => "Radius",
}
} else if *ctype == 0 {
@@ -4027,6 +4043,9 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
if let FeatureKind::Fillet { style, .. } = &f.kind {
match style {
FilletStyle::Constant => {}
FilletStyle::Curvature => {
d.extra.insert("type".into(), json!("curvature"));
}
FilletStyle::Chord => {
d.extra.insert("type".into(), json!("chord"));
}
@@ -4043,7 +4062,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
d.extra.insert("knots".into(), json!(knots));
d.extra.insert("radii".into(), json!(radii));
}
FilletStyle::Constant | FilletStyle::Chord => {}
FilletStyle::Constant | FilletStyle::Chord | FilletStyle::Curvature => {}
}
// The start was the first edge's last point: ends swapped.
let first_end = edges.first().and_then(|p| edge_sel(s, *p)).and_then(|x| match x {