1use approx::AbsDiffEq;
2
3use crate::{Aabb, Point, Scalar, Vector};
4
5#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
10pub struct Circle<const D: usize> {
11 center: Point<D>,
12 a: Vector<D>,
13 b: Vector<D>,
14}
15
16impl<const D: usize> Circle<D> {
17 pub fn new(
28 center: impl Into<Point<D>>,
29 a: impl Into<Vector<D>>,
30 b: impl Into<Vector<D>>,
31 ) -> Self {
32 let center = center.into();
33 let a = a.into();
34 let b = b.into();
35
36 assert_eq!(
37 a.magnitude(),
38 b.magnitude(),
39 "`a` and `b` must be of equal length"
40 );
41 assert_ne!(
42 a.magnitude(),
43 Scalar::ZERO,
44 "circle radius must not be zero"
45 );
46 assert!(
50 a.dot(&b) < Scalar::default_epsilon(),
51 "`a` and `b` must be perpendicular to each other"
52 );
53
54 Self { center, a, b }
55 }
56
57 pub fn from_center_and_radius(
59 center: impl Into<Point<D>>,
60 radius: impl Into<Scalar>,
61 ) -> Self {
62 let radius = radius.into();
63
64 let mut a = [Scalar::ZERO; D];
65 let mut b = [Scalar::ZERO; D];
66
67 a[0] = radius;
68 b[1] = radius;
69
70 Self::new(center, a, b)
71 }
72
73 pub fn center(&self) -> Point<D> {
75 self.center
76 }
77
78 pub fn radius(&self) -> Scalar {
80 self.a().magnitude()
81 }
82
83 pub fn a(&self) -> Vector<D> {
91 self.a
92 }
93
94 pub fn b(&self) -> Vector<D> {
100 self.b
101 }
102
103 #[must_use]
105 pub fn reverse(mut self) -> Self {
106 self.b = -self.b;
107 self
108 }
109
110 pub fn point_to_circle_coords(
123 &self,
124 point: impl Into<Point<D>>,
125 ) -> Point<1> {
126 let vector = (point.into() - self.center).to_uv();
127 let atan = Scalar::atan2(vector.v, vector.u);
128 let coord = if atan >= Scalar::ZERO {
129 atan
130 } else {
131 atan + Scalar::TAU
132 };
133 Point::from([coord])
134 }
135
136 pub fn point_from_circle_coords(
138 &self,
139 point: impl Into<Point<1>>,
140 ) -> Point<D> {
141 self.center + self.vector_from_circle_coords(point.into().coords)
142 }
143
144 pub fn vector_from_circle_coords(
146 &self,
147 vector: impl Into<Vector<1>>,
148 ) -> Vector<D> {
149 let angle = vector.into().t;
150 let (sin, cos) = angle.sin_cos();
151
152 self.a * cos + self.b * sin
153 }
154
155 pub fn aabb(&self) -> Aabb<D> {
157 let center_to_min_max = Vector::from_component(self.radius());
158
159 Aabb {
160 min: self.center() - center_to_min_max,
161 max: self.center() + center_to_min_max,
162 }
163 }
164}
165
166impl<const D: usize> approx::AbsDiffEq for Circle<D> {
167 type Epsilon = <Scalar as approx::AbsDiffEq>::Epsilon;
168
169 fn default_epsilon() -> Self::Epsilon {
170 Scalar::default_epsilon()
171 }
172
173 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
174 self.center.abs_diff_eq(&other.center, epsilon)
175 && self.a.abs_diff_eq(&other.a, epsilon)
176 && self.b.abs_diff_eq(&other.b, epsilon)
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use std::f64::consts::{FRAC_PI_2, PI};
183
184 use crate::{Point, Vector};
185
186 use super::Circle;
187
188 #[test]
189 fn point_to_circle_coords() {
190 let circle = Circle {
191 center: Point::from([1., 2., 3.]),
192 a: Vector::from([1., 0., 0.]),
193 b: Vector::from([0., 1., 0.]),
194 };
195
196 assert_eq!(
197 circle.point_to_circle_coords([2., 2., 3.]),
198 Point::from([0.]),
199 );
200 assert_eq!(
201 circle.point_to_circle_coords([1., 3., 3.]),
202 Point::from([FRAC_PI_2]),
203 );
204 assert_eq!(
205 circle.point_to_circle_coords([0., 2., 3.]),
206 Point::from([PI]),
207 );
208 assert_eq!(
209 circle.point_to_circle_coords([1., 1., 3.]),
210 Point::from([FRAC_PI_2 * 3.]),
211 );
212 }
213}