ifc_lite_geometry/analytic/
frame.rs1use super::AnalyticCurveSegment;
8
9#[derive(Debug, Clone, Copy, PartialEq)]
12#[non_exhaustive]
13pub struct AnalyticPoint {
14 pub origin: [f64; 3],
15 pub offset: [f64; 3],
16}
17
18impl AnalyticPoint {
19 pub fn distance_to(self, other: Self) -> Option<f64> {
21 let delta = std::array::from_fn::<_, 3, _>(|axis| {
22 (self.origin[axis] - other.origin[axis]) + (self.offset[axis] - other.offset[axis])
23 });
24 let length = delta[0].hypot(delta[1]).hypot(delta[2]);
25 length.is_finite().then_some(length)
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq)]
32#[non_exhaustive]
33pub struct AnalyticSegmentFrame {
34 pub start: AnalyticPoint,
35 pub end: AnalyticPoint,
36 pub start_tangent: Option<[f64; 3]>,
37 pub end_tangent: Option<[f64; 3]>,
38}
39
40impl AnalyticCurveSegment {
41 pub fn length(&self) -> Option<f64> {
44 let value = match self {
45 Self::Line { start, end } => {
46 let delta: [f64; 3] = std::array::from_fn(|axis| end[axis] - start[axis]);
47 delta[0].hypot(delta[1]).hypot(delta[2])
48 }
49 Self::Arc { radius, sweep_angle, .. } if *radius > 0.0 => radius * sweep_angle.abs(),
50 Self::Arc { .. } => return None,
51 };
52 value.is_finite().then_some(value)
53 }
54
55 pub fn endpoint_frame(&self) -> Option<AnalyticSegmentFrame> {
58 match self {
59 Self::Line { start, end } => {
60 let delta = std::array::from_fn::<_, 3, _>(|axis| end[axis] - start[axis]);
61 let length = delta[0].hypot(delta[1]).hypot(delta[2]);
62 if !length.is_finite() || !start.iter().chain(end).all(|value| value.is_finite()) {
63 return None;
64 }
65 let tangent = (length > 0.0).then(|| delta.map(|value| value / length));
66 Some(AnalyticSegmentFrame {
67 start: AnalyticPoint { origin: *start, offset: [0.0; 3] },
68 end: AnalyticPoint { origin: *end, offset: [0.0; 3] },
69 start_tangent: tangent,
70 end_tangent: tangent,
71 })
72 }
73 Self::Arc { center, normal, x_axis, radius, start_angle, sweep_angle } => {
74 if !center.iter().chain(normal).chain(x_axis).all(|v| v.is_finite())
75 || !radius.is_finite() || *radius <= 0.0
76 || !start_angle.is_finite() || !sweep_angle.is_finite()
77 {
78 return None;
79 }
80 let y_axis = [
81 normal[1] * x_axis[2] - normal[2] * x_axis[1],
82 normal[2] * x_axis[0] - normal[0] * x_axis[2],
83 normal[0] * x_axis[1] - normal[1] * x_axis[0],
84 ];
85 let end_angle = start_angle + sweep_angle;
86 if !end_angle.is_finite() { return None; }
87 let at = |angle: f64| {
88 let (sin, cos) = angle.sin_cos();
89 let offset = std::array::from_fn::<_, 3, _>(|axis| {
90 radius * (cos * x_axis[axis] + sin * y_axis[axis])
91 });
92 let tangent = std::array::from_fn::<_, 3, _>(|axis| {
93 sweep_angle.signum() * (-sin * x_axis[axis] + cos * y_axis[axis])
94 });
95 (AnalyticPoint { origin: *center, offset }, tangent)
96 };
97 let (start, start_tangent) = at(*start_angle);
98 let (end, end_tangent) = at(end_angle);
99 if !start.offset.iter().chain(end.offset.iter())
100 .chain(start_tangent.iter()).chain(end_tangent.iter())
101 .all(|v| v.is_finite())
102 { return None; }
103 let tangent = |vector: [f64; 3]| {
104 let length = vector[0].hypot(vector[1]).hypot(vector[2]);
105 (length > 0.0 && length.is_finite()).then(|| vector.map(|v| v / length))
106 };
107 Some(AnalyticSegmentFrame {
108 start, end,
109 start_tangent: (*sweep_angle != 0.0).then(|| tangent(start_tangent)).flatten(),
110 end_tangent: (*sweep_angle != 0.0).then(|| tangent(end_tangent)).flatten(),
111 })
112 }
113 }
114 }
115}