1use crate::events::{Event, PathEvent};
83use crate::geom::{traits::Transformation, Arc, ArcFlags, LineSegment, SvgArc};
84use crate::math::*;
85use crate::path::Verb;
86use crate::polygon::Polygon;
87use crate::{Attributes, EndpointId, Winding, NO_ATTRIBUTES};
88
89use core::f32::consts::PI;
90use core::marker::Sized;
91
92use alloc::vec;
93use alloc::vec::Vec;
94
95#[cfg(not(feature = "std"))]
96use num_traits::Float;
97
98#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Default)]
100pub struct BorderRadii {
101 pub top_left: f32,
102 pub top_right: f32,
103 pub bottom_left: f32,
104 pub bottom_right: f32,
105}
106
107impl BorderRadii {
108 pub fn new(radius: f32) -> Self {
109 let r = radius.abs();
110 BorderRadii {
111 top_left: r,
112 top_right: r,
113 bottom_left: r,
114 bottom_right: r,
115 }
116 }
117}
118
119impl core::fmt::Display for BorderRadii {
120 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121 write!(
123 f,
124 "BorderRadii({}, {}, {}, {})",
125 self.top_left, self.top_right, self.bottom_left, self.bottom_right
126 )
127 }
128}
129
130#[derive(Clone, Debug, PartialEq, Hash)]
137#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
138pub struct NoAttributes<B: PathBuilder> {
139 pub(crate) inner: B,
140}
141
142impl<B: PathBuilder> NoAttributes<B> {
143 #[inline]
144 pub fn wrap(inner: B) -> Self {
145 assert_eq!(inner.num_attributes(), 0);
146 NoAttributes { inner }
147 }
148
149 pub fn new() -> Self
150 where
151 B: Default,
152 {
153 NoAttributes::wrap(B::default())
154 }
155
156 pub fn with_capacity(endpoints: usize, ctrl_points: usize) -> Self
157 where
158 B: Default,
159 {
160 let mut builder = B::default();
161 builder.reserve(endpoints, ctrl_points);
162 NoAttributes::wrap(builder)
163 }
164
165 #[inline]
170 pub fn begin(&mut self, at: Point) -> EndpointId {
171 self.inner.begin(at, NO_ATTRIBUTES)
172 }
173
174 #[inline]
180 pub fn end(&mut self, close: bool) {
181 self.inner.end(close);
182 }
183
184 #[inline]
188 pub fn close(&mut self) {
189 self.inner.close();
190 }
191
192 #[inline]
196 pub fn line_to(&mut self, to: Point) -> EndpointId {
197 self.inner.line_to(to, NO_ATTRIBUTES)
198 }
199
200 #[inline]
204 pub fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId {
205 self.inner.quadratic_bezier_to(ctrl, to, NO_ATTRIBUTES)
206 }
207
208 #[inline]
212 pub fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId {
213 self.inner.cubic_bezier_to(ctrl1, ctrl2, to, NO_ATTRIBUTES)
214 }
215
216 #[inline]
222 pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
223 self.inner.reserve(endpoints, ctrl_points);
224 }
225
226 #[inline]
233 pub fn path_event(&mut self, event: PathEvent) {
234 self.inner.path_event(event, NO_ATTRIBUTES);
235 }
236
237 #[inline]
242 pub fn add_polygon(&mut self, polygon: Polygon<Point>) {
243 self.inner.add_polygon(polygon, NO_ATTRIBUTES);
244 }
245
246 #[inline]
251 pub fn add_point(&mut self, at: Point) -> EndpointId {
252 self.inner.add_point(at, NO_ATTRIBUTES)
253 }
254
255 #[inline]
260 pub fn add_line_segment(&mut self, line: &LineSegment<f32>) -> (EndpointId, EndpointId) {
261 self.inner.add_line_segment(line, NO_ATTRIBUTES)
262 }
263
264 #[inline]
269 pub fn add_ellipse(
270 &mut self,
271 center: Point,
272 radii: Vector,
273 x_rotation: Angle,
274 winding: Winding,
275 ) {
276 self.inner
277 .add_ellipse(center, radii, x_rotation, winding, NO_ATTRIBUTES);
278 }
279
280 #[inline]
285 pub fn add_circle(&mut self, center: Point, radius: f32, winding: Winding)
286 where
287 B: Sized,
288 {
289 self.inner
290 .add_circle(center, radius, winding, NO_ATTRIBUTES);
291 }
292
293 #[inline]
298 pub fn add_rectangle(&mut self, rect: &Box2D, winding: Winding) {
299 self.inner.add_rectangle(rect, winding, NO_ATTRIBUTES);
300 }
301
302 #[inline]
307 pub fn add_rounded_rectangle(&mut self, rect: &Box2D, radii: &BorderRadii, winding: Winding)
308 where
309 B: Sized,
310 {
311 self.inner
312 .add_rounded_rectangle(rect, radii, winding, NO_ATTRIBUTES);
313 }
314
315 #[inline]
317 pub fn flattened(self, tolerance: f32) -> NoAttributes<Flattened<B>>
318 where
319 B: Sized,
320 {
321 NoAttributes {
322 inner: Flattened::new(self.inner, tolerance),
323 }
324 }
325
326 #[inline]
328 pub fn transformed<Transform>(
329 self,
330 transform: Transform,
331 ) -> NoAttributes<Transformed<B, Transform>>
332 where
333 B: Sized,
334 Transform: Transformation<f32>,
335 {
336 NoAttributes {
337 inner: Transformed::new(self.inner, transform),
338 }
339 }
340
341 #[inline]
345 pub fn with_svg(self) -> WithSvg<B>
346 where
347 B: Sized,
348 {
349 WithSvg::new(self.inner)
350 }
351
352 #[inline]
354 pub fn build<P>(self) -> P
355 where
356 B: Build<PathType = P>,
357 {
358 self.inner.build()
359 }
360
361 #[inline]
362 pub fn inner(&self) -> &B {
363 &self.inner
364 }
365
366 #[inline]
367 pub fn inner_mut(&mut self) -> &mut B {
368 &mut self.inner
369 }
370
371 #[inline]
372 pub fn into_inner(self) -> B {
373 self.inner
374 }
375}
376
377impl<B: PathBuilder> PathBuilder for NoAttributes<B> {
378 #[inline]
379 fn num_attributes(&self) -> usize {
380 0
381 }
382
383 #[inline]
384 fn begin(&mut self, at: Point, _attributes: Attributes) -> EndpointId {
385 self.inner.begin(at, NO_ATTRIBUTES)
386 }
387
388 #[inline]
389 fn end(&mut self, close: bool) {
390 self.inner.end(close);
391 }
392
393 #[inline]
394 fn line_to(&mut self, to: Point, _attributes: Attributes) -> EndpointId {
395 self.inner.line_to(to, NO_ATTRIBUTES)
396 }
397
398 #[inline]
399 fn quadratic_bezier_to(
400 &mut self,
401 ctrl: Point,
402 to: Point,
403 _attributes: Attributes,
404 ) -> EndpointId {
405 self.inner.quadratic_bezier_to(ctrl, to, NO_ATTRIBUTES)
406 }
407
408 #[inline]
409 fn cubic_bezier_to(
410 &mut self,
411 ctrl1: Point,
412 ctrl2: Point,
413 to: Point,
414 _attributes: Attributes,
415 ) -> EndpointId {
416 self.inner.cubic_bezier_to(ctrl1, ctrl2, to, NO_ATTRIBUTES)
417 }
418
419 #[inline]
420 fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
421 self.inner.reserve(endpoints, ctrl_points)
422 }
423}
424
425impl<B: PathBuilder + Build> Build for NoAttributes<B> {
426 type PathType = B::PathType;
427
428 fn build(self) -> B::PathType {
429 self.inner.build()
430 }
431}
432
433impl<B: PathBuilder + Default> Default for NoAttributes<B> {
434 fn default() -> Self {
435 Self::new()
436 }
437}
438
439pub trait PathBuilder {
452 fn num_attributes(&self) -> usize;
453 fn begin(&mut self, at: Point, custom_attributes: Attributes) -> EndpointId;
458
459 fn end(&mut self, close: bool);
465
466 fn close(&mut self) {
470 self.end(true)
471 }
472
473 fn line_to(&mut self, to: Point, custom_attributes: Attributes) -> EndpointId;
477
478 fn quadratic_bezier_to(
482 &mut self,
483 ctrl: Point,
484 to: Point,
485 custom_attributes: Attributes,
486 ) -> EndpointId;
487
488 fn cubic_bezier_to(
492 &mut self,
493 ctrl1: Point,
494 ctrl2: Point,
495 to: Point,
496 custom_attributes: Attributes,
497 ) -> EndpointId;
498
499 fn reserve(&mut self, _endpoints: usize, _ctrl_points: usize) {}
505
506 fn path_event(&mut self, event: PathEvent, attributes: Attributes) {
513 match event {
514 PathEvent::Begin { at } => {
515 self.begin(at, attributes);
516 }
517 PathEvent::Line { to, .. } => {
518 self.line_to(to, attributes);
519 }
520 PathEvent::Quadratic { ctrl, to, .. } => {
521 self.quadratic_bezier_to(ctrl, to, attributes);
522 }
523 PathEvent::Cubic {
524 ctrl1, ctrl2, to, ..
525 } => {
526 self.cubic_bezier_to(ctrl1, ctrl2, to, attributes);
527 }
528 PathEvent::End { close, .. } => {
529 self.end(close);
530 }
531 }
532 }
533
534 fn event(&mut self, event: Event<(Point, Attributes), Point>) {
535 match event {
536 Event::Begin { at } => {
537 self.begin(at.0, at.1);
538 }
539 Event::Line { to, .. } => {
540 self.line_to(to.0, to.1);
541 }
542 Event::Quadratic { ctrl, to, .. } => {
543 self.quadratic_bezier_to(ctrl, to.0, to.1);
544 }
545 Event::Cubic {
546 ctrl1, ctrl2, to, ..
547 } => {
548 self.cubic_bezier_to(ctrl1, ctrl2, to.0, to.1);
549 }
550 Event::End { close, .. } => {
551 self.end(close);
552 }
553 }
554 }
555
556 fn add_polygon(&mut self, polygon: Polygon<Point>, attributes: Attributes) {
561 if polygon.points.is_empty() {
562 return;
563 }
564
565 self.reserve(polygon.points.len(), 0);
566
567 self.begin(polygon.points[0], attributes);
568 for p in &polygon.points[1..] {
569 self.line_to(*p, attributes);
570 }
571
572 self.end(polygon.closed);
573 }
574
575 fn add_point(&mut self, at: Point, attributes: Attributes) -> EndpointId {
580 let id = self.begin(at, attributes);
581 self.end(false);
582
583 id
584 }
585
586 fn add_line_segment(
591 &mut self,
592 line: &LineSegment<f32>,
593 attributes: Attributes,
594 ) -> (EndpointId, EndpointId) {
595 let a = self.begin(line.from, attributes);
596 let b = self.line_to(line.to, attributes);
597 self.end(false);
598
599 (a, b)
600 }
601
602 fn add_ellipse(
607 &mut self,
608 center: Point,
609 radii: Vector,
610 x_rotation: Angle,
611 winding: Winding,
612 attributes: Attributes,
613 ) {
614 let dir = match winding {
615 Winding::Positive => 1.0,
616 Winding::Negative => -1.0,
617 };
618
619 let arc = Arc {
620 center,
621 radii,
622 x_rotation,
623 start_angle: Angle::radians(0.0),
624 sweep_angle: Angle::radians(2.0 * PI) * dir,
625 };
626
627 self.begin(arc.sample(0.0), attributes);
628 arc.for_each_quadratic_bezier(&mut |curve| {
629 self.quadratic_bezier_to(curve.ctrl, curve.to, attributes);
630 });
631 self.end(true);
632 }
633
634 fn add_circle(&mut self, center: Point, radius: f32, winding: Winding, attributes: Attributes)
639 where
640 Self: Sized,
641 {
642 add_circle(self, center, radius, winding, attributes);
643 }
644
645 fn add_rectangle(&mut self, rect: &Box2D, winding: Winding, attributes: Attributes) {
650 match winding {
651 Winding::Positive => self.add_polygon(
652 Polygon {
653 points: &[
654 rect.min,
655 point(rect.max.x, rect.min.y),
656 rect.max,
657 point(rect.min.x, rect.max.y),
658 ],
659 closed: true,
660 },
661 attributes,
662 ),
663 Winding::Negative => self.add_polygon(
664 Polygon {
665 points: &[
666 rect.min,
667 point(rect.min.x, rect.max.y),
668 rect.max,
669 point(rect.max.x, rect.min.y),
670 ],
671 closed: true,
672 },
673 attributes,
674 ),
675 };
676 }
677
678 fn add_rounded_rectangle(
683 &mut self,
684 rect: &Box2D,
685 radii: &BorderRadii,
686 winding: Winding,
687 custom_attributes: Attributes,
688 ) where
689 Self: Sized,
690 {
691 add_rounded_rectangle(self, rect, radii, winding, custom_attributes);
692 }
693
694 fn flattened(self, tolerance: f32) -> Flattened<Self>
696 where
697 Self: Sized,
698 {
699 Flattened::new(self, tolerance)
700 }
701
702 fn transformed<Transform>(self, transform: Transform) -> Transformed<Self, Transform>
704 where
705 Self: Sized,
706 Transform: Transformation<f32>,
707 {
708 Transformed::new(self, transform)
709 }
710
711 fn with_svg(self) -> WithSvg<Self>
715 where
716 Self: Sized,
717 {
718 WithSvg::new(self)
719 }
720}
721
722pub trait SvgPathBuilder {
731 fn move_to(&mut self, to: Point);
739
740 fn close(&mut self);
749
750 fn line_to(&mut self, to: Point);
758
759 fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point);
768
769 fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point);
778
779 fn relative_move_to(&mut self, to: Vector);
786
787 fn relative_line_to(&mut self, to: Vector);
794
795 fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector);
802
803 fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector);
810
811 fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point);
821
822 fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector);
829
830 fn smooth_quadratic_bezier_to(&mut self, to: Point);
839
840 fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector);
847
848 fn horizontal_line_to(&mut self, x: f32);
854
855 fn relative_horizontal_line_to(&mut self, dx: f32);
862
863 fn vertical_line_to(&mut self, y: f32);
869
870 fn relative_vertical_line_to(&mut self, dy: f32);
877
878 fn arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Point);
889
890 fn relative_arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Vector);
897
898 fn reserve(&mut self, _endpoints: usize, _ctrl_points: usize) {}
904
905 fn add_polygon(&mut self, polygon: Polygon<Point>) {
910 if polygon.points.is_empty() {
911 return;
912 }
913
914 self.reserve(polygon.points.len(), 0);
915
916 self.move_to(polygon.points[0]);
917 for p in &polygon.points[1..] {
918 self.line_to(*p);
919 }
920
921 if polygon.closed {
922 self.close();
923 }
924 }
925}
926
927pub trait Build {
933 type PathType;
935
936 fn build(self) -> Self::PathType;
938}
939
940pub struct Flattened<Builder> {
942 builder: Builder,
943 current_position: Point,
944 tolerance: f32,
945 prev_attributes: Vec<f32>,
946 attribute_buffer: Vec<f32>,
947}
948
949impl<Builder: Build> Build for Flattened<Builder> {
950 type PathType = Builder::PathType;
951
952 fn build(self) -> Builder::PathType {
953 self.builder.build()
954 }
955}
956
957impl<Builder: PathBuilder> PathBuilder for Flattened<Builder> {
958 fn num_attributes(&self) -> usize {
959 self.builder.num_attributes()
960 }
961
962 fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId {
963 self.current_position = at;
964 self.builder.begin(at, attributes)
965 }
966
967 fn end(&mut self, close: bool) {
968 self.builder.end(close)
969 }
970
971 fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId {
972 let id = self.builder.line_to(to, attributes);
973 self.current_position = to;
974 self.prev_attributes.copy_from_slice(attributes);
975 id
976 }
977
978 fn quadratic_bezier_to(
979 &mut self,
980 ctrl: Point,
981 to: Point,
982 attributes: Attributes,
983 ) -> EndpointId {
984 let id = crate::private::flatten_quadratic_bezier(
985 self.tolerance,
986 self.current_position,
987 ctrl,
988 to,
989 attributes,
990 &self.prev_attributes,
991 &mut self.builder,
992 &mut self.attribute_buffer,
993 );
994 self.current_position = to;
995 self.prev_attributes.copy_from_slice(attributes);
996
997 id
998 }
999
1000 fn cubic_bezier_to(
1001 &mut self,
1002 ctrl1: Point,
1003 ctrl2: Point,
1004 to: Point,
1005 attributes: Attributes,
1006 ) -> EndpointId {
1007 let id = crate::private::flatten_cubic_bezier(
1008 self.tolerance,
1009 self.current_position,
1010 ctrl1,
1011 ctrl2,
1012 to,
1013 attributes,
1014 &self.prev_attributes,
1015 &mut self.builder,
1016 &mut self.attribute_buffer,
1017 );
1018 self.current_position = to;
1019 self.prev_attributes.copy_from_slice(attributes);
1020
1021 id
1022 }
1023
1024 fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1025 self.builder.reserve(endpoints + ctrl_points * 4, 0);
1026 }
1027}
1028
1029impl<Builder: PathBuilder> Flattened<Builder> {
1030 pub fn new(builder: Builder, tolerance: f32) -> Flattened<Builder> {
1031 let n = builder.num_attributes();
1032 Flattened {
1033 builder,
1034 current_position: point(0.0, 0.0),
1035 tolerance,
1036 prev_attributes: vec![0.0; n],
1037 attribute_buffer: vec![0.0; n],
1038 }
1039 }
1040
1041 pub fn build(self) -> Builder::PathType
1042 where
1043 Builder: Build,
1044 {
1045 self.builder.build()
1046 }
1047
1048 pub fn set_tolerance(&mut self, tolerance: f32) {
1049 self.tolerance = tolerance
1050 }
1051}
1052
1053pub struct Transformed<Builder, Transform> {
1055 builder: Builder,
1056 transform: Transform,
1057}
1058
1059impl<Builder, Transform> Transformed<Builder, Transform> {
1060 #[inline]
1061 pub fn new(builder: Builder, transform: Transform) -> Self {
1062 Transformed { builder, transform }
1063 }
1064
1065 #[inline]
1066 pub fn set_transform(&mut self, transform: Transform) {
1067 self.transform = transform;
1068 }
1069}
1070
1071impl<Builder: Build, Transform> Build for Transformed<Builder, Transform> {
1072 type PathType = Builder::PathType;
1073
1074 #[inline]
1075 fn build(self) -> Builder::PathType {
1076 self.builder.build()
1077 }
1078}
1079
1080impl<Builder, Transform> PathBuilder for Transformed<Builder, Transform>
1081where
1082 Builder: PathBuilder,
1083 Transform: Transformation<f32>,
1084{
1085 fn num_attributes(&self) -> usize {
1086 self.builder.num_attributes()
1087 }
1088
1089 #[inline]
1090 fn begin(&mut self, at: Point, attributes: Attributes) -> EndpointId {
1091 self.builder
1092 .begin(self.transform.transform_point(at), attributes)
1093 }
1094
1095 #[inline]
1096 fn end(&mut self, close: bool) {
1097 self.builder.end(close)
1098 }
1099
1100 #[inline]
1101 fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId {
1102 self.builder
1103 .line_to(self.transform.transform_point(to), attributes)
1104 }
1105
1106 #[inline]
1107 fn quadratic_bezier_to(
1108 &mut self,
1109 ctrl: Point,
1110 to: Point,
1111 attributes: Attributes,
1112 ) -> EndpointId {
1113 self.builder.quadratic_bezier_to(
1114 self.transform.transform_point(ctrl),
1115 self.transform.transform_point(to),
1116 attributes,
1117 )
1118 }
1119
1120 #[inline]
1121 fn cubic_bezier_to(
1122 &mut self,
1123 ctrl1: Point,
1124 ctrl2: Point,
1125 to: Point,
1126 attributes: Attributes,
1127 ) -> EndpointId {
1128 self.builder.cubic_bezier_to(
1129 self.transform.transform_point(ctrl1),
1130 self.transform.transform_point(ctrl2),
1131 self.transform.transform_point(to),
1132 attributes,
1133 )
1134 }
1135
1136 #[inline]
1137 fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1138 self.builder.reserve(endpoints, ctrl_points);
1139 }
1140}
1141
1142pub struct WithSvg<Builder: PathBuilder> {
1144 builder: Builder,
1145
1146 first_position: Point,
1147 current_position: Point,
1148 last_ctrl: Point,
1149 last_cmd: Verb,
1150 need_moveto: bool,
1151 is_empty: bool,
1152 attribute_buffer: Vec<f32>,
1153}
1154
1155impl<Builder: PathBuilder> WithSvg<Builder> {
1156 pub fn new(builder: Builder) -> Self {
1157 let attribute_buffer = vec![0.0; builder.num_attributes()];
1158 WithSvg {
1159 builder,
1160 first_position: point(0.0, 0.0),
1161 current_position: point(0.0, 0.0),
1162 last_ctrl: point(0.0, 0.0),
1163 need_moveto: true,
1164 is_empty: true,
1165 last_cmd: Verb::End,
1166 attribute_buffer,
1167 }
1168 }
1169
1170 pub fn build(mut self) -> Builder::PathType
1171 where
1172 Builder: Build,
1173 {
1174 self.end_if_needed();
1175 self.builder.build()
1176 }
1177
1178 pub fn flattened(self, tolerance: f32) -> WithSvg<Flattened<Builder>> {
1179 WithSvg::new(Flattened::new(self.builder, tolerance))
1180 }
1181
1182 pub fn transformed<Transform>(
1183 self,
1184 transform: Transform,
1185 ) -> WithSvg<Transformed<Builder, Transform>>
1186 where
1187 Transform: Transformation<f32>,
1188 {
1189 WithSvg::new(Transformed::new(self.builder, transform))
1190 }
1191
1192 pub fn move_to(&mut self, to: Point) -> EndpointId {
1193 self.end_if_needed();
1194
1195 let id = self.builder.begin(to, &self.attribute_buffer);
1196
1197 self.is_empty = false;
1198 self.need_moveto = false;
1199 self.first_position = to;
1200 self.current_position = to;
1201 self.last_cmd = Verb::Begin;
1202
1203 id
1204 }
1205
1206 pub fn line_to(&mut self, to: Point) -> EndpointId {
1207 if let Some(id) = self.begin_if_needed(&to) {
1208 return id;
1209 }
1210
1211 self.current_position = to;
1212 self.last_cmd = Verb::LineTo;
1213
1214 self.builder.line_to(to, &self.attribute_buffer)
1215 }
1216
1217 pub fn close(&mut self) {
1218 if self.need_moveto {
1219 return;
1220 }
1221
1222 self.current_position = self.first_position;
1239 self.need_moveto = true;
1240 self.last_cmd = Verb::Close;
1241
1242 self.builder.close();
1243 }
1244
1245 pub fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) -> EndpointId {
1246 if let Some(id) = self.begin_if_needed(&to) {
1247 return id;
1248 }
1249
1250 self.current_position = to;
1251 self.last_cmd = Verb::QuadraticTo;
1252 self.last_ctrl = ctrl;
1253
1254 self.builder
1255 .quadratic_bezier_to(ctrl, to, &self.attribute_buffer)
1256 }
1257
1258 pub fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) -> EndpointId {
1259 if let Some(id) = self.begin_if_needed(&to) {
1260 return id;
1261 }
1262
1263 self.current_position = to;
1264 self.last_cmd = Verb::CubicTo;
1265 self.last_ctrl = ctrl2;
1266
1267 self.builder
1268 .cubic_bezier_to(ctrl1, ctrl2, to, &self.attribute_buffer)
1269 }
1270
1271 pub fn arc(&mut self, center: Point, radii: Vector, sweep_angle: Angle, x_rotation: Angle) {
1272 nan_check(center);
1273 nan_check(radii.to_point());
1274 debug_assert!(!sweep_angle.get().is_nan());
1275 debug_assert!(!x_rotation.get().is_nan());
1276
1277 self.last_ctrl = self.current_position;
1278
1279 use lyon_geom::euclid::approxeq::ApproxEq;
1283 if self.current_position.approx_eq(¢er) {
1284 return;
1285 }
1286
1287 let start_angle = (self.current_position - center).angle_from_x_axis() - x_rotation;
1288
1289 let arc = Arc {
1290 center,
1291 radii,
1292 start_angle,
1293 sweep_angle,
1294 x_rotation,
1295 };
1296
1297 let arc_start = arc.from();
1300 if self.need_moveto {
1301 self.move_to(arc_start);
1302 } else if (arc_start - self.current_position).square_length() < 0.01 {
1303 self.builder.line_to(arc_start, &self.attribute_buffer);
1304 }
1305
1306 arc.cast::<f64>().for_each_quadratic_bezier(&mut |curve| {
1307 let curve = curve.cast::<f32>();
1308 self.builder
1309 .quadratic_bezier_to(curve.ctrl, curve.to, &self.attribute_buffer);
1310 self.current_position = curve.to;
1311 });
1312 }
1313
1314 #[inline(always)]
1318 fn begin_if_needed(&mut self, default: &Point) -> Option<EndpointId> {
1319 if self.need_moveto {
1320 return self.insert_move_to(default);
1321 }
1322
1323 None
1324 }
1325
1326 #[inline(never)]
1327 fn insert_move_to(&mut self, default: &Point) -> Option<EndpointId> {
1328 if self.is_empty {
1329 return Some(self.move_to(*default));
1330 }
1331
1332 self.move_to(self.first_position);
1333
1334 None
1335 }
1336
1337 fn end_if_needed(&mut self) {
1338 if (self.last_cmd as u8) <= (Verb::Begin as u8) {
1339 self.builder.end(false);
1340 }
1341 }
1342
1343 pub fn current_position(&self) -> Point {
1344 self.current_position
1345 }
1346
1347 pub fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1348 self.builder.reserve(endpoints, ctrl_points);
1349 }
1350
1351 fn get_smooth_cubic_ctrl(&self) -> Point {
1352 match self.last_cmd {
1353 Verb::CubicTo => self.current_position + (self.current_position - self.last_ctrl),
1354 _ => self.current_position,
1355 }
1356 }
1357
1358 fn get_smooth_quadratic_ctrl(&self) -> Point {
1359 match self.last_cmd {
1360 Verb::QuadraticTo => self.current_position + (self.current_position - self.last_ctrl),
1361 _ => self.current_position,
1362 }
1363 }
1364
1365 fn relative_to_absolute(&self, v: Vector) -> Point {
1366 self.current_position + v
1367 }
1368}
1369
1370impl<Builder, Transform> WithSvg<Transformed<Builder, Transform>>
1371where
1372 Builder: PathBuilder,
1373 Transform: Transformation<f32>,
1374{
1375 #[inline]
1376 pub fn set_transform(&mut self, transform: Transform) {
1377 self.builder.set_transform(transform);
1378 }
1379}
1380
1381impl<Builder: PathBuilder + Build> Build for WithSvg<Builder> {
1382 type PathType = Builder::PathType;
1383
1384 fn build(mut self) -> Builder::PathType {
1385 self.end_if_needed();
1386 self.builder.build()
1387 }
1388}
1389
1390impl<Builder: PathBuilder> SvgPathBuilder for WithSvg<Builder> {
1391 fn move_to(&mut self, to: Point) {
1392 self.move_to(to);
1393 }
1394
1395 fn close(&mut self) {
1396 self.close();
1397 }
1398
1399 fn line_to(&mut self, to: Point) {
1400 self.line_to(to);
1401 }
1402
1403 fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
1404 self.quadratic_bezier_to(ctrl, to);
1405 }
1406
1407 fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
1408 self.cubic_bezier_to(ctrl1, ctrl2, to);
1409 }
1410
1411 fn relative_move_to(&mut self, to: Vector) {
1412 let to = self.relative_to_absolute(to);
1413 self.move_to(to);
1414 }
1415
1416 fn relative_line_to(&mut self, to: Vector) {
1417 let to = self.relative_to_absolute(to);
1418 self.line_to(to);
1419 }
1420
1421 fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector) {
1422 let ctrl = self.relative_to_absolute(ctrl);
1423 let to = self.relative_to_absolute(to);
1424 self.quadratic_bezier_to(ctrl, to);
1425 }
1426
1427 fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector) {
1428 let to = self.relative_to_absolute(to);
1429 let ctrl1 = self.relative_to_absolute(ctrl1);
1430 let ctrl2 = self.relative_to_absolute(ctrl2);
1431 self.cubic_bezier_to(ctrl1, ctrl2, to);
1432 }
1433
1434 fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point) {
1435 let ctrl1 = self.get_smooth_cubic_ctrl();
1436 self.cubic_bezier_to(ctrl1, ctrl2, to);
1437 }
1438
1439 fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector) {
1440 let ctrl1 = self.get_smooth_cubic_ctrl();
1441 let ctrl2 = self.relative_to_absolute(ctrl2);
1442 let to = self.relative_to_absolute(to);
1443 self.cubic_bezier_to(ctrl1, ctrl2, to);
1444 }
1445
1446 fn smooth_quadratic_bezier_to(&mut self, to: Point) {
1447 let ctrl = self.get_smooth_quadratic_ctrl();
1448 self.quadratic_bezier_to(ctrl, to);
1449 }
1450
1451 fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector) {
1452 let ctrl = self.get_smooth_quadratic_ctrl();
1453 let to = self.relative_to_absolute(to);
1454 self.quadratic_bezier_to(ctrl, to);
1455 }
1456
1457 fn horizontal_line_to(&mut self, x: f32) {
1458 let y = self.current_position.y;
1459 self.line_to(point(x, y));
1460 }
1461
1462 fn relative_horizontal_line_to(&mut self, dx: f32) {
1463 let p = self.current_position;
1464 self.line_to(point(p.x + dx, p.y));
1465 }
1466
1467 fn vertical_line_to(&mut self, y: f32) {
1468 let x = self.current_position.x;
1469 self.line_to(point(x, y));
1470 }
1471
1472 fn relative_vertical_line_to(&mut self, dy: f32) {
1473 let p = self.current_position;
1474 self.line_to(point(p.x, p.y + dy));
1475 }
1476
1477 fn arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Point) {
1478 let svg_arc = SvgArc {
1479 from: self.current_position,
1480 to,
1481 radii,
1482 x_rotation,
1483 flags: ArcFlags {
1484 large_arc: flags.large_arc,
1485 sweep: flags.sweep,
1486 },
1487 };
1488
1489 if svg_arc.is_straight_line() {
1490 self.line_to(to);
1491 } else {
1492 let arc = svg_arc.to_arc();
1493 self.arc(arc.center, arc.radii, arc.sweep_angle, arc.x_rotation);
1494 }
1495 }
1496
1497 fn relative_arc_to(&mut self, radii: Vector, x_rotation: Angle, flags: ArcFlags, to: Vector) {
1498 let to = self.relative_to_absolute(to);
1499 self.arc_to(radii, x_rotation, flags, to);
1500 }
1501
1502 fn reserve(&mut self, endpoints: usize, ctrl_points: usize) {
1503 self.builder.reserve(endpoints, ctrl_points);
1504 }
1505}
1506
1507fn add_circle<Builder: PathBuilder>(
1509 builder: &mut Builder,
1510 center: Point,
1511 radius: f32,
1512 winding: Winding,
1513 attributes: Attributes,
1514) {
1515 let radius = radius.abs();
1516 let dir = match winding {
1517 Winding::Positive => 1.0,
1518 Winding::Negative => -1.0,
1519 };
1520
1521 const CONSTANT_FACTOR: f32 = 0.55191505;
1523 let d = radius * CONSTANT_FACTOR;
1524
1525 builder.begin(center + vector(-radius, 0.0), attributes);
1526
1527 let ctrl_0 = center + vector(-radius, -d * dir);
1528 let ctrl_1 = center + vector(-d, -radius * dir);
1529 let mid = center + vector(0.0, -radius * dir);
1530 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1531
1532 let ctrl_0 = center + vector(d, -radius * dir);
1533 let ctrl_1 = center + vector(radius, -d * dir);
1534 let mid = center + vector(radius, 0.0);
1535 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1536
1537 let ctrl_0 = center + vector(radius, d * dir);
1538 let ctrl_1 = center + vector(d, radius * dir);
1539 let mid = center + vector(0.0, radius * dir);
1540 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1541
1542 let ctrl_0 = center + vector(-d, radius * dir);
1543 let ctrl_1 = center + vector(-radius, d * dir);
1544 let mid = center + vector(-radius, 0.0);
1545 builder.cubic_bezier_to(ctrl_0, ctrl_1, mid, attributes);
1546
1547 builder.close();
1548}
1549
1550fn add_rounded_rectangle<Builder: PathBuilder>(
1552 builder: &mut Builder,
1553 rect: &Box2D,
1554 radii: &BorderRadii,
1555 winding: Winding,
1556 attributes: Attributes,
1557) {
1558 let w = rect.width();
1559 let h = rect.height();
1560 let x_min = rect.min.x;
1561 let y_min = rect.min.y;
1562 let x_max = rect.max.x;
1563 let y_max = rect.max.y;
1564 let min_wh = w.min(h);
1565 let mut tl = radii.top_left.abs().min(min_wh);
1566 let mut tr = radii.top_right.abs().min(min_wh);
1567 let mut bl = radii.bottom_left.abs().min(min_wh);
1568 let mut br = radii.bottom_right.abs().min(min_wh);
1569
1570 if tl + tr > w {
1572 let x = (tl + tr - w) * 0.5;
1573 tl -= x;
1574 tr -= x;
1575 }
1576 if bl + br > w {
1577 let x = (bl + br - w) * 0.5;
1578 bl -= x;
1579 br -= x;
1580 }
1581 if tr + br > h {
1582 let x = (tr + br - h) * 0.5;
1583 tr -= x;
1584 br -= x;
1585 }
1586 if tl + bl > h {
1587 let x = (tl + bl - h) * 0.5;
1588 tl -= x;
1589 bl -= x;
1590 }
1591
1592 const CONSTANT_FACTOR: f32 = 0.55191505;
1594
1595 let tl_d = tl * CONSTANT_FACTOR;
1596 let tl_corner = point(x_min, y_min);
1597
1598 let tr_d = tr * CONSTANT_FACTOR;
1599 let tr_corner = point(x_max, y_min);
1600
1601 let br_d = br * CONSTANT_FACTOR;
1602 let br_corner = point(x_max, y_max);
1603
1604 let bl_d = bl * CONSTANT_FACTOR;
1605 let bl_corner = point(x_min, y_max);
1606
1607 let points = [
1608 point(x_min, y_min + tl), tl_corner + vector(0.0, tl - tl_d), tl_corner + vector(tl - tl_d, 0.0), tl_corner + vector(tl, 0.0), point(x_max - tr, y_min),
1613 tr_corner + vector(-tr + tr_d, 0.0),
1614 tr_corner + vector(0.0, tr - tr_d),
1615 tr_corner + vector(0.0, tr),
1616 point(x_max, y_max - br),
1617 br_corner + vector(0.0, -br + br_d),
1618 br_corner + vector(-br + br_d, 0.0),
1619 br_corner + vector(-br, 0.0),
1620 point(x_min + bl, y_max),
1621 bl_corner + vector(bl - bl_d, 0.0),
1622 bl_corner + vector(0.0, -bl + bl_d),
1623 bl_corner + vector(0.0, -bl),
1624 ];
1625
1626 if winding == Winding::Positive {
1627 builder.begin(points[0], attributes);
1628 if tl > 0.0 {
1629 builder.cubic_bezier_to(points[1], points[2], points[3], attributes);
1630 }
1631 builder.line_to(points[4], attributes);
1632 if tr > 0.0 {
1633 builder.cubic_bezier_to(points[5], points[6], points[7], attributes);
1634 }
1635 builder.line_to(points[8], attributes);
1636 if br > 0.0 {
1637 builder.cubic_bezier_to(points[9], points[10], points[11], attributes);
1638 }
1639 builder.line_to(points[12], attributes);
1640 if bl > 0.0 {
1641 builder.cubic_bezier_to(points[13], points[14], points[15], attributes);
1642 }
1643 } else {
1644 builder.begin(points[15], attributes);
1645 if bl > 0.0 {
1646 builder.cubic_bezier_to(points[14], points[13], points[12], attributes);
1647 }
1648 builder.line_to(points[11], attributes);
1649 if br > 0.0 {
1650 builder.cubic_bezier_to(points[10], points[9], points[8], attributes);
1651 }
1652 builder.line_to(points[7], attributes);
1653 if tr > 0.0 {
1654 builder.cubic_bezier_to(points[6], points[5], points[4], attributes);
1655 }
1656 builder.line_to(points[3], attributes);
1657 if tl > 0.0 {
1658 builder.cubic_bezier_to(points[2], points[1], points[0], attributes);
1659 }
1660 }
1661 builder.end(true);
1662}
1663
1664#[inline]
1665fn nan_check(p: Point) {
1666 debug_assert!(p.x.is_finite());
1667 debug_assert!(p.y.is_finite());
1668}
1669
1670#[test]
1671fn svg_builder_line_to_after_close() {
1672 use crate::Path;
1673 use crate::PathEvent;
1674
1675 let mut p = Path::svg_builder();
1676 p.line_to(point(1.0, 0.0));
1677 p.close();
1678 p.line_to(point(2.0, 0.0));
1679
1680 let path = p.build();
1681 let mut it = path.iter();
1682 assert_eq!(
1683 it.next(),
1684 Some(PathEvent::Begin {
1685 at: point(1.0, 0.0)
1686 })
1687 );
1688 assert_eq!(
1689 it.next(),
1690 Some(PathEvent::End {
1691 last: point(1.0, 0.0),
1692 first: point(1.0, 0.0),
1693 close: true
1694 })
1695 );
1696 assert_eq!(
1697 it.next(),
1698 Some(PathEvent::Begin {
1699 at: point(1.0, 0.0)
1700 })
1701 );
1702 assert_eq!(
1703 it.next(),
1704 Some(PathEvent::Line {
1705 from: point(1.0, 0.0),
1706 to: point(2.0, 0.0)
1707 })
1708 );
1709 assert_eq!(
1710 it.next(),
1711 Some(PathEvent::End {
1712 last: point(2.0, 0.0),
1713 first: point(1.0, 0.0),
1714 close: false
1715 })
1716 );
1717 assert_eq!(it.next(), None);
1718}
1719
1720#[test]
1721fn svg_builder_relative_curves() {
1722 use crate::Path;
1723 use crate::PathEvent;
1724
1725 let mut p = Path::svg_builder();
1726 p.move_to(point(0.0, 0.0));
1727 p.relative_quadratic_bezier_to(vector(0., 100.), vector(-100., 100.));
1728 p.relative_line_to(vector(-50., 0.));
1729
1730 let path = p.build();
1731 let mut it = path.iter();
1732 assert_eq!(
1733 it.next(),
1734 Some(PathEvent::Begin {
1735 at: point(0.0, 0.0)
1736 })
1737 );
1738 assert_eq!(
1739 it.next(),
1740 Some(PathEvent::Quadratic {
1741 from: point(0.0, 0.0),
1742 ctrl: point(0.0, 100.0),
1743 to: point(-100., 100.),
1744 })
1745 );
1746 assert_eq!(
1747 it.next(),
1748 Some(PathEvent::Line {
1749 from: point(-100.0, 100.0),
1750 to: point(-150., 100.)
1751 })
1752 );
1753 assert_eq!(
1754 it.next(),
1755 Some(PathEvent::End {
1756 first: point(0.0, 0.0),
1757 last: point(-150., 100.),
1758 close: false,
1759 })
1760 );
1761 assert_eq!(it.next(), None);
1762}
1763
1764#[test]
1765fn svg_builder_arc_to_update_position() {
1766 use crate::Path;
1767
1768 let mut p = Path::svg_builder();
1769 p.move_to(point(0.0, 0.0));
1770 assert_eq!(p.current_position(), point(0.0, 0.0));
1771 p.arc_to(
1772 vector(100., 100.),
1773 Angle::degrees(0.),
1774 ArcFlags::default(),
1775 point(0.0, 100.0),
1776 );
1777 assert_ne!(p.current_position(), point(0.0, 0.0));
1778}
1779
1780#[test]
1781fn issue_650() {
1782 let mut builder = crate::path::Path::builder().with_svg();
1783 builder.arc(
1784 point(0.0, 0.0),
1785 vector(50.0, 50.0),
1786 Angle::radians(PI),
1787 Angle::radians(0.0),
1788 );
1789 builder.build();
1790}
1791
1792#[test]
1793fn straight_line_arc() {
1794 use crate::Path;
1795
1796 let mut p = Path::svg_builder();
1797 p.move_to(point(100.0, 0.0));
1798 p.arc_to(
1800 vector(100., 100.),
1801 Angle::degrees(0.),
1802 ArcFlags::default(),
1803 point(100.0, 0.0),
1804 );
1805}
1806
1807#[test]
1808fn top_right_rounded_rect() {
1809 use crate::{math::*, Path};
1810 let mut builder = Path::builder();
1811 builder.add_rounded_rectangle(
1812 &Box2D::new(point(0., 0.), point(100., 100.)),
1813 &BorderRadii {
1814 top_right: 2.,
1815 ..Default::default()
1816 },
1817 Winding::Positive,
1818 );
1819 let path = builder.build();
1820 let tr = path.iter().skip(2).next().unwrap();
1821 assert_eq!(tr.from(), point(98., 0.));
1822 assert_eq!(tr.to(), point(100., 2.));
1823}