truck_geometry/specifieds/
circle.rs1use super::*;
2use std::f64::consts::PI;
3
4impl<P> UnitCircle<P> {
5 #[inline]
7 pub const fn new() -> Self { Self(std::marker::PhantomData) }
8}
9
10impl ParametricCurve for UnitCircle<Point2> {
11 type Point = Point2;
12 type Vector = Vector2;
13 #[inline]
14 fn subs(&self, t: f64) -> Self::Point { Point2::new(f64::cos(t), f64::sin(t)) }
15 #[inline]
16 fn der(&self, t: f64) -> Self::Vector { Vector2::new(-f64::sin(t), f64::cos(t)) }
17 #[inline]
18 fn der2(&self, t: f64) -> Self::Vector { Vector2::new(-f64::cos(t), -f64::sin(t)) }
19 #[inline]
20 fn parameter_range(&self) -> ParameterRange {
21 (Bound::Included(0.0), Bound::Excluded(2.0 * PI))
22 }
23}
24
25impl BoundedCurve for UnitCircle<Point2> {}
26
27impl ParametricCurve for UnitCircle<Point3> {
28 type Point = Point3;
29 type Vector = Vector3;
30 #[inline]
31 fn subs(&self, t: f64) -> Self::Point { Point3::new(f64::cos(t), f64::sin(t), 0.0) }
32 #[inline]
33 fn der(&self, t: f64) -> Self::Vector { Vector3::new(-f64::sin(t), f64::cos(t), 0.0) }
34 #[inline]
35 fn der2(&self, t: f64) -> Self::Vector { Vector3::new(-f64::cos(t), -f64::sin(t), 0.0) }
36 #[inline]
37 fn period(&self) -> Option<f64> { Some(2.0 * PI) }
38 #[inline]
39 fn parameter_range(&self) -> ParameterRange {
40 (Bound::Included(0.0), Bound::Excluded(2.0 * PI))
41 }
42}
43
44impl BoundedCurve for UnitCircle<Point3> {}
45
46impl<P> ParameterDivision1D for UnitCircle<P>
47where UnitCircle<P>: ParametricCurve<Point = P>
48{
49 type Point = P;
50 fn parameter_division(&self, range: (f64, f64), tol: f64) -> (Vec<f64>, Vec<P>) {
51 nonpositive_tolerance!(tol);
52 let tol = f64::min(tol, 0.8);
53 let delta = 2.0 * f64::acos(1.0 - tol);
54 let n = 1 + ((range.1 - range.0) / delta) as usize;
55 let params = (0..=n)
56 .map(|i| {
57 let t = i as f64 / n as f64;
58 range.0 * (1.0 - t) + range.1 * t
59 })
60 .collect::<Vec<_>>();
61 let pts = params.iter().map(|t| self.subs(*t)).collect();
62 (params, pts)
63 }
64}
65
66impl SearchNearestParameter<D1> for UnitCircle<Point2> {
67 type Point = Point2;
68 fn search_nearest_parameter<H: Into<SPHint1D>>(
69 &self,
70 pt: Point2,
71 _: H,
72 _: usize,
73 ) -> Option<f64> {
74 let v = pt.to_vec();
75 if v.magnitude().so_small() {
76 return None;
77 }
78 let v = v.normalize();
79 let theta = f64::acos(f64::clamp(v.x, -1.0, 1.0));
80 let theta = match v.y > 0.0 {
81 true => theta,
82 false => 2.0 * PI - theta,
83 };
84 Some(theta)
85 }
86}
87
88#[test]
89fn search_nearest_parameter() {
90 const N: usize = 100;
91 let circle = UnitCircle::<Point2>::new();
92 for i in 0..N {
93 let t = 2.0 * PI * i as f64 / N as f64;
94 let a = 5.0 * rand::random::<f64>() + 0.1;
95 let p = a * circle.subs(t);
96 let s = circle.search_nearest_parameter(p, None, 1).unwrap();
97 let q = a * circle.subs(s);
98 assert_near!(p, q);
99 }
100}
101
102impl SearchParameter<D1> for UnitCircle<Point2> {
103 type Point = Point2;
104 fn search_parameter<H: Into<SPHint1D>>(&self, pt: Point2, _: H, _: usize) -> Option<f64> {
105 let v = pt.to_vec();
106 if !v.magnitude().near(&1.0) {
107 return None;
108 }
109 let v = v.normalize();
110 let theta = f64::acos(f64::clamp(v.x, -1.0, 1.0));
111 let theta = match v.y > 0.0 {
112 true => theta,
113 false => 2.0 * PI - theta,
114 };
115 Some(theta)
116 }
117}
118
119#[test]
120fn search_parameter() {
121 const N: usize = 100;
122 let circle = UnitCircle::<Point2>::new();
123 for i in 1..N {
124 let t = 2.0 * PI * i as f64 / N as f64;
125 let p = circle.subs(t);
126 let s = circle.search_parameter(p, None, 1).unwrap();
127 assert_near!(s, t);
128 }
129}
130
131impl SearchNearestParameter<D1> for UnitCircle<Point3> {
132 type Point = Point3;
133 fn search_nearest_parameter<H: Into<SPHint1D>>(
134 &self,
135 pt: Point3,
136 _: H,
137 _: usize,
138 ) -> Option<f64> {
139 UnitCircle::<Point2>::new().search_nearest_parameter(Point2::new(pt.x, pt.y), None, 0)
140 }
141}
142
143impl SearchParameter<D1> for UnitCircle<Point3> {
144 type Point = Point3;
145 fn search_parameter<H: Into<SPHint1D>>(&self, pt: Point3, _: H, _: usize) -> Option<f64> {
146 if !f64::abs(pt.z).so_small() {
147 return None;
148 }
149 UnitCircle::<Point2>::new().search_parameter(Point2::new(pt.x, pt.y), None, 0)
150 }
151}
152
153#[test]
154fn parameter_division() {
155 let c = UnitCircle::<Point2>::new();
156 let (_div, pts) = c.parameter_division(c.range_tuple(), 0.05);
157 for a in pts.windows(2) {
158 let p = a[0].midpoint(a[1]);
159 assert!(p.to_vec().magnitude() > 0.95);
160 }
161}