axiolid_construct/
fillet_variable.rs1use axiolid_contracts::{GeomError, GeomResult};
18use axiolid_core::{Point2, Point3, Scalar};
19use axiolid_curve::KnotSpec;
20use axiolid_surface::BSplineSurface;
21
22use crate::feature::BlendCorner;
23
24#[derive(Debug, Clone, Copy, PartialEq)]
26pub struct TaperedFillet {
27 pub corner: usize,
29 pub bottom_radius: Scalar,
31 pub top_radius: Scalar,
33}
34
35fn arc_control_points(
42 centre: Point2,
43 start: Point2,
44 sweep: Scalar,
45 height: Scalar,
46) -> Option<([Point3; 3], Scalar)> {
47 let half = sweep.abs() / 2.0;
48 let weight = half.cos();
49 if weight <= Scalar::EPSILON {
52 return None;
53 }
54 let radial = start - centre;
55 let radius = radial.length();
56 if radius <= Scalar::EPSILON {
57 return None;
58 }
59
60 let (sin, cos) = sweep.sin_cos();
61 let end = Point2::new(
64 centre.x + radial.x * cos - radial.y * sin,
65 centre.y + radial.x * sin + radial.y * cos,
66 );
67 let mid_angle = sweep / 2.0;
69 let (mid_sin, mid_cos) = mid_angle.sin_cos();
70 let bisector = Point2::new(
71 radial.x * mid_cos - radial.y * mid_sin,
72 radial.x * mid_sin + radial.y * mid_cos,
73 );
74 let shoulder_distance = radius / weight;
75 let shoulder = Point2::new(
76 centre.x + bisector.x / radius * shoulder_distance,
77 centre.y + bisector.y / radius * shoulder_distance,
78 );
79
80 Some((
81 [
82 Point3::new(start.x, start.y, height),
83 Point3::new(shoulder.x, shoulder.y, height),
84 Point3::new(end.x, end.y, height),
85 ],
86 weight,
87 ))
88}
89
90pub fn tapered_blend_surface(
96 bottom: &BlendCorner,
97 top: &BlendCorner,
98 height: Scalar,
99) -> GeomResult<BSplineSurface> {
100 let (bottom_points, bottom_weight) =
101 arc_control_points(bottom.centre, bottom.start, bottom.sweep, 0.0).ok_or_else(|| {
102 GeomError::Degenerate("tapered fillet arc spans half a turn or more".to_owned())
103 })?;
104 let (top_points, top_weight) = arc_control_points(top.centre, top.start, top.sweep, height)
105 .ok_or_else(|| {
106 GeomError::Degenerate("tapered fillet arc spans half a turn or more".to_owned())
107 })?;
108
109 Ok(BSplineSurface {
110 u_degree: 2,
111 v_degree: 1,
112 control_points: vec![
113 vec![bottom_points[0], top_points[0]],
114 vec![bottom_points[1], top_points[1]],
115 vec![bottom_points[2], top_points[2]],
116 ],
117 u_knots: vec![0.0, 1.0],
118 u_multiplicities: vec![3, 3],
119 v_knots: vec![0.0, 1.0],
120 v_multiplicities: vec![2, 2],
121 weights: Some(vec![
123 vec![1.0, 1.0],
124 vec![bottom_weight, top_weight],
125 vec![1.0, 1.0],
126 ]),
127 u_closed: false,
128 v_closed: false,
129 knot_spec: KnotSpec::Unspecified,
130 self_intersect: Some(false),
131 })
132}