1use crate::basics::{
8 VertexSource, PATH_CMD_CURVE4, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP, PI,
9};
10use crate::trans_affine::TransAffine;
11
12const BEZIER_ARC_ANGLE_EPSILON: f64 = 0.01;
14
15pub fn arc_to_bezier(
19 cx: f64,
20 cy: f64,
21 rx: f64,
22 ry: f64,
23 start_angle: f64,
24 sweep_angle: f64,
25 curve: &mut [f64],
26) {
27 let x0 = (sweep_angle / 2.0).cos();
28 let y0 = (sweep_angle / 2.0).sin();
29 let tx = (1.0 - x0) * 4.0 / 3.0;
30 let ty = y0 - tx * x0 / y0;
31
32 let px = [x0, x0 + tx, x0 + tx, x0];
33 let py = [-y0, -ty, ty, y0];
34
35 let sn = (start_angle + sweep_angle / 2.0).sin();
36 let cs = (start_angle + sweep_angle / 2.0).cos();
37
38 for i in 0..4 {
39 curve[i * 2] = cx + rx * (px[i] * cs - py[i] * sn);
40 curve[i * 2 + 1] = cy + ry * (px[i] * sn + py[i] * cs);
41 }
42}
43
44pub struct BezierArc {
50 vertex: usize,
51 num_vertices: usize,
52 vertices: [f64; 26],
53 cmd: u32,
54}
55
56impl BezierArc {
57 pub fn new() -> Self {
59 Self {
60 vertex: 26,
61 num_vertices: 0,
62 vertices: [0.0; 26],
63 cmd: PATH_CMD_LINE_TO,
64 }
65 }
66
67 pub fn new_with_params(
69 x: f64,
70 y: f64,
71 rx: f64,
72 ry: f64,
73 start_angle: f64,
74 sweep_angle: f64,
75 ) -> Self {
76 let mut arc = Self::new();
77 arc.init(x, y, rx, ry, start_angle, sweep_angle);
78 arc
79 }
80
81 pub fn init(&mut self, x: f64, y: f64, rx: f64, ry: f64, start_angle: f64, sweep_angle: f64) {
83 let mut start_angle = start_angle % (2.0 * PI);
84 let mut sweep_angle = sweep_angle;
85
86 if sweep_angle >= 2.0 * PI {
87 sweep_angle = 2.0 * PI;
88 }
89 if sweep_angle <= -2.0 * PI {
90 sweep_angle = -2.0 * PI;
91 }
92
93 if sweep_angle.abs() < 1e-10 {
94 self.num_vertices = 4;
95 self.cmd = PATH_CMD_LINE_TO;
96 self.vertices[0] = x + rx * start_angle.cos();
97 self.vertices[1] = y + ry * start_angle.sin();
98 self.vertices[2] = x + rx * (start_angle + sweep_angle).cos();
99 self.vertices[3] = y + ry * (start_angle + sweep_angle).sin();
100 return;
101 }
102
103 let mut total_sweep = 0.0;
104 let mut local_sweep;
105 self.num_vertices = 2;
106 self.cmd = PATH_CMD_CURVE4;
107 let mut done = false;
108
109 loop {
110 if sweep_angle < 0.0 {
111 let prev_sweep = total_sweep;
112 local_sweep = -PI * 0.5;
113 total_sweep -= PI * 0.5;
114 if total_sweep <= sweep_angle + BEZIER_ARC_ANGLE_EPSILON {
115 local_sweep = sweep_angle - prev_sweep;
116 done = true;
117 }
118 } else {
119 let prev_sweep = total_sweep;
120 local_sweep = PI * 0.5;
121 total_sweep += PI * 0.5;
122 if total_sweep >= sweep_angle - BEZIER_ARC_ANGLE_EPSILON {
123 local_sweep = sweep_angle - prev_sweep;
124 done = true;
125 }
126 }
127
128 arc_to_bezier(
129 x,
130 y,
131 rx,
132 ry,
133 start_angle,
134 local_sweep,
135 &mut self.vertices[self.num_vertices - 2..],
136 );
137
138 self.num_vertices += 6;
139 start_angle += local_sweep;
140
141 if done || self.num_vertices >= 26 {
142 break;
143 }
144 }
145 }
146
147 pub fn num_vertices(&self) -> usize {
149 self.num_vertices
150 }
151
152 pub fn vertices(&self) -> &[f64; 26] {
154 &self.vertices
155 }
156
157 pub fn vertices_mut(&mut self) -> &mut [f64; 26] {
159 &mut self.vertices
160 }
161}
162
163impl Default for BezierArc {
164 fn default() -> Self {
165 Self::new()
166 }
167}
168
169impl VertexSource for BezierArc {
170 fn rewind(&mut self, _path_id: u32) {
171 self.vertex = 0;
172 }
173
174 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
175 if self.vertex >= self.num_vertices {
176 return PATH_CMD_STOP;
177 }
178 *x = self.vertices[self.vertex];
179 *y = self.vertices[self.vertex + 1];
180 self.vertex += 2;
181 if self.vertex == 2 {
182 PATH_CMD_MOVE_TO
183 } else {
184 self.cmd
185 }
186 }
187}
188
189pub struct BezierArcSvg {
196 arc: BezierArc,
197 radii_ok: bool,
198}
199
200impl BezierArcSvg {
201 pub fn new() -> Self {
203 Self {
204 arc: BezierArc::new(),
205 radii_ok: false,
206 }
207 }
208
209 #[allow(clippy::too_many_arguments)]
211 pub fn new_with_params(
212 x1: f64,
213 y1: f64,
214 rx: f64,
215 ry: f64,
216 angle: f64,
217 large_arc_flag: bool,
218 sweep_flag: bool,
219 x2: f64,
220 y2: f64,
221 ) -> Self {
222 let mut svg = Self::new();
223 svg.init(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2);
224 svg
225 }
226
227 #[allow(clippy::too_many_arguments)]
229 pub fn init(
230 &mut self,
231 x0: f64,
232 y0: f64,
233 rx: f64,
234 ry: f64,
235 angle: f64,
236 large_arc_flag: bool,
237 sweep_flag: bool,
238 x2: f64,
239 y2: f64,
240 ) {
241 self.radii_ok = true;
242
243 let mut rx = rx.abs();
244 let mut ry = ry.abs();
245
246 let dx2 = (x0 - x2) / 2.0;
248 let dy2 = (y0 - y2) / 2.0;
249
250 let cos_a = angle.cos();
251 let sin_a = angle.sin();
252
253 let x1 = cos_a * dx2 + sin_a * dy2;
255 let y1 = -sin_a * dx2 + cos_a * dy2;
256
257 let mut prx = rx * rx;
259 let mut pry = ry * ry;
260 let px1 = x1 * x1;
261 let py1 = y1 * y1;
262
263 let radii_check = px1 / prx + py1 / pry;
264 if radii_check > 1.0 {
265 rx *= radii_check.sqrt();
266 ry *= radii_check.sqrt();
267 prx = rx * rx;
268 pry = ry * ry;
269 if radii_check > 10.0 {
270 self.radii_ok = false;
271 }
272 }
273
274 let sign = if large_arc_flag == sweep_flag {
276 -1.0
277 } else {
278 1.0
279 };
280 let sq = (prx * pry - prx * py1 - pry * px1) / (prx * py1 + pry * px1);
281 let coef = sign * (if sq < 0.0 { 0.0 } else { sq }).sqrt();
282 let cx1 = coef * ((rx * y1) / ry);
283 let cy1 = coef * -((ry * x1) / rx);
284
285 let sx2 = (x0 + x2) / 2.0;
287 let sy2 = (y0 + y2) / 2.0;
288 let cx = sx2 + (cos_a * cx1 - sin_a * cy1);
289 let cy = sy2 + (sin_a * cx1 + cos_a * cy1);
290
291 let ux = (x1 - cx1) / rx;
293 let uy = (y1 - cy1) / ry;
294 let vx = (-x1 - cx1) / rx;
295 let vy = (-y1 - cy1) / ry;
296
297 let n = (ux * ux + uy * uy).sqrt();
299 let p = ux;
300 let sign = if uy < 0.0 { -1.0 } else { 1.0 };
301 let v = (p / n).clamp(-1.0, 1.0);
302 let start_angle = sign * v.acos();
303
304 let n = ((ux * ux + uy * uy) * (vx * vx + vy * vy)).sqrt();
306 let p = ux * vx + uy * vy;
307 let sign = if ux * vy - uy * vx < 0.0 { -1.0 } else { 1.0 };
308 let v = (p / n).clamp(-1.0, 1.0);
309 let mut sweep_angle = sign * v.acos();
310
311 if !sweep_flag && sweep_angle > 0.0 {
312 sweep_angle -= PI * 2.0;
313 } else if sweep_flag && sweep_angle < 0.0 {
314 sweep_angle += PI * 2.0;
315 }
316
317 self.arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle);
319
320 let mut mtx = TransAffine::new_rotation(angle);
321 mtx.multiply(&TransAffine::new_translation(cx, cy));
322
323 let nv = self.arc.num_vertices();
324 if nv > 4 {
325 let verts = self.arc.vertices_mut();
326 for i in (2..nv - 2).step_by(2) {
327 let mut tx = verts[i];
328 let mut ty = verts[i + 1];
329 mtx.transform(&mut tx, &mut ty);
330 verts[i] = tx;
331 verts[i + 1] = ty;
332 }
333 }
334
335 let verts = self.arc.vertices_mut();
337 verts[0] = x0;
338 verts[1] = y0;
339 if nv > 2 {
340 verts[nv - 2] = x2;
341 verts[nv - 1] = y2;
342 }
343 }
344
345 pub fn radii_ok(&self) -> bool {
347 self.radii_ok
348 }
349
350 pub fn num_vertices(&self) -> usize {
352 self.arc.num_vertices()
353 }
354
355 pub fn vertices(&self) -> &[f64; 26] {
357 self.arc.vertices()
358 }
359}
360
361impl Default for BezierArcSvg {
362 fn default() -> Self {
363 Self::new()
364 }
365}
366
367impl VertexSource for BezierArcSvg {
368 fn rewind(&mut self, _path_id: u32) {
369 self.arc.rewind(0);
370 }
371
372 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
373 self.arc.vertex(x, y)
374 }
375}
376
377#[cfg(test)]
382mod tests {
383 use super::*;
384 use crate::basics::is_stop;
385
386 #[test]
387 fn test_bezier_arc_quarter() {
388 let mut arc = BezierArc::new_with_params(0.0, 0.0, 10.0, 10.0, 0.0, PI / 2.0);
389 arc.rewind(0);
390 let mut x = 0.0;
391 let mut y = 0.0;
392
393 let cmd = arc.vertex(&mut x, &mut y);
395 assert_eq!(cmd, PATH_CMD_MOVE_TO);
396 assert!((x - 10.0).abs() < 1e-6);
397 assert!(y.abs() < 1e-6);
398
399 for _ in 0..3 {
401 let cmd = arc.vertex(&mut x, &mut y);
402 assert_eq!(cmd, PATH_CMD_CURVE4);
403 }
404
405 let cmd = arc.vertex(&mut x, &mut y);
407 assert!(is_stop(cmd));
408 }
409
410 #[test]
411 fn test_bezier_arc_full_circle() {
412 let arc = BezierArc::new_with_params(0.0, 0.0, 10.0, 10.0, 0.0, 2.0 * PI);
413 assert_eq!(arc.num_vertices(), 26);
416 }
417
418 #[test]
419 fn test_bezier_arc_half_circle() {
420 let arc = BezierArc::new_with_params(0.0, 0.0, 10.0, 10.0, 0.0, PI);
421 assert_eq!(arc.num_vertices(), 14);
423 }
424
425 #[test]
426 fn test_bezier_arc_tiny_sweep() {
427 let arc = BezierArc::new_with_params(0.0, 0.0, 10.0, 10.0, 0.0, 1e-15);
428 assert_eq!(arc.num_vertices(), 4);
430 assert_eq!(arc.cmd, PATH_CMD_LINE_TO);
431 }
432
433 #[test]
434 fn test_bezier_arc_negative_sweep() {
435 let arc = BezierArc::new_with_params(0.0, 0.0, 10.0, 10.0, 0.0, -PI / 2.0);
436 assert_eq!(arc.num_vertices(), 8);
438 }
439
440 #[test]
441 fn test_bezier_arc_svg_basic() {
442 let mut svg = BezierArcSvg::new_with_params(
443 0.0, 10.0, 10.0, 10.0, 0.0, false, true, 10.0, 0.0, );
449 assert!(svg.radii_ok());
450
451 svg.rewind(0);
452 let mut x = 0.0;
453 let mut y = 0.0;
454
455 let cmd = svg.vertex(&mut x, &mut y);
457 assert_eq!(cmd, PATH_CMD_MOVE_TO);
458 assert!((x - 0.0).abs() < 1e-6);
459 assert!((y - 10.0).abs() < 1e-6);
460
461 let mut count = 1;
463 while !is_stop(svg.vertex(&mut x, &mut y)) {
464 count += 1;
465 }
466 assert!(count >= 4);
467
468 assert!((x - 10.0).abs() < 1e-6);
470 assert!((y - 0.0).abs() < 1e-6);
471 }
472
473 #[test]
474 fn test_arc_to_bezier_endpoints() {
475 let mut curve = [0.0; 8];
476 arc_to_bezier(0.0, 0.0, 10.0, 10.0, 0.0, PI / 2.0, &mut curve);
477
478 let start_x = curve[0];
480 let start_y = curve[1];
481 assert!((start_x - 10.0).abs() < 1e-6);
482 assert!(start_y.abs() < 1e-6);
483
484 let end_x = curve[6];
486 let end_y = curve[7];
487 assert!(end_x.abs() < 1e-6);
488 assert!((end_y - 10.0).abs() < 1e-6);
489 }
490
491 #[test]
492 fn test_bezier_arc_default() {
493 let arc = BezierArc::new();
494 assert_eq!(arc.num_vertices(), 0);
495 }
496
497 #[test]
498 fn test_bezier_arc_svg_small_radii() {
499 let svg = BezierArcSvg::new_with_params(
501 0.0, 0.0, 1.0, 1.0, 0.0, false, true, 100.0, 100.0, );
507 assert!(!svg.radii_ok());
509 }
510}