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