use axiolid_construct::profile::profile_rings;
use axiolid_construct::revolve::revolve;
use axiolid_core::{Point3, Scalar, Tolerance, Vec3};
use axiolid_measure::volume_properties;
use axiolid_mesh::TriMesh;
use axiolid_profile::{Profile, RectangleProfile};
fn tol_for(chord: Scalar) -> Tolerance {
Tolerance::new(chord, 1e-9).expect("tolerance")
}
fn volume(mesh: &TriMesh, tol: Tolerance) -> Scalar {
volume_properties(mesh, tol)
.expect("a revolved solid must be closed and two-manifold")
.signed_volume
}
fn rect(x: Scalar, y: Scalar) -> Profile {
Profile::Rectangle(RectangleProfile {
x,
y,
thickness: None,
outer_radius: None,
inner_radius: None,
})
}
fn shift(
rings: &axiolid_construct::profile::Rings,
dx: Scalar,
) -> axiolid_construct::profile::Rings {
let f = |p: &axiolid_core::Point2| axiolid_core::Point2::new(p.x + dx, p.y);
axiolid_construct::profile::Rings {
outer: rings.outer.iter().map(f).collect(),
holes: rings
.holes
.iter()
.map(|h| h.iter().map(f).collect())
.collect(),
}
}
#[test]
fn a_full_revolution_matches_pappus() {
let chord = 1e-5;
let rings = profile_rings(&rect(1.0, 2.0), chord, tol_for(chord)).expect("rings");
let shifted = shift(&rings, 4.0);
let mesh = revolve(
&shifted,
Point3::ZERO,
Vec3::Y,
core::f64::consts::TAU,
tol_for(chord),
)
.expect("revolve");
let want = core::f64::consts::TAU * 4.0 * 2.0;
let got = volume(&mesh, tol_for(chord));
assert!(
got < want,
"inscribed sweep must under-estimate: {got} vs {want}"
);
assert!((want - got) / want < 1e-4, "pappus volume {got} vs {want}");
}
#[test]
fn a_full_revolution_is_closed_and_seamless() {
let chord = 1e-4;
let rings = profile_rings(&rect(1.0, 2.0), chord, tol_for(chord)).expect("rings");
let shifted = shift(&rings, 4.0);
let mesh = revolve(
&shifted,
Point3::ZERO,
Vec3::Y,
core::f64::consts::TAU,
tol_for(chord),
)
.expect("revolve");
assert!(volume(&mesh, tol_for(chord)) > 0.0, "outward wound");
}
#[test]
fn a_half_revolution_is_half_the_volume() {
let chord = 1e-5;
let rings = profile_rings(&rect(1.0, 2.0), chord, tol_for(chord)).expect("rings");
let shifted = shift(&rings, 4.0);
let half = revolve(
&shifted,
Point3::ZERO,
Vec3::Y,
core::f64::consts::PI,
tol_for(chord),
)
.expect("revolve");
let want = core::f64::consts::TAU * 4.0 * 2.0 / 2.0;
let got = volume(&half, tol_for(chord));
assert!(
(want - got) / want < 1e-4,
"half revolution {got} vs {want}"
);
}
#[test]
fn a_zero_angle_is_refused() {
let chord = 1e-4;
let rings = profile_rings(&rect(1.0, 2.0), chord, tol_for(chord)).expect("rings");
let shifted = shift(&rings, 4.0);
assert!(revolve(&shifted, Point3::ZERO, Vec3::Y, 0.0, tol_for(chord)).is_err());
assert!(revolve(&shifted, Point3::ZERO, Vec3::ZERO, 1.0, tol_for(chord)).is_err());
}