use axiolid_contracts::{GeomError, GeomResult};
use axiolid_core::{Point2, Point3, Scalar};
use axiolid_curve::KnotSpec;
use axiolid_surface::BSplineSurface;
use crate::feature::BlendCorner;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TaperedFillet {
pub corner: usize,
pub bottom_radius: Scalar,
pub top_radius: Scalar,
}
fn arc_control_points(
centre: Point2,
start: Point2,
sweep: Scalar,
height: Scalar,
) -> Option<([Point3; 3], Scalar)> {
let half = sweep.abs() / 2.0;
let weight = half.cos();
if weight <= Scalar::EPSILON {
return None;
}
let radial = start - centre;
let radius = radial.length();
if radius <= Scalar::EPSILON {
return None;
}
let (sin, cos) = sweep.sin_cos();
let end = Point2::new(
centre.x + radial.x * cos - radial.y * sin,
centre.y + radial.x * sin + radial.y * cos,
);
let mid_angle = sweep / 2.0;
let (mid_sin, mid_cos) = mid_angle.sin_cos();
let bisector = Point2::new(
radial.x * mid_cos - radial.y * mid_sin,
radial.x * mid_sin + radial.y * mid_cos,
);
let shoulder_distance = radius / weight;
let shoulder = Point2::new(
centre.x + bisector.x / radius * shoulder_distance,
centre.y + bisector.y / radius * shoulder_distance,
);
Some((
[
Point3::new(start.x, start.y, height),
Point3::new(shoulder.x, shoulder.y, height),
Point3::new(end.x, end.y, height),
],
weight,
))
}
pub fn tapered_blend_surface(
bottom: &BlendCorner,
top: &BlendCorner,
height: Scalar,
) -> GeomResult<BSplineSurface> {
let (bottom_points, bottom_weight) =
arc_control_points(bottom.centre, bottom.start, bottom.sweep, 0.0).ok_or_else(|| {
GeomError::Degenerate("tapered fillet arc spans half a turn or more".to_owned())
})?;
let (top_points, top_weight) = arc_control_points(top.centre, top.start, top.sweep, height)
.ok_or_else(|| {
GeomError::Degenerate("tapered fillet arc spans half a turn or more".to_owned())
})?;
Ok(BSplineSurface {
u_degree: 2,
v_degree: 1,
control_points: vec![
vec![bottom_points[0], top_points[0]],
vec![bottom_points[1], top_points[1]],
vec![bottom_points[2], top_points[2]],
],
u_knots: vec![0.0, 1.0],
u_multiplicities: vec![3, 3],
v_knots: vec![0.0, 1.0],
v_multiplicities: vec![2, 2],
weights: Some(vec![
vec![1.0, 1.0],
vec![bottom_weight, top_weight],
vec![1.0, 1.0],
]),
u_closed: false,
v_closed: false,
knot_spec: KnotSpec::Unspecified,
self_intersect: Some(false),
})
}