iced_graphics/geometry/path/
builder.rs
1use crate::geometry::path::{arc, Arc, Path};
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 [top_left_corner, top_right_corner, bottom_right_corner, bottom_left_corner] =
175 radius.into();
176
177 self.move_to(Point::new(
178 top_left.x + min_size.min(top_left_corner),
179 top_left.y,
180 ));
181 self.line_to(Point::new(
182 top_left.x + size.width - min_size.min(top_right_corner),
183 top_left.y,
184 ));
185 self.arc_to(
186 Point::new(top_left.x + size.width, top_left.y),
187 Point::new(
188 top_left.x + size.width,
189 top_left.y + min_size.min(top_right_corner),
190 ),
191 min_size.min(top_right_corner),
192 );
193 self.line_to(Point::new(
194 top_left.x + size.width,
195 top_left.y + size.height - min_size.min(bottom_right_corner),
196 ));
197 self.arc_to(
198 Point::new(top_left.x + size.width, top_left.y + size.height),
199 Point::new(
200 top_left.x + size.width - min_size.min(bottom_right_corner),
201 top_left.y + size.height,
202 ),
203 min_size.min(bottom_right_corner),
204 );
205 self.line_to(Point::new(
206 top_left.x + min_size.min(bottom_left_corner),
207 top_left.y + size.height,
208 ));
209 self.arc_to(
210 Point::new(top_left.x, top_left.y + size.height),
211 Point::new(
212 top_left.x,
213 top_left.y + size.height - min_size.min(bottom_left_corner),
214 ),
215 min_size.min(bottom_left_corner),
216 );
217 self.line_to(Point::new(
218 top_left.x,
219 top_left.y + min_size.min(top_left_corner),
220 ));
221 self.arc_to(
222 Point::new(top_left.x, top_left.y),
223 Point::new(top_left.x + min_size.min(top_left_corner), top_left.y),
224 min_size.min(top_left_corner),
225 );
226 self.close();
227 }
228
229 #[inline]
232 pub fn circle(&mut self, center: Point, radius: f32) {
233 self.arc(Arc {
234 center,
235 radius,
236 start_angle: Radians(0.0),
237 end_angle: Radians(2.0 * std::f32::consts::PI),
238 });
239 }
240
241 #[inline]
244 pub fn close(&mut self) {
245 self.raw.close();
246 }
247
248 #[inline]
250 pub fn build(self) -> Path {
251 Path {
252 raw: self.raw.build(),
253 }
254 }
255}
256
257impl Default for Builder {
258 fn default() -> Self {
259 Self::new()
260 }
261}