Loop blends: accept straight wall seams that are not lines (#335)

* Loop blends accept straight wall seams that are not lines

Filleting or chamfering the top outline of two overlapping cylinders whose
concave edges were rounded failed with "the wall seams must be straight":
the round's contact lines with the cylinders are straight NURBS curves, and
the loop blend only accepted seams stored as lines. A seam now counts as
straight when every sample lies on its chord; it is trimmed as a line as
before.

* Fillet and chamfer arrows point into the body

The radius arrow pointed out of the body along the edge's bisector, so
dragging it away from the edge grew the round. It now points into the
material, like the shell's thickness arrow: dragging toward the body grows
the radius or distance, dragging out of it shrinks it.
This commit is contained in:
Pablo López authored and GitHub committed 2026-10-12 13:15:30 -04:00
1 parent e6252ae094
commit fc8a5b21cf
4 files changed
+71 -6

No files matched your search

+36
View File
@@ -209,6 +209,42 @@ fn fillet_the_edges_between_two_overlapping_cylinders() {
assert!(rel(v, expect) < 2e-4, "{v} vs {expect}");
}
/// The top outline of two overlapping cylinders whose concave edges were rounded: the walls of
/// the rounds meet the cylinders along the rolling-ball contact lines, straight but not lines.
#[test]
fn fillet_and_chamfer_the_top_loop_of_rounded_overlapping_cylinders() {
use std::f64::consts::{PI, TAU};
let (big, round, h) = (25.0_f64, 5.0_f64, 10.0);
let mut s = Session::default();
run(&mut s, "sketch.create", json!({"plane": "XY"}));
run(&mut s, "sketch.circle.center", json!({"center": [0, 0], "radius": big}));
run(&mut s, "sketch.circle.center", json!({"center": [-big, 0], "radius": big}));
run(&mut s, "sketch.finish", json!({}));
run(&mut s, "solid.extrude", json!({"distance": h, "profiles": [0, 1, 2]}));
// Plan view: ball centres at x = −12.5, |y| = yb; each round's arc turns by th, each big arc by
// TAU − 2·a1, and the loop turns by TAU in all.
let half = big / 2.0;
let yb = ((big + round).powi(2) - half * half).sqrt();
run(&mut s, "solid.fillet", json!({"edges": [[-half, half * 3f64.sqrt(), 5], [-half, -half * 3f64.sqrt(), 5]], "radius": round}));
let (a1, th) = (yb.atan2(half), 2.0 * (half / (big + round)).asin());
let base = volume(&mut s);
let top = [[big, 0.0, h], [-2.0 * big, 0.0, h], [-half, yb - round, h], [-half, round - yb, h]];
// Removed volume by Pappus: the corner's section swept along the loop at its centroid's offset.
let removed = |area: f64, xb: f64| area * (2.0 * (TAU - 2.0 * a1) * (big - xb) + 2.0 * th * (round + xb));
run(&mut s, "solid.fillet", json!({"edges": top, "radius": 2}));
let r: f64 = 2.0;
let expect = base - removed(r * r * (1.0 - PI / 4.0), (10.0 - 3.0 * PI) / (3.0 * (4.0 - PI)) * r);
let v = volume(&mut s);
assert!((v - expect).abs() < 1.5, "{v} vs {expect}");
let m = run(&mut s, "inspect.measure", json!({}));
assert_eq!(m["bodies"][0]["kernel"]["faces"].as_u64(), Some(10), "{m}");
run(&mut s, "edit.undo", json!({}));
run(&mut s, "solid.chamfer", json!({"edges": top, "distance": 2}));
let expect = base - removed(2.0, 2.0 / 3.0);
let v = volume(&mut s);
assert!((v - expect).abs() < 1.5, "{v} vs {expect}");
}
/// Sketch a 40 x 30 rectangle with dimensions, extrude 20, fillet a vertical edge, cut a hole.
#[test]
fn box_fillet_cut_part() {