iced_graphics/geometry/path/
builder.rs1use crate::geometry::path::{Arc, Path, arc};
2
3use crate::core::border;
4use crate::core::{Point, Radians, Size};
5
6use lyon_path::builder::{self, SvgPathBuilder};
7use lyon_path::geom;
8use lyon_path::math;
9
10pub struct Builder {
14 raw: builder::WithSvg<lyon_path::path::BuilderImpl>,
15}
16
17impl Builder {
18 pub fn new() -> Builder {
20 Builder {
21 raw: lyon_path::Path::builder().with_svg(),
22 }
23 }
24
25 #[inline]
27 pub fn move_to(&mut self, point: Point) {
28 let _ = self.raw.move_to(math::Point::new(point.x, point.y));
29 }
30
31 #[inline]
34 pub fn line_to(&mut self, point: Point) {
35 let _ = self.raw.line_to(math::Point::new(point.x, point.y));
36 }
37
38 #[inline]
41 pub fn arc(&mut self, arc: Arc) {
42 self.ellipse(arc.into());
43 }
44
45 pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) {
59 let start = self.raw.current_position();
60 let mid = math::Point::new(a.x, a.y);
61 let end = math::Point::new(b.x, b.y);
62
63 if start == mid || mid == end || radius == 0.0 {
64 let _ = self.raw.line_to(mid);
65 return;
66 }
67
68 let double_area = start.x * (mid.y - end.y)
69 + mid.x * (end.y - start.y)
70 + end.x * (start.y - mid.y);
71
72 if double_area == 0.0 {
73 let _ = self.raw.line_to(mid);
74 return;
75 }
76
77 let to_start = (start - mid).normalize();
78 let to_end = (end - mid).normalize();
79
80 let inner_angle = to_start.dot(to_end).acos();
81
82 let origin_angle = inner_angle / 2.0;
83
84 let origin_adjacent = radius / origin_angle.tan();
85
86 let arc_start = mid + to_start * origin_adjacent;
87 let arc_end = mid + to_end * origin_adjacent;
88
89 let sweep = to_start.cross(to_end) < 0.0;
90
91 let _ = self.raw.line_to(arc_start);
92
93 self.raw.arc_to(
94 math::Vector::new(radius, radius),
95 math::Angle::radians(0.0),
96 lyon_path::ArcFlags {
97 large_arc: false,
98 sweep,
99 },
100 arc_end,
101 );
102 }
103
104 pub fn ellipse(&mut self, arc: arc::Elliptical) {
106 let arc = geom::Arc {
107 center: math::Point::new(arc.center.x, arc.center.y),
108 radii: math::Vector::new(arc.radii.x, arc.radii.y),
109 x_rotation: math::Angle::radians(arc.rotation.0),
110 start_angle: math::Angle::radians(arc.start_angle.0),
111 sweep_angle: math::Angle::radians(
112 (arc.end_angle - arc.start_angle).0,
113 ),
114 };
115
116 let _ = self.raw.move_to(arc.sample(0.0));
117
118 arc.cast::<f64>().for_each_quadratic_bezier(&mut |curve| {
119 let curve = curve.cast::<f32>();
120 let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);
121 });
122 }
123
124 #[inline]
127 pub fn bezier_curve_to(
128 &mut self,
129 control_a: Point,
130 control_b: Point,
131 to: Point,
132 ) {
133 let _ = self.raw.cubic_bezier_to(
134 math::Point::new(control_a.x, control_a.y),
135 math::Point::new(control_b.x, control_b.y),
136 math::Point::new(to.x, to.y),
137 );
138 }
139
140 #[inline]
143 pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
144 let _ = self.raw.quadratic_bezier_to(
145 math::Point::new(control.x, control.y),
146 math::Point::new(to.x, to.y),
147 );
148 }
149
150 #[inline]
153 pub fn rectangle(&mut self, top_left: Point, size: Size) {
154 self.move_to(top_left);
155 self.line_to(Point::new(top_left.x + size.width, top_left.y));
156 self.line_to(Point::new(
157 top_left.x + size.width,
158 top_left.y + size.height,
159 ));
160 self.line_to(Point::new(top_left.x, top_left.y + size.height));
161 self.close();
162 }
163
164 #[inline]
167 pub fn rounded_rectangle(
168 &mut self,
169 top_left: Point,
170 size: Size,
171 radius: border::Radius,
172 ) {
173 let min_size = (size.height / 2.0).min(size.width / 2.0);
174 let [
175 top_left_corner,
176 top_right_corner,
177 bottom_right_corner,
178 bottom_left_corner,
179 ] = radius.into();
180
181 self.move_to(Point::new(
182 top_left.x + min_size.min(top_left_corner),
183 top_left.y,
184 ));
185 self.line_to(Point::new(
186 top_left.x + size.width - min_size.min(top_right_corner),
187 top_left.y,
188 ));
189 self.arc_to(
190 Point::new(top_left.x + size.width, top_left.y),
191 Point::new(
192 top_left.x + size.width,
193 top_left.y + min_size.min(top_right_corner),
194 ),
195 min_size.min(top_right_corner),
196 );
197 self.line_to(Point::new(
198 top_left.x + size.width,
199 top_left.y + size.height - min_size.min(bottom_right_corner),
200 ));
201 self.arc_to(
202 Point::new(top_left.x + size.width, top_left.y + size.height),
203 Point::new(
204 top_left.x + size.width - min_size.min(bottom_right_corner),
205 top_left.y + size.height,
206 ),
207 min_size.min(bottom_right_corner),
208 );
209 self.line_to(Point::new(
210 top_left.x + min_size.min(bottom_left_corner),
211 top_left.y + size.height,
212 ));
213 self.arc_to(
214 Point::new(top_left.x, top_left.y + size.height),
215 Point::new(
216 top_left.x,
217 top_left.y + size.height - min_size.min(bottom_left_corner),
218 ),
219 min_size.min(bottom_left_corner),
220 );
221 self.line_to(Point::new(
222 top_left.x,
223 top_left.y + min_size.min(top_left_corner),
224 ));
225 self.arc_to(
226 Point::new(top_left.x, top_left.y),
227 Point::new(top_left.x + min_size.min(top_left_corner), top_left.y),
228 min_size.min(top_left_corner),
229 );
230 self.close();
231 }
232
233 #[inline]
236 pub fn circle(&mut self, center: Point, radius: f32) {
237 self.arc(Arc {
238 center,
239 radius,
240 start_angle: Radians(0.0),
241 end_angle: Radians(2.0 * std::f32::consts::PI),
242 });
243 }
244
245 #[inline]
248 pub fn close(&mut self) {
249 self.raw.close();
250 }
251
252 #[inline]
254 pub fn build(self) -> Path {
255 Path {
256 raw: self.raw.build(),
257 }
258 }
259}
260
261impl Default for Builder {
262 fn default() -> Self {
263 Self::new()
264 }
265}