1use crate::{
2 BBox, Cubic, Curve, EPSILON, EllipArc, ImageMut, LinColor, Line, Paint, Point, Quad, Scalar,
3 ScalarFormatter, Segment, Size, SvgParserError, SvgPathParser, Transform, curve::line_offset,
4 rasterize::Rasterizer, utils::clamp,
5};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::{
9 fmt,
10 io::{Cursor, Read, Write},
11 str::FromStr,
12};
13
14pub const DEFAULT_FLATNESS: Scalar = 0.05;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub enum FillRule {
22 #[default]
24 #[cfg_attr(feature = "serde", serde(rename = "nonzero"))]
25 NonZero,
26 #[cfg_attr(feature = "serde", serde(rename = "evenodd"))]
28 EvenOdd,
29}
30
31impl FillRule {
32 pub fn alpha_from_winding(&self, winding: Scalar) -> Scalar {
33 match self {
34 FillRule::EvenOdd => ((winding + 1.0).rem_euclid(2.0) - 1.0).abs(),
35 FillRule::NonZero => {
36 let value = winding.abs();
37 if value >= 1.0 {
38 1.0
39 } else if value < 1e-6 {
40 0.0
41 } else {
42 value
43 }
44 }
45 }
46 }
47}
48
49impl FromStr for FillRule {
50 type Err = SvgParserError;
51
52 fn from_str(s: &str) -> Result<Self, Self::Err> {
53 match s {
54 "nonzero" => Ok(FillRule::NonZero),
55 "evenodd" => Ok(FillRule::EvenOdd),
56 _ => Err(SvgParserError::InvalidFillRule),
57 }
58 }
59}
60
61impl fmt::Display for FillRule {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 FillRule::NonZero => "nonzero".fmt(f),
65 FillRule::EvenOdd => "evenodd".fmt(f),
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
73#[cfg_attr(
74 feature = "serde",
75 derive(Serialize, Deserialize),
76 serde(rename_all = "lowercase")
77)]
78pub enum LineJoin {
79 Miter(Scalar),
82 Bevel,
84 Round,
87}
88
89impl Default for LineJoin {
90 fn default() -> Self {
91 Self::Miter(4.0)
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
98#[cfg_attr(
99 feature = "serde",
100 derive(Serialize, Deserialize),
101 serde(rename_all = "lowercase")
102)]
103pub enum LineCap {
104 Butt,
106 Square,
108 Round,
110}
111
112impl Default for LineCap {
113 fn default() -> Self {
114 Self::Butt
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
120#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
121pub struct StrokeStyle {
122 pub width: Scalar,
124 #[cfg_attr(
126 feature = "serde",
127 serde(default, skip_serializing_if = "crate::utils::is_default")
128 )]
129 pub line_join: LineJoin,
130 #[cfg_attr(
132 feature = "serde",
133 serde(default, skip_serializing_if = "crate::utils::is_default")
134 )]
135 pub line_cap: LineCap,
136}
137
138#[derive(Clone, Copy, PartialEq)]
140pub struct SubPath<'a> {
141 segments: &'a [Segment],
143 closed: bool,
145}
146
147impl fmt::Debug for SubPath<'_> {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 for segment in self.segments.iter() {
150 writeln!(f, "{:?}", segment)?;
151 }
152 if self.closed {
153 writeln!(f, "Close")?;
154 } else {
155 writeln!(f, "End")?
156 }
157 Ok(())
158 }
159}
160
161impl<'a> SubPath<'a> {
162 pub fn new(segments: &'a [Segment], closed: bool) -> Option<Self> {
163 if segments.is_empty() {
164 None
165 } else {
166 Some(Self { segments, closed })
167 }
168 }
169
170 pub fn is_closed(&self) -> bool {
172 self.closed
173 }
174
175 pub fn segments(&self) -> &[Segment] {
176 self.segments
177 }
178
179 pub fn first(&self) -> Segment {
181 *self.segments.first().expect("SubPath is never emtpy")
182 }
183
184 pub fn last(&self) -> Segment {
186 *self.segments.last().expect("SubPath is never empty")
187 }
188
189 pub fn flatten(
190 &self,
191 tr: Transform,
192 flatness: Scalar,
193 close: bool,
194 ) -> impl Iterator<Item = Line> + '_ {
195 let last = if self.closed || close {
196 Some(Line::new(self.end(), self.start()).transform(tr))
197 } else {
198 None
199 };
200 self.segments
201 .iter()
202 .flat_map(move |segment| segment.flatten(tr, flatness))
203 .chain(last)
204 }
205
206 pub fn start(&self) -> Point {
208 self.first().start()
209 }
210
211 pub fn end(&self) -> Point {
213 self.last().end()
214 }
215
216 pub fn bbox(&self, init: Option<BBox>, tr: Transform) -> BBox {
218 self.segments
219 .iter()
220 .fold(init, |bbox, seg| Some(seg.transform(tr).bbox(bbox)))
221 .expect("SubPath is never empty")
222 }
223}
224
225#[derive(Clone, PartialEq, Default)]
228pub struct Path {
229 segments: Vec<Segment>,
230 subpaths: Vec<usize>,
232 closed: Vec<bool>,
233}
234
235impl Path {
236 pub fn new(segments: Vec<Segment>, subpaths: Vec<usize>, closed: Vec<bool>) -> Self {
237 Self {
238 segments,
239 subpaths,
240 closed,
241 }
242 }
243
244 pub fn empty() -> Self {
246 Self {
247 segments: Vec::new(),
248 subpaths: Vec::new(),
249 closed: Vec::new(),
250 }
251 }
252
253 pub fn is_empty(&self) -> bool {
255 self.subpaths.is_empty()
256 }
257
258 pub fn len(&self) -> usize {
260 if self.subpaths.len() < 2 {
261 0
262 } else {
263 self.subpaths.len() - 1
264 }
265 }
266
267 pub fn winding_at(&self, point: impl Into<Point>) -> i32 {
269 let point = point.into();
270 let y = point.y();
277 let tr = Transform::new_translate(0.0, -y);
278 let mut winding = 0;
279 for subpath in self.subpaths() {
280 let last = if subpath.closed {
281 Some(Line::new(subpath.end(), subpath.start()).into())
282 } else {
283 None
284 };
285 for segment in subpath.segments().iter().chain(&last) {
286 let points: &[Point] = match segment {
287 Segment::Line(Line(points)) => points,
288 Segment::Quad(Quad(points)) => points,
289 Segment::Cubic(Cubic(points)) => points,
290 };
291 let (above, below) = points.iter().fold((false, false), |(above, below), ctrl| {
292 if ctrl.y() > point.y() {
293 (true, below)
294 } else {
295 (above, true)
296 }
297 });
298 if !above || !below {
299 continue;
301 }
302 for root_t in segment.transform(tr).roots() {
303 let root = segment.at(root_t);
304 if root.x() > point.x() {
305 continue;
306 }
307 let deriv = segment.deriv().at(root_t);
308 if deriv.y() > 0.0 {
309 winding += 1;
310 } else if deriv.y() < 0.0 {
311 winding -= 1;
312 }
313 }
314 }
315 }
316 winding
317 }
318
319 pub fn get(&self, index: usize) -> Option<SubPath<'_>> {
321 let start = *self.subpaths.get(index)?;
322 let end = *self.subpaths.get(index + 1)?;
323 let segments = self.segments.get(start..end)?;
324 let closed = *self.closed.get(index)?;
325 Some(SubPath { segments, closed })
326 }
327
328 pub fn subpaths(&self) -> PathIter<'_> {
330 PathIter {
331 path: self,
332 index: 0,
333 }
334 }
335
336 pub fn push(&mut self, segments: &[Segment], closed: bool) {
337 if segments.is_empty() {
338 return;
339 }
340 if self.subpaths.is_empty() {
341 self.subpaths.push(0);
342 }
343 self.segments.extend(segments.iter().copied());
344 self.subpaths.push(self.segments.len());
345 self.closed.push(closed);
346 }
347
348 pub fn builder() -> PathBuilder {
350 PathBuilder::new()
351 }
352
353 pub fn into_builder(self) -> PathBuilder {
355 PathBuilder::from_path(self)
356 }
357
358 pub fn transform(&mut self, tr: Transform) {
360 self.segments.iter_mut().for_each(|segment| {
361 *segment = segment.transform(tr);
362 });
363 }
364
365 pub fn segments_count(&self) -> usize {
367 self.segments.len()
368 }
369
370 pub fn stroke(&self, style: StrokeStyle) -> Path {
375 let mut result = Path::empty();
376 let mut segments = Vec::new();
377 for subpath in self.subpaths() {
378 for segment in subpath.segments() {
380 stroke_segment(&mut segments, *segment, style, Segment::line_join);
381 }
382 let mut backward = subpath.segments.iter().rev().map(Segment::reverse);
383 if subpath.is_closed() {
385 stroke_close(subpath, &mut segments, style, true);
386 result.push(&segments, true);
387 segments.clear();
388 } else {
389 if let Some(segment) = backward.next() {
391 stroke_segment(&mut segments, segment, style, Segment::line_cap);
392 }
393 }
394 for segment in backward {
396 stroke_segment(&mut segments, segment, style, Segment::line_join);
397 }
398 if subpath.is_closed() {
400 stroke_close(subpath, &mut segments, style, false);
401 result.push(&segments, true);
402 segments.clear();
403 } else {
404 let last = segments.last().copied();
406 let first = segments.first().copied();
407 if let (Some(last), Some(first)) = (last, first) {
408 segments.extend(last.line_cap(first, style));
409 }
410 result.push(&segments, true);
411 segments.clear();
412 }
413 }
414 result
415 }
416
417 pub fn flatten(
419 &self,
420 tr: Transform,
421 flatness: Scalar,
422 close: bool,
423 ) -> impl Iterator<Item = Line> + '_ {
424 PathFlattenIter::new(self, tr, flatness, close)
425 }
426
427 pub fn bbox(&self, tr: Transform) -> Option<BBox> {
429 self.subpaths()
430 .fold(None, |bbox, subpath| Some(subpath.bbox(bbox, tr)))
431 }
432
433 pub fn size(&self, tr: Transform) -> Option<(Size, Transform, Point)> {
440 let bbox = self.bbox(tr)?;
441 let Point([min_x, min_y]) = bbox.min();
442 let Point([max_x, max_y]) = bbox.max();
443 let min = Point::new(min_x.floor() - 1.0, min_y.floor() - 1.0);
444 let max = Point::new(max_x.ceil() + 1.0, max_y.ceil() + 1.0);
445 let size = Size {
446 width: (max.x() - min.x()).round() as usize,
447 height: (max.y() - min.y()).round() as usize,
448 };
449 let shift = Transform::new_translate(1.0 - min_x, 1.0 - min_y);
450 Some((size, shift * tr, min))
451 }
452
453 pub fn reverse(&mut self) {
455 if self.segments.is_empty() {
456 return;
457 }
458
459 let mut left = 0;
461 let mut right = self.segments.len() - 1;
462 while left < right {
463 let left_segment = self.segments[left].reverse();
464 let right_segment = self.segments[right].reverse();
465 self.segments[left] = right_segment;
466 self.segments[right] = left_segment;
467 left += 1;
468 right -= 1;
469 }
470 if left == right {
471 let left_segment = self.segments[left].reverse();
472 self.segments[left] = left_segment;
473 }
474
475 for index in 0..(self.subpaths.len() - 1) {
477 self.subpaths[index] = self.subpaths[index + 1] - self.subpaths[index];
478 }
479 self.subpaths.reverse();
480 self.subpaths[0] = 0;
481 let mut offset = 0;
482 for index in 1..self.subpaths.len() {
483 offset += self.subpaths[index];
484 self.subpaths[index] = offset;
485 }
486
487 self.closed.reverse();
489 }
490
491 pub fn fill<R, P, I>(
493 &self,
494 rasterizer: R,
495 tr: Transform,
496 fill_rule: FillRule,
497 paint: P,
498 mut img: I,
499 ) -> I
500 where
501 R: Rasterizer,
502 P: Paint,
503 I: ImageMut<Pixel = LinColor>,
504 {
505 rasterizer.fill(self, tr, fill_rule, &paint, &mut img);
506 img
507 }
508
509 pub fn mask<R, I>(&self, rasterizer: R, tr: Transform, fill_rule: FillRule, mut img: I) -> I
514 where
515 R: Rasterizer,
516 I: ImageMut<Pixel = Scalar>,
517 {
518 rasterizer.mask(self, tr, &mut img, fill_rule);
519 img
520 }
521
522 pub fn write_svg_path(&self, mut out: impl Write) -> std::io::Result<()> {
524 write!(out, "{}", self)
525 }
526
527 pub fn read_svg_path(input: impl Read) -> std::io::Result<Self> {
529 let mut builder = PathBuilder::new();
530 for cmd in SvgPathParser::new(input) {
531 cmd?.apply(&mut builder)
532 }
533 Ok(builder.build())
534 }
535
536 pub fn display(&self, relative: bool, tr: Transform) -> PathSvgDisplay<'_> {
537 PathSvgDisplay {
538 path: self,
539 relative,
540 tr,
541 }
542 }
543
544 pub fn verbose_debug(&self) -> PathVerboseDebug<'_> {
546 PathVerboseDebug { path: self }
547 }
548}
549
550impl fmt::Debug for Path {
551 fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
552 use std::fmt::Display;
553 self.display(false, Transform::identity()).fmt(out)
554 }
555}
556
557impl fmt::Display for Path {
558 fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
559 self.display(false, Transform::identity()).fmt(out)
560 }
561}
562
563pub struct PathVerboseDebug<'a> {
564 path: &'a Path,
565}
566
567impl fmt::Debug for PathVerboseDebug<'_> {
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 if self.path.subpaths.is_empty() {
570 write!(f, "Empty")?;
571 } else {
572 for subpath in self.path.subpaths.iter() {
573 subpath.fmt(f)?
574 }
575 }
576 Ok(())
577 }
578}
579
580pub struct PathSvgDisplay<'a> {
581 path: &'a Path,
582 relative: bool,
583 tr: Transform,
584}
585
586impl PathSvgDisplay<'_> {
587 fn fmt_point(
588 &self,
589 out: &mut fmt::Formatter<'_>,
590 formatter: &mut ScalarFormatter,
591 point: Point,
592 previous: Option<Point>,
593 sep: bool,
594 ) -> Result<Point, fmt::Error> {
595 let point_tr = self.tr.apply(point);
596 let point = previous.map_or_else(|| point_tr, |point_prev| point_tr - point_prev);
597 let x = point.x();
598 let y = point.y();
599 if sep && x >= 0.0 {
600 out.write_str(" ")?;
601 }
602 out.write_str(formatter.format_str(x))?;
603 if y >= 0.0 {
604 out.write_str(",")?;
605 }
606 out.write_str(formatter.format_str(y))?;
607 Ok(point_tr)
608 }
609}
610
611impl fmt::Display for PathSvgDisplay<'_> {
612 fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
613 let mut formatter = ScalarFormatter::new_fmt(out);
614 let mut previous: Option<Point> = None;
615 for subpath in self.path.subpaths() {
616 out.write_str(if self.relative && previous.is_some() {
617 "m"
618 } else {
619 "M"
620 })?;
621 let point_last =
622 self.fmt_point(out, &mut formatter, subpath.start(), previous, false)?;
623 if self.relative {
624 previous = Some(point_last);
625 }
626
627 for segment in subpath.segments().iter() {
628 let point_last = match segment {
629 Segment::Line(line) => {
630 out.write_str(if self.relative { "l" } else { "L" })?;
631 self.fmt_point(out, &mut formatter, line.end(), previous, false)?
632 }
633 Segment::Quad(quad) => {
634 out.write_str(if self.relative { "q" } else { "Q" })?;
635 let [_, p1, p2] = quad.points();
636 self.fmt_point(out, &mut formatter, p1, previous, false)?;
637 self.fmt_point(out, &mut formatter, p2, previous, true)?
638 }
639 Segment::Cubic(cubic) => {
640 out.write_str(if self.relative { "c" } else { "C" })?;
641 let [_, p1, p2, p3] = cubic.points();
642 self.fmt_point(out, &mut formatter, p1, previous, false)?;
643 self.fmt_point(out, &mut formatter, p2, previous, true)?;
644 self.fmt_point(out, &mut formatter, p3, previous, true)?
645 }
646 };
647 if self.relative {
648 previous = Some(point_last);
649 }
650 }
651 if subpath.is_closed() {
652 write!(out, "Z")?;
653 }
654 }
655 Ok(())
656 }
657}
658
659pub struct PathIter<'a> {
660 path: &'a Path,
661 index: usize,
662}
663
664impl<'a> Iterator for PathIter<'a> {
665 type Item = SubPath<'a>;
666
667 fn next(&mut self) -> Option<Self::Item> {
668 let result = self.path.get(self.index)?;
669 self.index += 1;
670 Some(result)
671 }
672}
673
674impl<'a> IntoIterator for &'a Path {
675 type Item = SubPath<'a>;
676 type IntoIter = PathIter<'a>;
677
678 fn into_iter(self) -> Self::IntoIter {
679 self.subpaths()
680 }
681}
682
683impl<'a> Extend<SubPath<'a>> for Path {
684 fn extend<T: IntoIterator<Item = SubPath<'a>>>(&mut self, iter: T) {
685 for subpath in iter {
686 self.push(subpath.segments, subpath.closed);
687 }
688 }
689}
690
691fn stroke_segment<F, S>(segments: &mut Vec<Segment>, segment: Segment, style: StrokeStyle, join: F)
693where
694 F: Fn(Segment, Segment, StrokeStyle) -> S,
695 S: IntoIterator<Item = Segment>,
696{
697 let offset = segments.len();
698 segment.offset(style.width / 2.0, segments);
699 if offset != 0 {
700 let src = segments.get(offset - 1).copied();
701 let dst = segments.get(offset).copied();
702 if let (Some(src), Some(dst)) = (src, dst) {
703 segments.splice(offset..offset, join(src, dst, style));
704 }
705 }
706}
707
708fn stroke_close(
709 subpath: SubPath<'_>,
710 segments: &mut Vec<Segment>,
711 style: StrokeStyle,
712 forward: bool,
713) {
714 let (first, last) = match (segments.first(), segments.last()) {
715 (Some(first), Some(last)) => (*first, *last),
716 _ => return,
717 };
718 let close = if forward {
719 Line::new(subpath.end(), subpath.start())
720 } else {
721 Line::new(subpath.start(), subpath.end())
722 };
723 match line_offset(close, style.width / 2.0) {
724 Some(close) if close.length() * 100.0 > style.width => {
725 let close = Segment::from(close);
726 segments.extend(last.line_join(close, style));
727 segments.push(close);
728 segments.extend(close.line_join(first, style));
729 }
730 _ => segments.extend(last.line_join(first, style)),
731 }
732}
733
734pub struct PathFlattenIter<'a> {
735 path: &'a Path,
736 transform: Transform,
737 flatness: Scalar,
738 close: bool,
739 subpath_index: usize,
740 segment_index: usize,
741 stack: Vec<Segment>,
742}
743
744impl<'a> PathFlattenIter<'a> {
745 fn new(path: &'a Path, transform: Transform, flatness: Scalar, close: bool) -> Self {
746 Self {
747 path,
748 transform,
749 flatness: 16.0 * flatness * flatness,
750 close,
751 subpath_index: 0,
752 segment_index: 0,
753 stack: Default::default(),
754 }
755 }
756}
757
758impl Iterator for PathFlattenIter<'_> {
759 type Item = Line;
760
761 fn next(&mut self) -> Option<Self::Item> {
762 loop {
763 match self.stack.pop() {
764 Some(segment) => {
765 if segment.has_nans() {
766 panic!("cannot flatten segment with NaN");
767 }
768 if segment.flatness() < self.flatness {
769 return Some(Line::new(segment.start(), segment.end()));
770 }
771 let (s0, s1) = segment.split();
772 self.stack.push(s1);
773 self.stack.push(s0);
774 }
775 None => {
776 let subpath = self.path.get(self.subpath_index)?;
777 match subpath.segments().get(self.segment_index) {
778 None => {
779 self.subpath_index += 1;
780 self.segment_index = 0;
781 if subpath.closed || self.close {
782 let line = Line::new(subpath.end(), subpath.start())
783 .transform(self.transform);
784 return Some(line);
785 }
786 }
787 Some(segment) => {
788 self.segment_index += 1;
789 self.stack.push(segment.transform(self.transform));
790 }
791 }
792 }
793 }
794 }
795 }
796}
797
798#[derive(Clone)]
800pub struct PathBuilder {
801 position: Point,
802 segments: Vec<Segment>,
803 subpaths: Vec<usize>,
804 closed: Vec<bool>,
805}
806
807impl Default for PathBuilder {
808 fn default() -> Self {
809 Self::new()
810 }
811}
812
813impl PathBuilder {
814 pub fn new() -> Self {
815 Self {
816 position: Point::new(0.0, 0.0),
817 segments: Vec::new(),
818 subpaths: Vec::new(),
819 closed: Vec::new(),
820 }
821 }
822
823 pub fn from_path(path: Path) -> Self {
824 let mut builder = Self::new();
825 builder.segments = path.segments;
826 builder.subpaths = path.subpaths;
827 builder.closed = path.closed;
828 builder
829 }
830
831 pub fn build(&mut self) -> Path {
833 self.subpath_finish(false);
834 let PathBuilder {
835 segments,
836 subpaths,
837 closed,
838 ..
839 } = std::mem::take(self);
840 Path::new(segments, subpaths, closed)
841 }
842
843 pub fn push(&mut self, segments: &[Segment], closed: bool) {
844 self.segments.extend(segments.iter().copied());
845 self.subpath_finish(closed);
846 }
847
848 fn subpath_finish(&mut self, close: bool) {
850 if self.segments.is_empty() || self.subpaths.last().copied() == Some(self.segments.len()) {
851 return;
852 }
853 if self.subpaths.is_empty() {
854 self.subpaths.push(0);
855 }
856 if close {
857 if let Some(subpath_start) = self
859 .subpaths
860 .last()
861 .and_then(|first_index| Some(self.segments.get(*first_index)?.start()))
862 {
863 self.position = subpath_start;
864 }
865 }
866 self.subpaths.push(self.segments.len());
867 self.closed.push(close);
868 }
869
870 pub fn append_svg_path(
872 &mut self,
873 string: impl AsRef<[u8]>,
874 ) -> Result<&mut Self, SvgParserError> {
875 for cmd in SvgPathParser::new(Cursor::new(string)) {
876 cmd?.apply(self);
877 }
878 Ok(self)
879 }
880
881 pub fn move_to(&mut self, p: impl Into<Point>) -> &mut Self {
883 self.subpath_finish(false);
884 self.position = p.into();
885 self
886 }
887
888 pub fn close(&mut self) -> &mut Self {
890 self.subpath_finish(true);
891 self
892 }
893
894 pub fn line_to(&mut self, p: impl Into<Point>) -> &mut Self {
896 let p = p.into();
897 if !self.position.is_close_to(p) {
898 let line = Line::new(self.position, p);
899 self.position = line.end();
900 self.segments.push(line.into());
901 }
902 self
903 }
904
905 pub fn quad_to(&mut self, p1: impl Into<Point>, p2: impl Into<Point>) -> &mut Self {
907 let quad = Quad::new(self.position, p1, p2);
908 self.position = quad.end();
909 self.segments.push(quad.into());
910 self
911 }
912
913 pub fn quad_smooth_to(&mut self, p2: impl Into<Point>) -> &mut Self {
915 let p1 = match self.segments.last() {
916 Some(Segment::Quad(quad)) => quad.smooth(),
917 _ => self.position,
918 };
919 self.quad_to(p1, p2)
920 }
921
922 pub fn cubic_to(
924 &mut self,
925 p1: impl Into<Point>,
926 p2: impl Into<Point>,
927 p3: impl Into<Point>,
928 ) -> &mut Self {
929 let cubic = Cubic::new(self.position, p1, p2, p3);
930 self.position = cubic.end();
931 self.segments.push(cubic.into());
932 self
933 }
934
935 pub fn cubic_smooth_to(&mut self, p2: impl Into<Point>, p3: impl Into<Point>) -> &mut Self {
937 let p1 = match self.segments.last() {
938 Some(Segment::Cubic(cubic)) => cubic.smooth(),
939 _ => self.position,
940 };
941 self.cubic_to(p1, p2, p3)
942 }
943
944 pub fn arc_to(
946 &mut self,
947 radii: impl Into<Point>,
948 x_axis_rot: Scalar,
949 large: bool,
950 sweep: bool,
951 p: impl Into<Point>,
952 ) -> &mut Self {
953 let radii: Point = radii.into();
954 let p = p.into();
955 let arc = EllipArc::new_param(
956 self.position,
957 p,
958 radii.x(),
959 radii.y(),
960 x_axis_rot,
961 large,
962 sweep,
963 );
964 match arc {
965 None => self.line_to(p),
966 Some(arc) => {
967 self.segments.extend(arc.to_cubics().map(Segment::from));
968 self.position = p;
969 self
970 }
971 }
972 }
973
974 pub fn circle(&mut self, radius: Scalar) -> &mut Self {
978 let offset = 0.5522847498307935 * radius;
981 let x_offset = Point::new(offset, 0.0);
982 let y_offset = Point::new(0.0, offset);
983 let center = self.position();
984 let p0 = center - Point::new(radius, 0.0);
985 let p1 = center - Point::new(0.0, radius);
986 let p2 = center + Point::new(radius, 0.0);
987 let p3 = center + Point::new(0.0, radius);
988
989 self.move_to(p0)
990 .cubic_to(p0 - y_offset, p1 - x_offset, p1)
991 .cubic_to(p1 + x_offset, p2 - y_offset, p2)
992 .cubic_to(p2 + y_offset, p3 + x_offset, p3)
993 .cubic_to(p3 - x_offset, p0 + y_offset, p0)
994 .close()
995 .move_to(center)
996 }
997
998 pub fn rbox(&mut self, size: impl Into<Point>, radii: impl Into<Point>) -> &mut Self {
1000 let init = self.position;
1001 let bbox = BBox::new(self.position, self.position + size.into());
1002 let Point([lx, ly]) = bbox.min();
1003 let Point([hx, hy]) = bbox.max();
1004
1005 let Point([rx, ry]) = radii.into();
1006 let rx = clamp(rx.abs(), 0.0, hx - lx);
1007 let ry = clamp(ry.abs(), 0.0, hy - ly);
1008 let radii = Point::new(rx, ry);
1009 let rounded = rx > EPSILON && ry > EPSILON;
1010
1011 self.move_to((lx + rx, ly)).line_to((hx - rx, ly));
1012 if rounded {
1013 self.arc_to(radii, 0.0, false, true, (hx, ly + ry));
1014 }
1015 self.line_to((hx, hy - ry));
1016 if rounded {
1017 self.arc_to(radii, 0.0, false, true, (hx - rx, hy));
1018 }
1019 self.line_to((lx + rx, hy));
1020 if rounded {
1021 self.arc_to(radii, 0.0, false, true, (lx, hy - ry));
1022 }
1023 self.line_to((lx, ly + ry));
1024 if rounded {
1025 self.arc_to(radii, 0.0, false, true, (lx + rx, ly));
1026 }
1027 self.close().move_to(init)
1028 }
1029
1030 pub fn checkerboard(&mut self, bbox: BBox, cell_size: Scalar) -> &mut Self {
1032 let mut x = bbox.x();
1033 let mut y = bbox.y();
1034 while y < bbox.max().y() {
1035 while x < bbox.max().x() {
1036 let offset = Point::new(x, y);
1037 self.move_to(offset)
1038 .line_to(offset + Point::new(cell_size, 0.0))
1039 .line_to(offset + Point::new(cell_size, 2.0 * cell_size))
1040 .line_to(offset + Point::new(2.0 * cell_size, 2.0 * cell_size))
1041 .line_to(offset + Point::new(2.0 * cell_size, cell_size))
1042 .line_to(offset + Point::new(0.0, cell_size))
1043 .close();
1044 x += 2.0 * cell_size;
1045 }
1046 x = bbox.x();
1047 y += 2.0 * cell_size;
1048 }
1049 self.move_to(bbox.min())
1050 }
1051
1052 pub fn position(&self) -> Point {
1054 self.position
1055 }
1056}
1057
1058impl FromStr for Path {
1059 type Err = SvgParserError;
1060
1061 fn from_str(text: &str) -> Result<Path, Self::Err> {
1062 Ok(Path::read_svg_path(Cursor::new(text))?)
1063 }
1064}
1065
1066#[cfg(feature = "serde")]
1067impl Serialize for Path {
1068 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1069 where
1070 S: serde::Serializer,
1071 {
1072 serializer.collect_str(self)
1073 }
1074}
1075
1076#[cfg(feature = "serde")]
1077impl<'de> Deserialize<'de> for Path {
1078 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1079 where
1080 D: serde::Deserializer<'de>,
1081 {
1082 std::borrow::Cow::<'de, str>::deserialize(deserializer)?
1083 .parse()
1084 .map_err(serde::de::Error::custom)
1085 }
1086}
1087
1088#[cfg(test)]
1089mod tests {
1090 use super::*;
1091 use crate::{PI, assert_approx_eq};
1092
1093 fn assert_path_eq(p0: &Path, p1: &Path) {
1094 assert_eq!(format!("{:#}", p0), format!("{:#}", p1));
1095 }
1096
1097 #[test]
1098 fn test_bbox() {
1099 let path: Path = SQUIRREL.parse().unwrap();
1100 let bbox = path.bbox(Transform::identity()).unwrap();
1101 assert_approx_eq!(bbox.x(), 0.25);
1102 assert_approx_eq!(bbox.y(), 1.0);
1103 assert_approx_eq!(bbox.width(), 15.75);
1104 assert_approx_eq!(bbox.height(), 14.0);
1105 }
1106
1107 const SQUIRREL: &str = r#"
1108 M12 1C9.79 1 8 2.31 8 3.92c0 1.94.5 3.03 0 6.08 0-4.5-2.77-6.34-4-6.34.05-.5-.48
1109 -.66-.48-.66s-.22.11-.3.34c-.27-.31-.56-.27-.56-.27l-.13.58S.7 4.29 .68 6.87c.2.33
1110 1.53.6 2.47.43.89.05.67.79.47.99C2.78 9.13 2 8 1 8S0 9 1 9s1 1 3 1c-3.09 1.2 0 4 0 4
1111 H3c-1 0-1 1-1 1h6c3 0 5-1 5-3.47 0-.85-.43-1.79 -1-2.53-1.11-1.46.23-2.68 1-2
1112 .77.68 3 1 3-2 0-2.21-1.79-4-4-4zM2.5 6 c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5
1113 -.22.5-.5.5z
1114 "#;
1115
1116 #[test]
1117 fn test_path_parse() -> Result<(), SvgParserError> {
1118 let path: Path = SQUIRREL.parse()?;
1120 let reference = Path::builder()
1121 .move_to((12.0, 1.0))
1122 .cubic_to((9.79, 1.0), (8.0, 2.31), (8.0, 3.92))
1123 .cubic_to((8.0, 5.86), (8.5, 6.95), (8.0, 10.0))
1124 .cubic_to((8.0, 5.5), (5.23, 3.66), (4.0, 3.66))
1125 .cubic_to((4.05, 3.16), (3.52, 3.0), (3.52, 3.0))
1126 .cubic_to((3.52, 3.0), (3.3, 3.11), (3.22, 3.34))
1127 .cubic_to((2.95, 3.03), (2.66, 3.07), (2.66, 3.07))
1128 .line_to((2.53, 3.65))
1129 .cubic_to((2.53, 3.65), (0.7, 4.29), (0.68, 6.87))
1130 .cubic_to((0.88, 7.2), (2.21, 7.47), (3.15, 7.3))
1131 .cubic_to((4.04, 7.35), (3.82, 8.09), (3.62, 8.29))
1132 .cubic_to((2.78, 9.13), (2.0, 8.0), (1.0, 8.0))
1133 .cubic_to((0.0, 8.0), (0.0, 9.0), (1.0, 9.0))
1134 .cubic_to((2.0, 9.0), (2.0, 10.0), (4.0, 10.0))
1135 .cubic_to((0.91, 11.2), (4.0, 14.0), (4.0, 14.0))
1136 .line_to((3.0, 14.0))
1137 .cubic_to((2.0, 14.0), (2.0, 15.0), (2.0, 15.0))
1138 .line_to((8.0, 15.0))
1139 .cubic_to((11.0, 15.0), (13.0, 14.0), (13.0, 11.53))
1140 .cubic_to((13.0, 10.68), (12.57, 9.74), (12.0, 9.0))
1141 .cubic_to((10.89, 7.54), (12.23, 6.32), (13.0, 7.0))
1142 .cubic_to((13.77, 7.68), (16.0, 8.0), (16.0, 5.0))
1143 .cubic_to((16.0, 2.79), (14.21, 1.0), (12.0, 1.0))
1144 .close()
1145 .move_to((2.5, 6.0))
1146 .cubic_to((2.22, 6.0), (2.0, 5.78), (2.0, 5.5))
1147 .cubic_to((2.0, 5.22), (2.22, 5.0), (2.5, 5.0))
1148 .cubic_to((2.78, 5.0), (3.0, 5.22), (3.0, 5.5))
1149 .cubic_to((3.0, 5.78), (2.78, 6.0), (2.5, 6.0))
1150 .close()
1151 .build();
1152 assert_path_eq(&path, &reference);
1153
1154 let path: Path = " M0,0L1-1L1,0ZL0,1 L1,1Z ".parse()?;
1155 let reference = Path::builder()
1156 .move_to((0.0, 0.0))
1157 .line_to((1.0, -1.0))
1158 .line_to((1.0, 0.0))
1159 .close()
1160 .move_to((0.0, 0.0))
1161 .line_to((0.0, 1.0))
1162 .line_to((1.0, 1.0))
1163 .close()
1164 .build();
1165 assert_path_eq(&path, &reference);
1166
1167 let reference = Path::builder()
1168 .move_to((0.5, -3.0))
1169 .line_to((-11.0, -0.11))
1170 .build();
1171 let p1: Path = "M.5-3-11-.11".parse()?;
1173 let p2: Path = " m.5,-3 -11.5\n2.89 ".parse()?;
1175 assert_path_eq(&reference, &p1);
1176 assert_path_eq(&reference, &p2);
1177
1178 Ok(())
1179 }
1180
1181 #[test]
1182 fn test_save_load() -> std::io::Result<()> {
1183 let path: Path = SQUIRREL.parse()?;
1184 let mut path_save = Vec::new();
1185 path.write_svg_path(&mut path_save)?;
1186 let path_load = Path::read_svg_path(std::io::Cursor::new(path_save))?;
1187 assert_path_eq(&path, &path_load);
1188 Ok(())
1189 }
1190
1191 #[test]
1192 fn test_flatten() -> Result<(), SvgParserError> {
1193 let path: Path = SQUIRREL.parse()?;
1194 let tr = Transform::new_rotate(PI / 3.0).pre_translate(-10.0, -20.0);
1195 let lines: Vec<_> = path.flatten(tr, DEFAULT_FLATNESS, true).collect();
1196 let mut reference = Vec::new();
1197 for subpath in path.subpaths() {
1198 let subpath_lines: Vec<_> = subpath.flatten(tr, DEFAULT_FLATNESS, false).collect();
1199 for ls in subpath_lines.windows(2) {
1201 assert!(ls[0].end().is_close_to(ls[1].start()));
1202 }
1203 reference.extend(subpath_lines);
1204 }
1205 assert_eq!(reference.len(), lines.len());
1206 for (l0, l1) in lines.iter().zip(reference.iter()) {
1207 assert!(l0.start().is_close_to(l1.start()));
1208 assert!(l0.end().is_close_to(l1.end()));
1209 }
1210 Ok(())
1211 }
1212
1213 #[test]
1214 fn test_winding() -> Result<(), SvgParserError> {
1215 let path: Path = SQUIRREL.parse()?;
1216 assert_eq!(path.winding_at((2.4, 5.4)), 0);
1217 assert_eq!(path.winding_at((3.5, 3.155)), 1);
1218 assert_eq!(path.winding_at((3.92, 3.155)), 0);
1219 assert_eq!(path.winding_at((12.46, 6.87)), 0);
1220 assert_eq!(path.winding_at((14.24, 7.455)), 1);
1221 Ok(())
1222 }
1223
1224 #[test]
1225 fn test_stroke() -> Result<(), SvgParserError> {
1226 let path: Path = "M2,2L8,2C11,2 11,8 8,8L5,4".parse()?;
1228
1229 let path_stroke = path.stroke(StrokeStyle {
1230 width: 1.0,
1231 ..Default::default()
1232 });
1233 let path_reference: Path = r#"
1234 M2,1.5 L8,1.5 C9.80902,1.5 10.75,3.38197 10.75,5 10.75,6.61803 9.80902,8.5 8,8.5 L7.75,8.5 7.6,8.3 4.6,4.3
1235 5.4,3.7 8.4,7.7 8,7.5 C9.19098,7.5 9.75,6.38197 9.75,5 9.75,3.61803 9.19098,2.5 8,2.5 L2,2.5 2,1.5 Z
1236 "#.parse()?;
1237 assert_path_eq(&path_reference, &path_stroke);
1238
1239 let path_stroke = path.stroke(StrokeStyle {
1240 width: 1.0,
1241 line_cap: LineCap::Round,
1242 line_join: LineJoin::Round,
1243 });
1244 let path_reference: Path = r#"
1245 M2,1.5 L8,1.5 C9.80902,1.5 10.75,3.38197 10.75,5 10.75,6.61803 9.80902,8.5 8,8.5 7.84274,8.5 7.69436,8.42581
1246 7.6,8.3 L4.6,4.3 C4.43542,4.08057 4.48057,3.76458 4.7,3.6 4.91943,3.43542 5.23542,3.48057 5.4,3.7 L8.4,7.7 8,7.5
1247 C9.19098,7.5 9.75,6.38197 9.75,5 9.75,3.61803 9.19098,2.5 8,2.5 L2,2.5 C1.72571,2.5 1.5,2.27429 1.5,2
1248 1.5,1.72571 1.72571,1.5 2,1.5 Z
1249 "#.parse()?;
1250 assert_path_eq(&path_reference, &path_stroke);
1251
1252 let path: Path = "M2,2L8,2C11,2 11,8 8,8L5,4Z".parse()?;
1254
1255 let path_stroke = path.stroke(StrokeStyle {
1256 width: 1.0,
1257 line_cap: LineCap::Round,
1258 line_join: LineJoin::Round,
1259 });
1260 let path_reference: Path = r#"
1261 M2,1.5 L8,1.5 C9.80902,1.5 10.75,3.38197 10.75,5 10.75,6.61803 9.80902,8.5 8,8.5 7.84274,8.5 7.69436,8.42581
1262 7.6,8.3 L4.6,4.3 4.72265,4.41603 1.72265,2.41603 C1.53984,2.29415 1.45778,2.06539 1.52145,1.85511 1.58512,1.64482
1263 1.78029,1.5 2,1.5 ZM5.4,3.7 L8.4,7.7 8,7.5 C9.19098,7.5 9.75,6.38197 9.75,5 9.75,3.61803 9.19098,2.5 8,2.5
1264 L2,2.5 2.27735,1.58397 5.27735,3.58397 C5.32451,3.61542 5.36599,3.65465 5.4,3.7 Z
1265 "#.parse()?;
1266 assert_path_eq(&path_reference, &path_stroke);
1267
1268 Ok(())
1269 }
1270
1271 #[test]
1272 fn test_reverse() -> Result<(), SvgParserError> {
1273 let mut path: Path =
1274 "M2,2L8,2C11,2 11,8 8,8L5,4ZM7,3L8,6C9,6 10,5 9,4ZM2,4Q2,8 3,8L5,8C6,8 2,3 2,4Z"
1275 .parse()?;
1276 path.reverse();
1277 let path_reference =
1278 "M2,4C2,3 6,8 5,8L3,8Q2,8 2,4ZM9,4C10,5 9,6 8,6L7,3ZM5,4L8,8C11,8 11,2 8,2L2,2Z"
1279 .parse()?;
1280 assert_path_eq(&path_reference, &path);
1281 Ok(())
1282 }
1283
1284 #[test]
1285 fn test_display() -> Result<(), SvgParserError> {
1286 let path: Path = SQUIRREL.parse()?;
1287
1288 let path_display = format!("{:#}", path);
1289 assert_path_eq(&path, &path_display.parse()?);
1290 let path_reference = "M12,1C9.79,1 8,2.31 8,3.92C8,5.86 8.5,6.95 8,10C8,5.5 5.23,3.66 4,3.66C4.05,3.16 3.52,3 3.52,3C3.52,3 3.3,3.11 3.22,3.34C2.95,3.03 2.66,3.07 2.66,3.07L2.53,3.65C2.53,3.65 0.7,4.29 0.68,6.87C0.88,7.2 2.21,7.47 3.15,7.3C4.04,7.35 3.82,8.09 3.62,8.29C2.78,9.13 2,8 1,8C0,8 0,9 1,9C2,9 2,10 4,10C0.91,11.2 4,14 4,14L3,14C2,14 2,15 2,15L8,15C11,15 13,14 13,11.53C13,10.68 12.57,9.74 12,9C10.89,7.54 12.23,6.32 13,7C13.77,7.68 16,8 16,5C16,2.79 14.21,1 12,1ZM2.5,6C2.22,6 2,5.78 2,5.5C2,5.22 2.22,5 2.5,5C2.78,5 3,5.22 3,5.5C3,5.78 2.78,6 2.5,6Z";
1291 assert_eq!(path_reference, path_display.as_str());
1292
1293 let path_relative = format!("{:#}", path.display(true, Transform::identity()));
1294 assert_path_eq(&path, &path_relative.parse()?);
1295 let path_reference = "M12,1c-2.21,0-4,1.31-4,2.92c0,1.94 0.5,3.03 0,6.08c0-4.5-2.77-6.34-4-6.34c0.05-0.5-0.48-0.66-0.48-0.66c0,0-0.22,0.11-0.3,0.34c-0.27-0.31-0.56-0.27-0.56-0.27l-0.13,0.58c0,0-1.83,0.64-1.85,3.22c0.2,0.33 1.53,0.6 2.47,0.43c0.89,0.05 0.67,0.79 0.47,0.99c-0.84,0.84-1.62-0.29-2.62-0.29c-1,0-1,1 0,1c1,0 1,1 3,1c-3.09,1.2 0,4 0,4l-1,0c-1,0-1,1-1,1l6,0c3,0 5-1 5-3.47c0-0.85-0.43-1.79-1-2.53c-1.11-1.46 0.23-2.68 1-2c0.77,0.68 3,1 3-2c0-2.21-1.79-4-4-4Zm-9.5,5c-0.28,0-0.5-0.22-0.5-0.5c0-0.28 0.22-0.5 0.5-0.5c0.28,0 0.5,0.22 0.5,0.5c0,0.28-0.22,0.5-0.5,0.5Z";
1296 assert_eq!(path_reference, path_relative.as_str());
1297
1298 Ok(())
1299 }
1300}