1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use libm;
use crate::{PathEl, Point, Vec2};
use core::f64::consts::{FRAC_PI_2, PI};
#[derive(Clone, Copy, Debug)]
pub struct Arc {
pub center: Point,
pub radii: Vec2,
pub start_angle: f64,
pub sweep_angle: f64,
pub x_rotation: f64,
}
fn signum(x: f64) -> f64 {
if x >= 0 as f64 { 1 as f64 }
else { -1 as f64 }
}
impl Arc {
pub fn append_iter(&self, tolerance: f64) -> ArcAppendIter {
let sign = signum(self.sweep_angle);
let scaled_err = self.radii.x.max(self.radii.y) / tolerance;
let n_err = libm::pow(1.1163 * scaled_err, 1.0 / 6.0).max(3.999_999);
let n = libm::ceil(n_err * libm::fabs(self.sweep_angle) * (1.0 / (2.0 * PI)));
let angle_step = self.sweep_angle / n;
let n = n as usize;
let arm_len = (4.0 / 3.0) * libm::tan(libm::fabs(0.25 * angle_step)) * sign;
let angle0 = self.start_angle;
let p0 = sample_ellipse(self.radii, self.x_rotation, angle0);
ArcAppendIter {
idx: 0,
center: self.center,
radii: self.radii,
x_rotation: self.x_rotation,
n,
arm_len,
angle_step,
p0,
angle0,
}
}
pub fn to_cubic_beziers<P>(self, tolerance: f64, mut p: P)
where
P: FnMut(Point, Point, Point),
{
let mut path = self.append_iter(tolerance);
while let Some(PathEl::CurveTo(p1, p2, p3)) = path.next() {
p(p1, p2, p3);
}
}
}
#[doc(hidden)]
pub struct ArcAppendIter {
idx: usize,
center: Point,
radii: Vec2,
x_rotation: f64,
n: usize,
arm_len: f64,
angle_step: f64,
p0: Vec2,
angle0: f64,
}
impl Iterator for ArcAppendIter {
type Item = PathEl;
fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.n {
return None;
}
let angle1 = self.angle0 + self.angle_step;
let p0 = self.p0;
let p1 = p0
+ self.arm_len * sample_ellipse(self.radii, self.x_rotation, self.angle0 + FRAC_PI_2);
let p3 = sample_ellipse(self.radii, self.x_rotation, angle1);
let p2 =
p3 - self.arm_len * sample_ellipse(self.radii, self.x_rotation, angle1 + FRAC_PI_2);
self.angle0 = angle1;
self.p0 = p3;
self.idx += 1;
Some(PathEl::CurveTo(
self.center + p1,
self.center + p2,
self.center + p3,
))
}
}
fn sample_ellipse(radii: Vec2, x_rotation: f64, angle: f64) -> Vec2 {
let u = radii.x * libm::cos(angle);
let v = radii.y * libm::sin(angle);
rotate_pt(Vec2::new(u, v), x_rotation)
}
fn rotate_pt(pt: Vec2, angle: f64) -> Vec2 {
Vec2::new(
pt.x * libm::cos(angle) - pt.y * libm::sin(angle),
pt.x * libm::sin(angle) + pt.y * libm::cos(angle),
)
}