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.for_each_quadratic_bezier(&mut |curve| {
119 let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);
120 });
121 }
122
123 #[inline]
126 pub fn bezier_curve_to(
127 &mut self,
128 control_a: Point,
129 control_b: Point,
130 to: Point,
131 ) {
132 let _ = self.raw.cubic_bezier_to(
133 math::Point::new(control_a.x, control_a.y),
134 math::Point::new(control_b.x, control_b.y),
135 math::Point::new(to.x, to.y),
136 );
137 }
138
139 #[inline]
142 pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
143 let _ = self.raw.quadratic_bezier_to(
144 math::Point::new(control.x, control.y),
145 math::Point::new(to.x, to.y),
146 );
147 }
148
149 #[inline]
152 pub fn rectangle(&mut self, top_left: Point, size: Size) {
153 self.move_to(top_left);
154 self.line_to(Point::new(top_left.x + size.width, top_left.y));
155 self.line_to(Point::new(
156 top_left.x + size.width,
157 top_left.y + size.height,
158 ));
159 self.line_to(Point::new(top_left.x, top_left.y + size.height));
160 self.close();
161 }
162
163 #[inline]
166 pub fn rounded_rectangle(
167 &mut self,
168 top_left: Point,
169 size: Size,
170 radius: border::Radius,
171 ) {
172 let min_size = (size.height / 2.0).min(size.width / 2.0);
173 let [
174 top_left_corner,
175 top_right_corner,
176 bottom_right_corner,
177 bottom_left_corner,
178 ] = radius.into();
179
180 self.move_to(Point::new(
181 top_left.x + min_size.min(top_left_corner),
182 top_left.y,
183 ));
184 self.line_to(Point::new(
185 top_left.x + size.width - min_size.min(top_right_corner),
186 top_left.y,
187 ));
188 self.arc_to(
189 Point::new(top_left.x + size.width, top_left.y),
190 Point::new(
191 top_left.x + size.width,
192 top_left.y + min_size.min(top_right_corner),
193 ),
194 min_size.min(top_right_corner),
195 );
196 self.line_to(Point::new(
197 top_left.x + size.width,
198 top_left.y + size.height - min_size.min(bottom_right_corner),
199 ));
200 self.arc_to(
201 Point::new(top_left.x + size.width, top_left.y + size.height),
202 Point::new(
203 top_left.x + size.width - min_size.min(bottom_right_corner),
204 top_left.y + size.height,
205 ),
206 min_size.min(bottom_right_corner),
207 );
208 self.line_to(Point::new(
209 top_left.x + min_size.min(bottom_left_corner),
210 top_left.y + size.height,
211 ));
212 self.arc_to(
213 Point::new(top_left.x, top_left.y + size.height),
214 Point::new(
215 top_left.x,
216 top_left.y + size.height - min_size.min(bottom_left_corner),
217 ),
218 min_size.min(bottom_left_corner),
219 );
220 self.line_to(Point::new(
221 top_left.x,
222 top_left.y + min_size.min(top_left_corner),
223 ));
224 self.arc_to(
225 Point::new(top_left.x, top_left.y),
226 Point::new(top_left.x + min_size.min(top_left_corner), top_left.y),
227 min_size.min(top_left_corner),
228 );
229 self.close();
230 }
231
232 #[inline]
235 pub fn circle(&mut self, center: Point, radius: f32) {
236 self.arc(Arc {
237 center,
238 radius,
239 start_angle: Radians(0.0),
240 end_angle: Radians(2.0 * std::f32::consts::PI),
241 });
242 }
243
244 #[inline]
247 pub fn close(&mut self) {
248 self.raw.close();
249 }
250
251 #[inline]
253 pub fn build(self) -> Path {
254 Path {
255 raw: self.raw.build(),
256 }
257 }
258}
259
260impl Default for Builder {
261 fn default() -> Self {
262 Self::new()
263 }
264}