iced_graphics/geometry/path/
builder.rs
1use 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
10#[allow(missing_debug_implementations)]
14pub struct Builder {
15 raw: builder::WithSvg<lyon_path::path::BuilderImpl>,
16}
17
18impl Builder {
19 pub fn new() -> Builder {
21 Builder {
22 raw: lyon_path::Path::builder().with_svg(),
23 }
24 }
25
26 #[inline]
28 pub fn move_to(&mut self, point: Point) {
29 let _ = self.raw.move_to(math::Point::new(point.x, point.y));
30 }
31
32 #[inline]
35 pub fn line_to(&mut self, point: Point) {
36 let _ = self.raw.line_to(math::Point::new(point.x, point.y));
37 }
38
39 #[inline]
42 pub fn arc(&mut self, arc: Arc) {
43 self.ellipse(arc.into());
44 }
45
46 pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) {
60 let start = self.raw.current_position();
61 let mid = math::Point::new(a.x, a.y);
62 let end = math::Point::new(b.x, b.y);
63
64 if start == mid || mid == end || radius == 0.0 {
65 let _ = self.raw.line_to(mid);
66 return;
67 }
68
69 let double_area = start.x * (mid.y - end.y)
70 + mid.x * (end.y - start.y)
71 + end.x * (start.y - mid.y);
72
73 if double_area == 0.0 {
74 let _ = self.raw.line_to(mid);
75 return;
76 }
77
78 let to_start = (start - mid).normalize();
79 let to_end = (end - mid).normalize();
80
81 let inner_angle = to_start.dot(to_end).acos();
82
83 let origin_angle = inner_angle / 2.0;
84
85 let origin_adjacent = radius / origin_angle.tan();
86
87 let arc_start = mid + to_start * origin_adjacent;
88 let arc_end = mid + to_end * origin_adjacent;
89
90 let sweep = to_start.cross(to_end) < 0.0;
91
92 let _ = self.raw.line_to(arc_start);
93
94 self.raw.arc_to(
95 math::Vector::new(radius, radius),
96 math::Angle::radians(0.0),
97 lyon_path::ArcFlags {
98 large_arc: false,
99 sweep,
100 },
101 arc_end,
102 );
103 }
104
105 pub fn ellipse(&mut self, arc: arc::Elliptical) {
107 let arc = geom::Arc {
108 center: math::Point::new(arc.center.x, arc.center.y),
109 radii: math::Vector::new(arc.radii.x, arc.radii.y),
110 x_rotation: math::Angle::radians(arc.rotation.0),
111 start_angle: math::Angle::radians(arc.start_angle.0),
112 sweep_angle: math::Angle::radians(
113 (arc.end_angle - arc.start_angle).0,
114 ),
115 };
116
117 let _ = self.raw.move_to(arc.sample(0.0));
118
119 arc.for_each_quadratic_bezier(&mut |curve| {
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}