mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 03:51:08 +00:00
solidcraft-smlib: a horn torus to rounding is no ring torus
A revolved circle whose centre is as far from the axis as its radius (a horn torus, its tube touching the axis) measured its major radius a few ulps above its minor from the profile's control points, became SMLib's ring torus, and SMLib refused it (SM_ERR_INVALID_INPUT: no ring torus of that NURBS). A ring torus now needs its tube clear of the axis by more than rounding (1e-9 of the radius, the slack the same test already takes for the circle's plane); a horn torus goes as its rational B-spline. Test: a revolved unit circle 1.5 from the axis is a ring torus; at 1, 1 + 4 ulp and 0.5 it stays a revolution (develop calls the first two ring tori). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
This commit is contained in:
@@ -331,8 +331,10 @@ impl Revolution {
|
||||
let v = [v[0].max(-FRAC_PI_2), v[1].min(FRAC_PI_2)];
|
||||
return Ok((b::Surface::Sphere { frame: frame(foot), radius }, [angle, v]));
|
||||
}
|
||||
// A ring torus only (SMLib has no other): a tube that reaches the axis to rounding (a
|
||||
// horn torus, major = minor) is none.
|
||||
let major = off.dot(x);
|
||||
if !(major > radius) {
|
||||
if !(major > radius + slack) {
|
||||
return Err(NOT_ANALYTIC);
|
||||
}
|
||||
let torus = b::Surface::Torus { frame: frame(foot), major_radius: major, minor_radius: radius };
|
||||
|
||||
@@ -237,3 +237,33 @@ fn arcs_past_a_turn_come_a_turn_back() {
|
||||
let line = Curve::Line { start: [0.0; 3], end: [1.0, 0.0, 0.0] };
|
||||
assert_eq!(turned(&line, [7.0, 9.0]), ([7.0, 9.0], 0.0));
|
||||
}
|
||||
|
||||
/// A revolved circle is SMLib's ring torus only where its tube stays off the axis: a tube that
|
||||
/// reaches it to rounding (a horn torus, its centre as far from the axis as its radius, give or
|
||||
/// take an ulp) and one across it (a spindle) go as their rational B-splines.
|
||||
#[test]
|
||||
fn only_a_tube_clear_of_its_axis_is_a_ring_torus() {
|
||||
let ring = |major: f64| {
|
||||
let frame = solidcraft_geom::brep::Frame { origin: [major, 0.0, 0.0], x_axis: [1.0, 0.0, 0.0], y_axis: [0.0, 0.0, 1.0] };
|
||||
let (profile, _) = solidcraft_geom::brep::form::curve(&Curve::Circle { frame, radius: 1.0 }, [-1.0, 1.0]).unwrap();
|
||||
let identity = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]];
|
||||
let surface =
|
||||
Surface::PlacedRevolution { profile: Box::new(profile), origin: [0.0; 3], axis: [0.0, 0.0, 1.0], matrix: identity, forward: false };
|
||||
let face = Face { surface, nurbs: None, domain: [[0.0, 1.0], [-0.5, 0.5]], loops: Vec::new() };
|
||||
let record = Record {
|
||||
version: b::VERSION,
|
||||
tolerance: 1e-6,
|
||||
regions: Vec::new(),
|
||||
shells: Vec::new(),
|
||||
faces: vec![face],
|
||||
edges: Vec::new(),
|
||||
vertices: Vec::new(),
|
||||
};
|
||||
let made = analytic::analytic(&record).unwrap();
|
||||
made.faces[0].surface.clone()
|
||||
};
|
||||
assert!(matches!(ring(1.5), Surface::Torus { .. }));
|
||||
for major in [1.0, 1.0 + 4.0 * f64::EPSILON, 0.5] {
|
||||
assert!(matches!(ring(major), Surface::PlacedRevolution { .. }), "major {major}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user