1use crate::basics::{
7 uround, VertexSource, PATH_CMD_END_POLY, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP,
8 PATH_FLAGS_CCW, PATH_FLAGS_CLOSE, PI,
9};
10
11pub struct Ellipse {
19 x: f64,
20 y: f64,
21 rx: f64,
22 ry: f64,
23 scale: f64,
24 num: u32,
25 step: u32,
26 cw: bool,
27}
28
29impl Ellipse {
30 pub fn new(x: f64, y: f64, rx: f64, ry: f64, num_steps: u32, cw: bool) -> Self {
32 let mut e = Self {
33 x,
34 y,
35 rx,
36 ry,
37 scale: 1.0,
38 num: num_steps,
39 step: 0,
40 cw,
41 };
42 if e.num == 0 {
43 e.calc_num_steps();
44 }
45 e
46 }
47
48 pub fn default_new() -> Self {
50 Self {
51 x: 0.0,
52 y: 0.0,
53 rx: 1.0,
54 ry: 1.0,
55 scale: 1.0,
56 num: 4,
57 step: 0,
58 cw: false,
59 }
60 }
61
62 pub fn init(&mut self, x: f64, y: f64, rx: f64, ry: f64, num_steps: u32, cw: bool) {
64 self.x = x;
65 self.y = y;
66 self.rx = rx;
67 self.ry = ry;
68 self.num = num_steps;
69 self.step = 0;
70 self.cw = cw;
71 if self.num == 0 {
72 self.calc_num_steps();
73 }
74 }
75
76 pub fn set_approximation_scale(&mut self, scale: f64) {
78 self.scale = scale;
79 self.calc_num_steps();
80 }
81
82 fn calc_num_steps(&mut self) {
84 let ra = (self.rx.abs() + self.ry.abs()) / 2.0;
85 let da = (ra / (ra + 0.125 / self.scale)).acos() * 2.0;
86 self.num = uround(2.0 * PI / da);
87 }
88}
89
90impl VertexSource for Ellipse {
91 fn rewind(&mut self, _path_id: u32) {
92 self.step = 0;
93 }
94
95 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
96 if self.step == self.num {
97 self.step += 1;
98 return PATH_CMD_END_POLY | PATH_FLAGS_CLOSE | PATH_FLAGS_CCW;
99 }
100 if self.step > self.num {
101 return PATH_CMD_STOP;
102 }
103 let mut angle = self.step as f64 / self.num as f64 * 2.0 * PI;
104 if self.cw {
105 angle = 2.0 * PI - angle;
106 }
107 *x = self.x + angle.cos() * self.rx;
108 *y = self.y + angle.sin() * self.ry;
109 self.step += 1;
110 if self.step == 1 {
111 PATH_CMD_MOVE_TO
112 } else {
113 PATH_CMD_LINE_TO
114 }
115 }
116}
117
118#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::basics::{is_end_poly, is_stop};
126
127 #[test]
128 fn test_ellipse_basic() {
129 let mut e = Ellipse::new(0.0, 0.0, 10.0, 10.0, 8, false);
130 e.rewind(0);
131 let mut x = 0.0;
132 let mut y = 0.0;
133
134 let cmd = e.vertex(&mut x, &mut y);
136 assert_eq!(cmd, PATH_CMD_MOVE_TO);
137 assert!((x - 10.0).abs() < 1e-6);
138 assert!(y.abs() < 1e-6);
139
140 for _ in 1..8 {
142 let cmd = e.vertex(&mut x, &mut y);
143 assert_eq!(cmd, PATH_CMD_LINE_TO);
144 }
145
146 let cmd = e.vertex(&mut x, &mut y);
148 assert!(is_end_poly(cmd));
149
150 let cmd = e.vertex(&mut x, &mut y);
152 assert!(is_stop(cmd));
153 }
154
155 #[test]
156 fn test_ellipse_vertices_on_circle() {
157 let mut e = Ellipse::new(0.0, 0.0, 10.0, 10.0, 4, false);
158 e.rewind(0);
159 let mut x = 0.0;
160 let mut y = 0.0;
161
162 e.vertex(&mut x, &mut y);
164 assert!((x - 10.0).abs() < 1e-6);
165 assert!(y.abs() < 1e-6);
166
167 e.vertex(&mut x, &mut y);
169 assert!(x.abs() < 1e-6);
170 assert!((y - 10.0).abs() < 1e-6);
171
172 e.vertex(&mut x, &mut y);
174 assert!((x + 10.0).abs() < 1e-6);
175 assert!(y.abs() < 1e-6);
176
177 e.vertex(&mut x, &mut y);
179 assert!(x.abs() < 1e-6);
180 assert!((y + 10.0).abs() < 1e-6);
181 }
182
183 #[test]
184 fn test_ellipse_cw() {
185 let mut e = Ellipse::new(0.0, 0.0, 10.0, 10.0, 4, true);
186 e.rewind(0);
187 let mut x = 0.0;
188 let mut y = 0.0;
189
190 e.vertex(&mut x, &mut y);
192 assert!((x - 10.0).abs() < 1e-6);
193
194 e.vertex(&mut x, &mut y);
196 assert!(x.abs() < 1e-6);
197 assert!((y + 10.0).abs() < 1e-6);
198 }
199
200 #[test]
201 fn test_ellipse_center_offset() {
202 let mut e = Ellipse::new(5.0, 3.0, 10.0, 10.0, 4, false);
203 e.rewind(0);
204 let mut x = 0.0;
205 let mut y = 0.0;
206
207 e.vertex(&mut x, &mut y);
208 assert!((x - 15.0).abs() < 1e-6);
209 assert!((y - 3.0).abs() < 1e-6);
210 }
211
212 #[test]
213 fn test_ellipse_auto_steps() {
214 let e = Ellipse::new(0.0, 0.0, 100.0, 100.0, 0, false);
215 assert!(e.num > 20);
217 }
218
219 #[test]
220 fn test_ellipse_rewind_restarts() {
221 let mut e = Ellipse::new(0.0, 0.0, 10.0, 10.0, 4, false);
222 let mut x = 0.0;
223 let mut y = 0.0;
224
225 e.rewind(0);
227 e.vertex(&mut x, &mut y);
228 e.vertex(&mut x, &mut y);
229
230 e.rewind(0);
232 let cmd = e.vertex(&mut x, &mut y);
233 assert_eq!(cmd, PATH_CMD_MOVE_TO);
234 assert!((x - 10.0).abs() < 1e-6);
235 }
236
237 #[test]
238 fn test_ellipse_different_radii() {
239 let mut e = Ellipse::new(0.0, 0.0, 20.0, 10.0, 4, false);
240 e.rewind(0);
241 let mut x = 0.0;
242 let mut y = 0.0;
243
244 e.vertex(&mut x, &mut y);
246 assert!((x - 20.0).abs() < 1e-6);
247
248 e.vertex(&mut x, &mut y);
250 assert!(x.abs() < 1e-6);
251 assert!((y - 10.0).abs() < 1e-6);
252 }
253}