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 =
69 start.x * (mid.y - end.y) + mid.x * (end.y - start.y) + end.x * (start.y - mid.y);
70
71 if double_area == 0.0 {
72 let _ = self.raw.line_to(mid);
73 return;
74 }
75
76 let to_start = (start - mid).normalize();
77 let to_end = (end - mid).normalize();
78
79 let inner_angle = to_start.dot(to_end).acos();
80
81 let origin_angle = inner_angle / 2.0;
82
83 let origin_adjacent = radius / origin_angle.tan();
84
85 let arc_start = mid + to_start * origin_adjacent;
86 let arc_end = mid + to_end * origin_adjacent;
87
88 let sweep = to_start.cross(to_end) < 0.0;
89
90 let _ = self.raw.line_to(arc_start);
91
92 self.raw.arc_to(
93 math::Vector::new(radius, radius),
94 math::Angle::radians(0.0),
95 lyon_path::ArcFlags {
96 large_arc: false,
97 sweep,
98 },
99 arc_end,
100 );
101 }
102
103 pub fn ellipse(&mut self, arc: arc::Elliptical) {
105 let arc = geom::Arc {
106 center: math::Point::new(arc.center.x, arc.center.y),
107 radii: math::Vector::new(arc.radii.x, arc.radii.y),
108 x_rotation: math::Angle::radians(arc.rotation.0),
109 start_angle: math::Angle::radians(arc.start_angle.0),
110 sweep_angle: math::Angle::radians((arc.end_angle - arc.start_angle).0),
111 };
112
113 let _ = self.raw.move_to(arc.sample(0.0));
114
115 arc.cast::<f64>().for_each_quadratic_bezier(&mut |curve| {
116 let curve = curve.cast::<f32>();
117 let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);
118 });
119 }
120
121 #[inline]
124 pub fn bezier_curve_to(&mut self, control_a: Point, control_b: Point, to: Point) {
125 let _ = self.raw.cubic_bezier_to(
126 math::Point::new(control_a.x, control_a.y),
127 math::Point::new(control_b.x, control_b.y),
128 math::Point::new(to.x, to.y),
129 );
130 }
131
132 #[inline]
135 pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
136 let _ = self.raw.quadratic_bezier_to(
137 math::Point::new(control.x, control.y),
138 math::Point::new(to.x, to.y),
139 );
140 }
141
142 #[inline]
145 pub fn rectangle(&mut self, top_left: Point, size: Size) {
146 self.move_to(top_left);
147 self.line_to(Point::new(top_left.x + size.width, top_left.y));
148 self.line_to(Point::new(
149 top_left.x + size.width,
150 top_left.y + size.height,
151 ));
152 self.line_to(Point::new(top_left.x, top_left.y + size.height));
153 self.close();
154 }
155
156 #[inline]
159 pub fn rounded_rectangle(&mut self, top_left: Point, size: Size, radius: border::Radius) {
160 let min_size = (size.height / 2.0).min(size.width / 2.0);
161 let [
162 top_left_corner,
163 top_right_corner,
164 bottom_right_corner,
165 bottom_left_corner,
166 ] = radius.into();
167
168 self.move_to(Point::new(
169 top_left.x + min_size.min(top_left_corner),
170 top_left.y,
171 ));
172 self.line_to(Point::new(
173 top_left.x + size.width - min_size.min(top_right_corner),
174 top_left.y,
175 ));
176 self.arc_to(
177 Point::new(top_left.x + size.width, top_left.y),
178 Point::new(
179 top_left.x + size.width,
180 top_left.y + min_size.min(top_right_corner),
181 ),
182 min_size.min(top_right_corner),
183 );
184 self.line_to(Point::new(
185 top_left.x + size.width,
186 top_left.y + size.height - min_size.min(bottom_right_corner),
187 ));
188 self.arc_to(
189 Point::new(top_left.x + size.width, top_left.y + size.height),
190 Point::new(
191 top_left.x + size.width - min_size.min(bottom_right_corner),
192 top_left.y + size.height,
193 ),
194 min_size.min(bottom_right_corner),
195 );
196 self.line_to(Point::new(
197 top_left.x + min_size.min(bottom_left_corner),
198 top_left.y + size.height,
199 ));
200 self.arc_to(
201 Point::new(top_left.x, top_left.y + size.height),
202 Point::new(
203 top_left.x,
204 top_left.y + size.height - min_size.min(bottom_left_corner),
205 ),
206 min_size.min(bottom_left_corner),
207 );
208 self.line_to(Point::new(
209 top_left.x,
210 top_left.y + min_size.min(top_left_corner),
211 ));
212 self.arc_to(
213 Point::new(top_left.x, top_left.y),
214 Point::new(top_left.x + min_size.min(top_left_corner), top_left.y),
215 min_size.min(top_left_corner),
216 );
217 self.close();
218 }
219
220 #[inline]
223 pub fn circle(&mut self, center: Point, radius: f32) {
224 self.arc(Arc {
225 center,
226 radius,
227 start_angle: Radians(0.0),
228 end_angle: Radians(2.0 * std::f32::consts::PI),
229 });
230 }
231
232 #[inline]
235 pub fn close(&mut self) {
236 self.raw.close();
237 }
238
239 #[inline]
241 pub fn build(self) -> Path {
242 Path {
243 raw: self.raw.build(),
244 }
245 }
246}
247
248impl Default for Builder {
249 fn default() -> Self {
250 Self::new()
251 }
252}