1use crate::{Affine, Ellipse, PathEl, Point, Rect, Shape, Vec2};
7use core::{
8 f64::consts::{FRAC_PI_2, PI},
9 iter,
10 ops::Mul,
11};
12
13#[cfg(not(feature = "std"))]
14use crate::common::FloatFuncs;
15
16#[derive(Clone, Copy, Debug, PartialEq)]
18#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct Arc {
21 pub center: Point,
23 pub radii: Vec2,
26 pub start_angle: f64,
28 pub sweep_angle: f64,
30 pub x_rotation: f64,
32}
33
34impl Arc {
35 #[inline(always)]
37 pub fn new(
38 center: impl Into<Point>,
39 radii: impl Into<Vec2>,
40 start_angle: f64,
41 sweep_angle: f64,
42 x_rotation: f64,
43 ) -> Self {
44 Self {
45 center: center.into(),
46 radii: radii.into(),
47 start_angle,
48 sweep_angle,
49 x_rotation,
50 }
51 }
52
53 #[must_use]
58 #[inline]
59 pub fn reversed(&self) -> Arc {
60 Self {
61 center: self.center,
62 radii: self.radii,
63 start_angle: self.start_angle + self.sweep_angle,
64 sweep_angle: -self.sweep_angle,
65 x_rotation: self.x_rotation,
66 }
67 }
68
69 pub fn append_iter(&self, tolerance: f64) -> ArcAppendIter {
73 let sign = self.sweep_angle.signum();
74 let scaled_err = self.radii.x.max(self.radii.y) / tolerance;
75 let n_err = (1.1163 * scaled_err).powf(1.0 / 6.0).max(3.999_999);
78 let n = (n_err * self.sweep_angle.abs() * (1.0 / (2.0 * PI))).ceil();
79 let angle_step = self.sweep_angle / n;
80 let n = n as usize;
81 let arm_len = (4.0 / 3.0) * (0.25 * angle_step).abs().tan() * sign;
82 let angle0 = self.start_angle;
83 let p0 = sample_ellipse(self.radii, self.x_rotation, angle0);
84
85 ArcAppendIter {
86 idx: 0,
87
88 center: self.center,
89 radii: self.radii,
90 x_rotation: self.x_rotation,
91 n,
92 arm_len,
93 angle_step,
94
95 p0,
96 angle0,
97 }
98 }
99
100 pub fn to_cubic_beziers<P>(self, tolerance: f64, mut p: P)
104 where
105 P: FnMut(Point, Point, Point),
106 {
107 let mut path = self.append_iter(tolerance);
108 while let Some(PathEl::CurveTo(p1, p2, p3)) = path.next() {
109 p(p1, p2, p3);
110 }
111 }
112}
113
114#[doc(hidden)]
115pub struct ArcAppendIter {
116 idx: usize,
117
118 center: Point,
119 radii: Vec2,
120 x_rotation: f64,
121 n: usize,
122 arm_len: f64,
123 angle_step: f64,
124
125 p0: Vec2,
126 angle0: f64,
127}
128
129impl Iterator for ArcAppendIter {
130 type Item = PathEl;
131
132 fn next(&mut self) -> Option<Self::Item> {
133 if self.idx >= self.n {
134 return None;
135 }
136
137 let angle1 = self.angle0 + self.angle_step;
138 let p0 = self.p0;
139 let p1 = p0
140 + self.arm_len * sample_ellipse(self.radii, self.x_rotation, self.angle0 + FRAC_PI_2);
141 let p3 = sample_ellipse(self.radii, self.x_rotation, angle1);
142 let p2 =
143 p3 - self.arm_len * sample_ellipse(self.radii, self.x_rotation, angle1 + FRAC_PI_2);
144
145 self.angle0 = angle1;
146 self.p0 = p3;
147 self.idx += 1;
148
149 Some(PathEl::CurveTo(
150 self.center + p1,
151 self.center + p2,
152 self.center + p3,
153 ))
154 }
155}
156
157fn sample_ellipse(radii: Vec2, x_rotation: f64, angle: f64) -> Vec2 {
160 let (angle_sin, angle_cos) = angle.sin_cos();
161 let u = radii.x * angle_cos;
162 let v = radii.y * angle_sin;
163 rotate_pt(Vec2::new(u, v), x_rotation)
164}
165
166fn rotate_pt(pt: Vec2, angle: f64) -> Vec2 {
168 let (angle_sin, angle_cos) = angle.sin_cos();
169 Vec2::new(
170 pt.x * angle_cos - pt.y * angle_sin,
171 pt.x * angle_sin + pt.y * angle_cos,
172 )
173}
174
175impl Shape for Arc {
176 type PathElementsIter<'iter> = iter::Chain<iter::Once<PathEl>, ArcAppendIter>;
177
178 fn path_elements(&self, tolerance: f64) -> Self::PathElementsIter<'_> {
179 let p0 = sample_ellipse(self.radii, self.x_rotation, self.start_angle);
180 iter::once(PathEl::MoveTo(self.center + p0)).chain(self.append_iter(tolerance))
181 }
182
183 #[inline]
185 fn area(&self) -> f64 {
186 let Vec2 { x, y } = self.radii;
187 PI * x * y
188 }
189
190 #[inline]
194 fn perimeter(&self, accuracy: f64) -> f64 {
195 self.path_segments(0.1).perimeter(accuracy)
196 }
197
198 #[inline]
200 fn winding(&self, pt: Point) -> i32 {
201 self.path_segments(0.1).winding(pt)
202 }
203
204 #[inline]
205 fn bounding_box(&self) -> Rect {
206 self.path_segments(0.1).bounding_box()
207 }
208}
209
210impl Mul<Arc> for Affine {
211 type Output = Arc;
212
213 fn mul(self, arc: Arc) -> Self::Output {
214 let ellipse = self * Ellipse::new(arc.center, arc.radii, arc.x_rotation);
215 let center = ellipse.center();
216 let (radii, rotation) = ellipse.radii_and_rotation();
217 Arc {
218 center,
219 radii,
220 x_rotation: rotation,
221 start_angle: arc.start_angle,
222 sweep_angle: arc.sweep_angle,
223 }
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 #[test]
231 fn reversed_arc() {
232 let a = Arc::new((0., 0.), (1., 0.), 0., PI, 0.);
233 let f = a.reversed();
234
235 assert_eq!(a.center, f.center);
237 assert_eq!(a.radii, f.radii);
238 assert_eq!(a.x_rotation, f.x_rotation);
239
240 assert_eq!(a.sweep_angle, -f.sweep_angle);
242
243 assert_eq!(a, f.reversed());
245 }
246}