fix: tapered extrude of profiles mixing lines and tangent arcs (#546) (#587)

Tapered extrudes accept profiles that mix lines and tangent arcs (slots, D-profiles, rounded rectangles): each arc keeps its centre and angles and its radius moves with the taper, so its side stays an exact cone; lines are offset and mitred. An arc meeting another edge at a corner is still refused. Un-ignores the #546 regression test. Fixes #546.

Co-authored-by: jonaswre <jonas.wrede@beyondit.gmbh>
This commit is contained in:
Jonas Wrede authored and GitHub committed 2026-10-13 04:28:21 -04:00
1 parent 651e8e0853
commit abff7a49bb
3 files changed
+106 -3

No files matched your search

+32 -1
View File
@@ -1,6 +1,6 @@
use std::f64::consts::PI;
use solidcraft_geom::{Loop2, Plane, Region2, Vec2, Vec3};
use solidcraft_geom::{Loop2, Plane, Region2, Seg2, Vec2, Vec3};
use crate::*;
@@ -429,6 +429,37 @@ fn tapered_extrudes() {
assert!(extrude_tapered(&Plane::XY, &c, 10.0, 1.0, -60f64.to_radians()).is_err(), "closes the profile");
}
/// A slot (two lines and two half-circles, tangent) tapered inward: every edge moves in by
/// d = z · tan 8°, so A(d) = A₀ − P·d + π·d² (#546). An arc meeting a line at a corner is refused.
#[test]
fn tapered_extrude_of_lines_and_arcs() {
let (r, l, h, a) = (5.0, 20.0, 12.0, 8f64.to_radians());
let slot = Loop2 {
segs: vec![
Seg2::Line { a: Vec2::new(0.0, -r), b: Vec2::new(l, -r) },
Seg2::Arc { center: Vec2::new(l, 0.0), radius: r, start: -PI / 2.0, sweep: PI },
Seg2::Line { a: Vec2::new(l, r), b: Vec2::new(0.0, r) },
Seg2::Arc { center: Vec2::ZERO, radius: r, start: PI / 2.0, sweep: PI },
],
};
let b = extrude_tapered(&Plane::XY, &Region2 { outer: slot, holes: vec![] }, h, 1.0, -a).unwrap();
let (a0, p, t) = (2.0 * r * l + PI * r * r, 2.0 * l + 2.0 * PI * r, a.tan());
let v = a0 * h - p * t * h * h / 2.0 + PI * t * t * h.powi(3) / 3.0;
let m = measure(&b).unwrap();
assert!(rel(m.volume, v) < 5e-4, "{} vs {v}", m.volume);
assert_eq!(m.merged.faces, 6);
// A quarter disc: the arc meets both lines at right angles.
let quarter = Loop2 {
segs: vec![
Seg2::Line { a: Vec2::ZERO, b: Vec2::new(r, 0.0) },
Seg2::Arc { center: Vec2::ZERO, radius: r, start: 0.0, sweep: PI / 2.0 },
Seg2::Line { a: Vec2::new(0.0, r), b: Vec2::ZERO },
],
};
let e = extrude_tapered(&Plane::XY, &Region2 { outer: quarter, holes: vec![] }, h, 1.0, a).unwrap_err();
assert!(e.to_string().contains("arc meets another edge at a corner"), "{e}");
}
#[test]
fn polyhedra_shell_and_draft() {
let b = box_solid(Vec3::ZERO, Vec3::new(50.0, 40.0, 30.0)).unwrap();