use axiolid_construct::feature::{fillet_polygon_corner, fillet_polygon_corners};
use axiolid_core::Point2;
use axiolid_surface::Surface;
fn square(half: f64) -> Vec<Point2> {
vec![
Point2::new(-half, -half),
Point2::new(half, -half),
Point2::new(half, half),
Point2::new(-half, half),
]
}
fn cylinders(solid: &axiolid_brep::ExactBRep) -> Vec<(f64, Point2)> {
solid
.surfaces()
.iter()
.filter_map(|surface| match surface {
Surface::Cylinder(cylinder) => Some((
cylinder.radius,
Point2::new(cylinder.frame.origin.x, cylinder.frame.origin.y),
)),
_ => None,
})
.collect()
}
#[test]
fn four_corners_give_four_distinct_blend_surfaces() {
let ring = square(1.0);
let radius = 0.25;
let solid = fillet_polygon_corners(
&ring,
&[(0, radius), (1, radius), (2, radius), (3, radius)],
1.0,
)
.expect("four fillets");
let found = cylinders(&solid);
assert_eq!(found.len(), 4, "one blend per corner, got {}", found.len());
let inset = 1.0 - radius;
for (found_radius, centre) in &found {
assert!(
(found_radius - radius).abs() < 1e-12,
"blend radius {found_radius} should be {radius}"
);
assert!(
(centre.x.abs() - inset).abs() < 1e-12 && (centre.y.abs() - inset).abs() < 1e-12,
"centre {centre:?} should sit at (+-{inset}, +-{inset})"
);
}
}
#[test]
fn each_corner_keeps_its_own_radius() {
let ring = square(2.0);
let solid =
fillet_polygon_corners(&ring, &[(0, 0.3), (2, 0.9)], 1.0).expect("two different radii");
let mut found: Vec<f64> = cylinders(&solid).into_iter().map(|(r, _)| r).collect();
found.sort_by(f64::total_cmp);
assert_eq!(found.len(), 2, "expected two blends, got {}", found.len());
assert!(
(found[0] - 0.3).abs() < 1e-12,
"small radius, got {}",
found[0]
);
assert!(
(found[1] - 0.9).abs() < 1e-12,
"large radius, got {}",
found[1]
);
}
#[test]
fn radii_that_individually_fit_but_collide_together_are_refused() {
let ring = square(1.0);
let fits = fillet_polygon_corners(&ring, &[(0, 1.1), (1, 0.8)], 1.0);
assert!(fits.is_ok(), "1.1 + 0.8 = 1.9 fits on an edge of 2");
let collides = fillet_polygon_corners(&ring, &[(0, 1.1), (1, 1.0)], 1.0);
let error = collides.expect_err("1.1 + 1.0 = 2.1 overruns an edge of 2");
let text = format!("{error:?}");
assert!(
text.contains("too large for the edge"),
"refusal must name the shared edge, got {text}"
);
}
#[test]
fn one_corner_matches_the_single_corner_path_exactly() {
let ring = square(1.5);
let single = fillet_polygon_corner(&ring, 2, 0.4, 1.0).expect("single");
let multi = fillet_polygon_corners(&ring, &[(2, 0.4)], 1.0).expect("multi");
assert_eq!(
single.topology().faces().len(),
multi.topology().faces().len(),
"same face count"
);
assert_eq!(cylinders(&single), cylinders(&multi), "same blend geometry");
}
#[test]
fn a_duplicate_corner_is_refused_rather_than_filleted_twice() {
let ring = square(1.0);
let error = fillet_polygon_corners(&ring, &[(1, 0.2), (1, 0.3)], 1.0)
.expect_err("the same corner twice is ambiguous");
let text = format!("{error:?}");
assert!(text.contains("more than once"), "got {text}");
}