truck_geometry/specifieds/
circle.rs

1use super::*;
2use std::f64::consts::PI;
3
4impl<P> UnitCircle<P> {
5    /// constructor
6    #[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}