1use super::*;
2use crate::elements::polygons::Polyline;
3use num_traits::FromPrimitive;
4
5#[allow(unused_variables)]
6impl<T> Ellipse<T>
7where
8 T: Clone + Real + FloatConst,
9{
10 pub fn new<P>(center: P, radius: (T, T), angle: T) -> Self
12 where
13 P: Into<Point<T>>,
14 {
15 Self { center: center.into(), radius, rotate: angle }
16 }
17 pub fn from_coefficient(a: T, b: T, c: T, d: T, e: T, f: T) -> Self {
23 let b = half(b);
24 let d = half(d);
25 let e = half(e);
26
27 let delta_s = minor_delta(&a, &b, &c);
28 let delta_m = major_delta(&a, &b, &c, &d, &e, &f);
29 let eta = eta_invariant(&a, &b, &c);
30
31 Self {
32 center: Point { x: center_x(&a, &b, &c, &d, &e, &delta_s), y: center_y(&a, &b, &c, &d, &e, &delta_s) },
33 radius: (major_axis(&a, &c, &delta_m, &delta_s, &eta), minor_axis(&a, &c, &delta_m, &delta_s, &eta)),
34 rotate: angle(&a, &b, &c),
35 }
36 }
37
38 pub fn from_5_points(p1: Point<T>, p2: Point<T>, p3: Point<T>, p4: Point<T>, p5: Point<T>) -> Self {
40 let (a, b, c, d, e, f) = null_space(&[
41 null_space_line(p1),
42 null_space_line(p2),
43 null_space_line(p3),
44 null_space_line(p4),
45 null_space_line(p5),
46 ]);
47 Self::from_coefficient(a, b, c, d, e, f)
48 }
49}
50
51impl<T> Ellipse<T>
52where
53 T: Zero + PartialEq,
54{
55 pub fn is_horizontal(&self) -> bool {
57 self.rotate == T::zero()
58 }
59}
60
61impl<T> Ellipse<T>
62where
63 T: Real,
64{
65 pub fn major_axis(&self) -> &T {
67 &self.radius.0
68 }
69 pub fn minor_axis(&self) -> &T {
71 &self.radius.1
72 }
73 #[inline]
78 pub fn homogeneous(&self) -> (T, T, T, T, T, T) {
79 todo!()
80 }
81 #[inline]
86 pub fn coefficients(&self) -> (T, T, T, T, T, T) {
87 let (a, b, c, d, e, f) = self.homogeneous();
88 (a, half(b), c, half(d), half(e), f)
89 }
90 pub fn major_delta(&self) -> T {
101 let (a, b, c, d, e, f) = self.homogeneous();
102 major_delta(&a, &b, &c, &d, &e, &f)
103 }
104 pub fn minor_delta(&self) -> T {
114 let (a, b, c, _, _, _) = self.homogeneous();
115 minor_delta(&a, &b, &c)
116 }
117}
118
119impl<T> Ellipse<T>
120where
121 T: Clone + Real + FromPrimitive + FloatConst,
122{
123 #[track_caller]
124 pub fn sample_polygon(&self, n: usize) -> Polygon<T> {
125 debug_assert!(n >= 3, "at least 3 points");
126 let mut vertex = Vec::with_capacity(n);
127 for i in 0..n {
128 let angle = T::from_usize(i).unwrap() * two_pi() / T::from_usize(n).unwrap();
129 let x = self.sample_x(&angle);
130 let y = self.sample_y(&angle);
131 vertex.push(Point::new(x, y));
132 }
133 Polygon::from_iter(vertex)
134 }
135 #[track_caller]
136 pub fn sample_polyline(&self, n: usize) -> Polyline<T> {
137 debug_assert!(n >= 3, "at least 3 points");
138 let mut vertex = Vec::with_capacity(n);
139 for i in 0..n {
140 let angle = T::from_usize(i).unwrap() * two_pi() / T::from_usize(n).unwrap();
141 let x = self.sample_x(&angle);
142 let y = self.sample_y(&angle);
143 vertex.push(Point::new(x, y));
144 }
145 todo!()
146 }
148 pub fn sample_x(&self, t: &T) -> T {
149 self.radius.0 * self.rotate.cos() * t.cos() - self.radius.1 * self.rotate.sin() * t.sin() + self.center.x
150 }
151 pub fn sample_y(&self, t: &T) -> T {
152 self.radius.0 * self.rotate.sin() * t.cos() + self.radius.1 * self.rotate.cos() * t.cos() + self.center.y
153 }
154}
155
156#[inline(always)]
158fn major_delta<T>(a: &T, b: &T, c: &T, d: &T, e: &T, f: &T) -> T
159where
160 T: Clone + Real,
161{
162 let p1 = a.clone() + c.clone() + f.clone();
163 let p2 = two::<T>() * b.clone() * d.clone() + two::<T>() * e.clone() * f.clone();
164 let p3 = a.clone() * e.clone() * e.clone() + c.clone() * d.clone() * d.clone() + f.clone() * b.clone() * b.clone();
165 p1 + p2 - p3
166}
167
168#[inline(always)]
170fn minor_delta<T>(a: &T, b: &T, c: &T) -> T
171where
172 T: Clone + Real,
173{
174 a.clone() * c.clone() - b.clone() * b.clone()
175}
176
177#[inline(always)]
179fn eta_invariant<T>(a: &T, b: &T, c: &T) -> T
180where
181 T: Clone + Real,
182{
183 let p1 = a.clone() - c.clone();
184 let eta = p1.powi(2) + four::<T>() * b.powi(2);
185 eta.sqrt()
186}
187
188#[inline(always)]
190fn center_x<T>(_: &T, b: &T, c: &T, d: &T, e: &T, delta_s: &T) -> T
191where
192 T: Clone + Real,
193{
194 let p1 = b.clone() * e.clone() - c.clone() * d.clone();
195 p1 / delta_s.clone()
196}
197
198#[inline(always)]
200fn center_y<T>(a: &T, b: &T, _: &T, d: &T, e: &T, delta_s: &T) -> T
201where
202 T: Clone + Real,
203{
204 let p1 = b.clone() * d.clone() - a.clone() * e.clone();
205 p1 / delta_s.clone()
206}
207
208#[inline(always)]
209fn major_axis<T>(a: &T, c: &T, delta_m: &T, delta_s: &T, eta: &T) -> T
210where
211 T: Clone + Real,
212{
213 let p1 = (a.clone() + c.clone() + eta.clone()) * delta_s.clone();
214 let axis = -double(delta_m) / p1;
215 axis.sqrt()
216}
217
218#[inline(always)]
219fn minor_axis<T>(a: &T, c: &T, delta_m: &T, delta_s: &T, eta: &T) -> T
220where
221 T: Clone + Real,
222{
223 let p1 = (a.clone() + c.clone() - eta.clone()) * delta_s.clone();
224 let axis = -double(delta_m) / p1;
225 axis.sqrt()
226}
227
228fn angle<T>(a: &T, b: &T, c: &T) -> T
229where
230 T: Clone + Real + FloatConst,
231{
232 match b.is_zero() {
233 true if a < c => zero(),
234 true => half(pi()),
235 false if a < c => double(b).atan2(a.clone() - c.clone()),
236 false => half::<T>(pi()) - double(b).atan2(a.clone() - c.clone()),
237 }
238}
239
240fn null_space_line<T>(p: Point<T>) -> [T; 6]
241where
242 T: Clone + Real,
243{
244 [p.x.clone().powi(2), p.x.clone() * p.y.clone(), p.y.clone().powi(2), p.x.clone(), p.y.clone(), T::one()]
245}
246
247fn null_space<T: Clone + Real>(_matrix: &[[T; 6]; 5]) -> (T, T, T, T, T, T) {
257 todo!()
258}