1use std::f64::consts::*;
4use std::ops::Deref;
5
6use cssparser::Parser;
7use markup5ever::{expanded_name, local_name, ns};
8
9use crate::angle::Angle;
10use crate::aspect_ratio::*;
11use crate::borrow_element_as;
12use crate::document::AcquiredNodes;
13use crate::drawing_ctx::{DrawingCtx, Viewport};
14use crate::element::{DrawResult, ElementTrait, set_attribute};
15use crate::error::*;
16use crate::float_eq_cairo::ApproxEqCairo;
17use crate::layout::{self, Shape, StackingContext};
18use crate::length::*;
19use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
20use crate::parse_identifiers;
21use crate::parsers::{Parse, ParseValue};
22use crate::path_builder::{ArcParameterization, CubicBezierCurve, Path, PathCommand, arc_segment};
23use crate::rect::Rect;
24use crate::rsvg_log;
25use crate::session::Session;
26use crate::transform::Transform;
27use crate::viewbox::*;
28use crate::xml::Attributes;
29
30#[derive(Debug, Default, Copy, Clone, PartialEq)]
32enum MarkerUnits {
33 UserSpaceOnUse,
34 #[default]
35 StrokeWidth,
36}
37
38impl Parse for MarkerUnits {
39 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerUnits, ParseError<'i>> {
40 Ok(parse_identifiers!(
41 parser,
42 "userSpaceOnUse" => MarkerUnits::UserSpaceOnUse,
43 "strokeWidth" => MarkerUnits::StrokeWidth,
44 )?)
45 }
46}
47
48#[derive(Debug, Copy, Clone, PartialEq)]
50enum MarkerOrient {
51 Auto,
52 AutoStartReverse,
53 Angle(Angle),
54}
55
56impl Default for MarkerOrient {
57 #[inline]
58 fn default() -> MarkerOrient {
59 MarkerOrient::Angle(Angle::new(0.0))
60 }
61}
62
63impl Parse for MarkerOrient {
64 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerOrient, ParseError<'i>> {
65 if parser
66 .try_parse(|p| p.expect_ident_matching("auto"))
67 .is_ok()
68 {
69 return Ok(MarkerOrient::Auto);
70 }
71
72 if parser
73 .try_parse(|p| p.expect_ident_matching("auto-start-reverse"))
74 .is_ok()
75 {
76 Ok(MarkerOrient::AutoStartReverse)
77 } else {
78 Angle::parse(parser).map(MarkerOrient::Angle)
79 }
80 }
81}
82
83pub struct Marker {
84 units: MarkerUnits,
85 ref_x: Length<Horizontal>,
86 ref_y: Length<Vertical>,
87 width: ULength<Horizontal>,
88 height: ULength<Vertical>,
89 orient: MarkerOrient,
90 aspect: AspectRatio,
91 vbox: Option<ViewBox>,
92}
93
94#[derive(Debug, Copy, Clone, PartialEq)]
96enum MarkerType {
97 Start,
98 Middle,
99 End,
100}
101
102#[derive(Debug, PartialEq)]
104struct MarkerSpec {
105 marker_type: MarkerType,
106 x: f64,
107 y: f64,
108 angle: Angle,
109}
110
111impl Default for Marker {
112 fn default() -> Marker {
113 Marker {
114 units: MarkerUnits::default(),
115 ref_x: Default::default(),
116 ref_y: Default::default(),
117 width: ULength::<Horizontal>::parse_str("3").unwrap(),
119 height: ULength::<Vertical>::parse_str("3").unwrap(),
120 orient: MarkerOrient::default(),
121 aspect: AspectRatio::default(),
122 vbox: None,
123 }
124 }
125}
126
127impl Marker {
128 fn render(
129 &self,
130 node: &Node,
131 acquired_nodes: &mut AcquiredNodes<'_>,
132 viewport: &Viewport,
133 draw_ctx: &mut DrawingCtx,
134 spec: &MarkerSpec,
135 line_width: f64,
136 marker: &layout::Marker,
137 ) -> DrawResult {
138 let mut cascaded = CascadedValues::new_from_node(node);
139 cascaded.context_fill = Some(marker.context_fill.clone());
140 cascaded.context_stroke = Some(marker.context_stroke.clone());
141
142 let values = cascaded.get();
143
144 let params = NormalizeParams::new(values, viewport);
145
146 let marker_width = self.width.to_user(¶ms);
147 let marker_height = self.height.to_user(¶ms);
148
149 if marker_width.approx_eq_cairo(0.0) || marker_height.approx_eq_cairo(0.0) {
150 return Ok(viewport.empty_bbox());
153 }
154
155 let rotation = match self.orient {
156 MarkerOrient::Auto => spec.angle,
157 MarkerOrient::AutoStartReverse => {
158 if spec.marker_type == MarkerType::Start {
159 spec.angle.flip()
160 } else {
161 spec.angle
162 }
163 }
164 MarkerOrient::Angle(a) => a,
165 };
166
167 let mut transform = Transform::new_translate(spec.x, spec.y).pre_rotate(rotation);
168
169 if self.units == MarkerUnits::StrokeWidth {
170 transform = transform.pre_scale(line_width, line_width);
171 }
172
173 let content_viewport = if let Some(vbox) = self.vbox {
174 if vbox.is_empty() {
175 return Ok(viewport.empty_bbox());
176 }
177
178 let r = self
179 .aspect
180 .compute(&vbox, &Rect::from_size(marker_width, marker_height));
181
182 let (vb_width, vb_height) = vbox.size();
183 transform = transform.pre_scale(r.width() / vb_width, r.height() / vb_height);
184
185 viewport.with_view_box(vb_width, vb_height)
186 } else {
187 viewport.with_view_box(marker_width, marker_height)
188 };
189
190 let content_params = NormalizeParams::new(values, &content_viewport);
191
192 transform = transform.pre_translate(
193 -self.ref_x.to_user(&content_params),
194 -self.ref_y.to_user(&content_params),
195 );
196
197 let clip_rect = if values.is_overflow() {
205 None
206 } else if let Some(vbox) = self.vbox {
207 Some(*vbox)
208 } else {
209 Some(Rect::from_size(marker_width, marker_height))
210 };
211
212 let elt = node.borrow_element();
213 let stacking_ctx = Box::new(StackingContext::new(
214 draw_ctx,
215 acquired_nodes,
216 &elt,
217 transform,
218 clip_rect,
219 values,
220 &content_viewport,
221 ));
222
223 draw_ctx.with_discrete_layer(
224 &stacking_ctx,
225 acquired_nodes,
226 &content_viewport,
227 None,
228 false,
229 &mut |an, dc, new_viewport| node.draw_children(an, &cascaded, new_viewport, dc), )
231 }
232}
233
234impl ElementTrait for Marker {
235 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
236 for (attr, value) in attrs.iter() {
237 match attr.expanded() {
238 expanded_name!("", "markerUnits") => {
239 set_attribute(&mut self.units, attr.parse(value), session)
240 }
241 expanded_name!("", "refX") => {
242 set_attribute(&mut self.ref_x, attr.parse(value), session)
243 }
244 expanded_name!("", "refY") => {
245 set_attribute(&mut self.ref_y, attr.parse(value), session)
246 }
247 expanded_name!("", "markerWidth") => {
248 set_attribute(&mut self.width, attr.parse(value), session)
249 }
250 expanded_name!("", "markerHeight") => {
251 set_attribute(&mut self.height, attr.parse(value), session)
252 }
253 expanded_name!("", "orient") => {
254 set_attribute(&mut self.orient, attr.parse(value), session)
255 }
256 expanded_name!("", "preserveAspectRatio") => {
257 set_attribute(&mut self.aspect, attr.parse(value), session)
258 }
259 expanded_name!("", "viewBox") => {
260 set_attribute(&mut self.vbox, attr.parse(value), session)
261 }
262 _ => (),
263 }
264 }
265 }
266}
267
268#[derive(Debug, PartialEq)]
270enum Segment {
271 Degenerate {
272 x: f64,
274 y: f64,
275 },
276
277 LineOrCurve {
278 x1: f64,
279 y1: f64,
280 x2: f64,
281 y2: f64,
282 x3: f64,
283 y3: f64,
284 x4: f64,
285 y4: f64,
286 is_subpath_start: bool,
287 is_subpath_end: bool,
288 },
289}
290
291impl Segment {
292 fn degenerate(x: f64, y: f64) -> Segment {
293 Segment::Degenerate { x, y }
294 }
295
296 fn curve(
297 x1: f64,
298 y1: f64,
299 x2: f64,
300 y2: f64,
301 x3: f64,
302 y3: f64,
303 x4: f64,
304 y4: f64,
305 is_subpath_start: bool,
306 is_subpath_end: bool,
307 ) -> Segment {
308 Segment::LineOrCurve {
309 x1,
310 y1,
311 x2,
312 y2,
313 x3,
314 y3,
315 x4,
316 y4,
317 is_subpath_start,
318 is_subpath_end,
319 }
320 }
321
322 fn line(
323 x1: f64,
324 y1: f64,
325 x2: f64,
326 y2: f64,
327 is_subpath_start: bool,
328 is_subpath_end: bool,
329 ) -> Segment {
330 Segment::curve(
331 x1,
332 y1,
333 x2,
334 y2,
335 x1,
336 y1,
337 x2,
338 y2,
339 is_subpath_start,
340 is_subpath_end,
341 )
342 }
343
344 fn get_directionalities(&self) -> Option<(f64, f64, f64, f64)> {
350 match *self {
351 Segment::Degenerate { .. } => None,
352
353 Segment::LineOrCurve {
354 x1,
355 y1,
356 x2,
357 y2,
358 x3,
359 y3,
360 x4,
361 y4,
362 ..
363 } => {
364 let coincide_1_and_2 = points_equal(x1, y1, x2, y2);
365 let coincide_1_and_3 = points_equal(x1, y1, x3, y3);
366 let coincide_1_and_4 = points_equal(x1, y1, x4, y4);
367 let coincide_2_and_3 = points_equal(x2, y2, x3, y3);
368 let coincide_2_and_4 = points_equal(x2, y2, x4, y4);
369 let coincide_3_and_4 = points_equal(x3, y3, x4, y4);
370
371 if coincide_1_and_2 && coincide_1_and_3 && coincide_1_and_4 {
372 None
373 } else if coincide_1_and_2 && coincide_1_and_3 {
374 Some((x4 - x1, y4 - y1, x4 - x3, y4 - y3))
375 } else if coincide_1_and_2 && coincide_3_and_4 {
376 Some((x4 - x1, y4 - y1, x4 - x1, y4 - y1))
377 } else if coincide_2_and_3 && coincide_2_and_4 {
378 Some((x2 - x1, y2 - y1, x4 - x1, y4 - y1))
379 } else if coincide_1_and_2 {
380 Some((x3 - x1, y3 - y1, x4 - x3, y4 - y3))
381 } else if coincide_3_and_4 {
382 Some((x2 - x1, y2 - y1, x4 - x2, y4 - y2))
383 } else {
384 Some((x2 - x1, y2 - y1, x4 - x3, y4 - y3))
385 }
386 }
387 }
388 }
389}
390
391fn points_equal(x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
392 x1.approx_eq_cairo(x2) && y1.approx_eq_cairo(y2)
393}
394
395enum SegmentState {
396 Initial,
397 NewSubpath,
398 InSubpath,
399 ClosedSubpath,
400}
401
402#[derive(Debug, PartialEq)]
403struct Segments(Vec<Segment>);
404
405impl Deref for Segments {
406 type Target = [Segment];
407
408 fn deref(&self) -> &[Segment] {
409 &self.0
410 }
411}
412
413impl From<&Path> for Segments {
432 fn from(path: &Path) -> Segments {
433 let mut last_x: f64;
434 let mut last_y: f64;
435
436 let mut cur_x: f64 = 0.0;
437 let mut cur_y: f64 = 0.0;
438 let mut subpath_start_x: f64 = 0.0;
439 let mut subpath_start_y: f64 = 0.0;
440
441 let mut segments = Vec::new();
442 let mut state = SegmentState::Initial;
443
444 let path_commands: Vec<PathCommand> = path.iter().collect();
445
446 for i in 0..path_commands.len() {
447 last_x = cur_x;
448 last_y = cur_y;
449
450 let path_command = &path_commands[i];
451
452 let is_subpath_start = matches!(state, SegmentState::NewSubpath);
453
454 let is_subpath_end = if i == path_commands.len() - 1 {
455 true
456 } else {
457 matches!(path_commands[i + 1], PathCommand::MoveTo(..))
466 };
467
468 match path_command {
469 &PathCommand::MoveTo(x, y) => {
470 cur_x = x;
471 cur_y = y;
472
473 subpath_start_x = cur_x;
474 subpath_start_y = cur_y;
475
476 match state {
477 SegmentState::Initial
478 | SegmentState::InSubpath
479 | SegmentState::ClosedSubpath => {
480 state = SegmentState::NewSubpath;
484 }
485
486 SegmentState::NewSubpath => {
487 segments.push(Segment::degenerate(last_x, last_y));
491 state = SegmentState::NewSubpath;
492 }
493 }
494 }
495
496 &PathCommand::LineTo(x, y) => {
497 cur_x = x;
498 cur_y = y;
499
500 segments.push(Segment::line(
501 last_x,
502 last_y,
503 cur_x,
504 cur_y,
505 is_subpath_start,
506 is_subpath_end,
507 ));
508
509 state = SegmentState::InSubpath;
510 }
511
512 PathCommand::CurveTo(curve) => {
513 let CubicBezierCurve {
514 pt1: (x2, y2),
515 pt2: (x3, y3),
516 to,
517 } = *curve;
518 cur_x = to.0;
519 cur_y = to.1;
520
521 segments.push(Segment::curve(
522 last_x,
523 last_y,
524 x2,
525 y2,
526 x3,
527 y3,
528 cur_x,
529 cur_y,
530 is_subpath_start,
531 is_subpath_end,
532 ));
533
534 state = SegmentState::InSubpath;
535 }
536
537 PathCommand::Arc(arc) => {
538 cur_x = arc.to.0;
539 cur_y = arc.to.1;
540
541 match arc.center_parameterization() {
542 ArcParameterization::CenterParameters {
543 center,
544 radii,
545 theta1,
546 delta_theta,
547 } => {
548 let rot = arc.x_axis_rotation;
549 let theta2 = theta1 + delta_theta;
550 let n_segs = (delta_theta / (PI * 0.5 + 0.001)).abs().ceil() as u32;
551 let d_theta = delta_theta / f64::from(n_segs);
552
553 let segment1 =
554 arc_segment(center, radii, rot, theta1, theta1 + d_theta);
555 let segment2 =
556 arc_segment(center, radii, rot, theta2 - d_theta, theta2);
557
558 let (x2, y2) = segment1.pt1;
559 let (x3, y3) = segment2.pt2;
560
561 segments.push(Segment::curve(
562 last_x,
563 last_y,
564 x2,
565 y2,
566 x3,
567 y3,
568 cur_x,
569 cur_y,
570 is_subpath_start,
571 is_subpath_end,
572 ));
573
574 state = SegmentState::InSubpath;
575 }
576 ArcParameterization::LineTo => {
577 segments.push(Segment::line(
578 last_x,
579 last_y,
580 cur_x,
581 cur_y,
582 is_subpath_start,
583 is_subpath_end,
584 ));
585
586 state = SegmentState::InSubpath;
587 }
588 ArcParameterization::Omit => {}
589 }
590 }
591
592 PathCommand::ClosePath => {
593 cur_x = subpath_start_x;
594 cur_y = subpath_start_y;
595
596 segments.push(Segment::line(
597 last_x,
598 last_y,
599 cur_x,
600 cur_y,
601 is_subpath_start,
602 is_subpath_end,
603 ));
604
605 state = SegmentState::ClosedSubpath;
606 }
607 }
608 }
609
610 if let SegmentState::NewSubpath = state {
611 segments.push(Segment::degenerate(cur_x, cur_y));
614 };
615
616 Segments(segments)
617 }
618}
619
620impl Segments {
645 fn find_incoming_angle_backwards(&self, start_index: usize) -> Option<Angle> {
646 for segment in self[..=start_index].iter().rev() {
649 match *segment {
650 Segment::Degenerate { .. } => {
651 return None; }
653
654 Segment::LineOrCurve { .. } => match segment.get_directionalities() {
655 Some((_, _, v2x, v2y)) => {
656 return Some(Angle::from_vector(v2x, v2y));
657 }
658 None => {
659 continue;
660 }
661 },
662 }
663 }
664
665 None
666 }
667
668 fn find_outgoing_angle_forwards(&self, start_index: usize) -> Option<Angle> {
669 for segment in &self[start_index..] {
672 match *segment {
673 Segment::Degenerate { .. } => {
674 return None; }
676
677 Segment::LineOrCurve { .. } => match segment.get_directionalities() {
678 Some((v1x, v1y, _, _)) => {
679 return Some(Angle::from_vector(v1x, v1y));
680 }
681 None => {
682 continue;
683 }
684 },
685 }
686 }
687
688 None
689 }
690}
691
692fn emit_marker_by_node(
693 viewport: &Viewport,
694 draw_ctx: &mut DrawingCtx,
695 acquired_nodes: &mut AcquiredNodes<'_>,
696 marker: &layout::Marker,
697 spec: &MarkerSpec,
698 line_width: f64,
699) -> DrawResult {
700 match acquired_nodes.acquire_ref(marker.node_ref.as_ref().unwrap()) {
701 Ok(acquired) => {
702 let node = acquired.get();
703
704 let marker_elt = borrow_element_as!(node, Marker);
705
706 marker_elt.render(
707 node,
708 acquired_nodes,
709 viewport,
710 draw_ctx,
711 spec,
712 line_width,
713 marker,
714 )
715 }
716
717 Err(e) => {
718 rsvg_log!(draw_ctx.session(), "could not acquire marker: {}", e);
719 Ok(viewport.empty_bbox())
720 }
721 }
722}
723
724#[derive(Debug, Copy, Clone, PartialEq)]
725enum MarkerEndpoint {
726 Start,
727 End,
728}
729
730fn emit_marker(
731 segment: &Segment,
732 endpoint: MarkerEndpoint,
733 marker_type: MarkerType,
734 orient: Angle,
735) -> MarkerSpec {
736 let (x, y) = match *segment {
737 Segment::Degenerate { x, y } => (x, y),
738
739 Segment::LineOrCurve { x1, y1, x4, y4, .. } => match endpoint {
740 MarkerEndpoint::Start => (x1, y1),
741 MarkerEndpoint::End => (x4, y4),
742 },
743 };
744
745 MarkerSpec {
746 marker_type,
747 x,
748 y,
749 angle: orient,
750 }
751}
752
753pub fn render_markers_for_shape(
754 shape: &Shape,
755 viewport: &Viewport,
756 draw_ctx: &mut DrawingCtx,
757 acquired_nodes: &mut AcquiredNodes<'_>,
758) -> DrawResult {
759 if shape.stroke.width.approx_eq_cairo(0.0) {
760 return Ok(viewport.empty_bbox());
761 }
762
763 if shape.marker_start.node_ref.is_none()
764 && shape.marker_mid.node_ref.is_none()
765 && shape.marker_end.node_ref.is_none()
766 {
767 return Ok(viewport.empty_bbox());
768 }
769
770 let specs = emit_markers_for_path(&shape.path.path);
771
772 for spec in specs {
773 let marker = match spec.marker_type {
774 MarkerType::Start => &shape.marker_start,
775 MarkerType::Middle => &shape.marker_mid,
776 MarkerType::End => &shape.marker_end,
777 };
778
779 if marker.node_ref.is_some() {
780 emit_marker_by_node(
781 viewport,
782 draw_ctx,
783 acquired_nodes,
784 marker,
785 &spec,
786 shape.stroke.width,
787 )?;
788 }
789 }
790
791 Ok(viewport.empty_bbox())
792}
793
794fn emit_markers_for_path(path: &Path) -> Vec<MarkerSpec> {
795 enum SubpathState {
796 NoSubpath,
797 InSubpath,
798 }
799
800 let mut specs = Vec::new();
801
802 let segments = Segments::from(path);
804
805 let mut subpath_state = SubpathState::NoSubpath;
806
807 for (i, segment) in segments.iter().enumerate() {
808 match *segment {
809 Segment::Degenerate { .. } => {
810 if let SubpathState::InSubpath = subpath_state {
811 assert!(i > 0);
812
813 let angle = segments
815 .find_incoming_angle_backwards(i - 1)
816 .unwrap_or_else(|| Angle::new(0.0));
817 specs.push(emit_marker(
818 &segments[i - 1],
819 MarkerEndpoint::End,
820 MarkerType::End,
821 angle,
822 ));
823 }
824
825 specs.push(emit_marker(
827 segment,
828 MarkerEndpoint::Start,
829 MarkerType::Middle,
830 Angle::new(0.0),
831 ));
832
833 subpath_state = SubpathState::NoSubpath;
834 }
835
836 Segment::LineOrCurve { is_subpath_end, .. } => {
837 match subpath_state {
839 SubpathState::NoSubpath => {
840 let angle = segments
841 .find_outgoing_angle_forwards(i)
842 .unwrap_or_else(|| Angle::new(0.0));
843 specs.push(emit_marker(
844 segment,
845 MarkerEndpoint::Start,
846 MarkerType::Start,
847 angle,
848 ));
849
850 subpath_state = SubpathState::InSubpath;
851 }
852
853 SubpathState::InSubpath => {
854 assert!(i > 0);
855
856 let incoming = segments.find_incoming_angle_backwards(i - 1);
857 let outgoing = segments.find_outgoing_angle_forwards(i);
858
859 let angle = match (incoming, outgoing) {
860 (Some(incoming), Some(outgoing)) => incoming.bisect(outgoing),
861 (Some(incoming), _) => incoming,
862 (_, Some(outgoing)) => outgoing,
863 _ => Angle::new(0.0),
864 };
865
866 specs.push(emit_marker(
867 segment,
868 MarkerEndpoint::Start,
869 MarkerType::Middle,
870 angle,
871 ));
872
873 if is_subpath_end && i + 1 < segments.len() {
874 specs.push(emit_marker(
875 segment,
876 MarkerEndpoint::End,
877 MarkerType::Middle,
878 outgoing.unwrap_or_else(|| Angle::new(0.0)),
879 ));
880 }
881 }
882 }
883 }
884 }
885 }
886
887 if !segments.is_empty() {
889 let segment = &segments[segments.len() - 1];
890 if let Segment::LineOrCurve { .. } = *segment {
891 let incoming = segments
892 .find_incoming_angle_backwards(segments.len() - 1)
893 .unwrap_or_else(|| Angle::new(0.0));
894
895 let angle = {
896 if let PathCommand::ClosePath = path.iter().nth(segments.len()).unwrap() {
897 let outgoing = segments
898 .find_outgoing_angle_forwards(0)
899 .unwrap_or_else(|| Angle::new(0.0));
900 incoming.bisect(outgoing)
901 } else {
902 incoming
903 }
904 };
905
906 specs.push(emit_marker(
907 segment,
908 MarkerEndpoint::End,
909 MarkerType::End,
910 angle,
911 ));
912 }
913 }
914
915 specs
916}
917
918#[cfg(test)]
919mod parser_tests {
920 use super::*;
921
922 #[test]
923 fn parsing_invalid_marker_units_yields_error() {
924 assert!(MarkerUnits::parse_str("").is_err());
925 assert!(MarkerUnits::parse_str("foo").is_err());
926 }
927
928 #[test]
929 fn parses_marker_units() {
930 assert_eq!(
931 MarkerUnits::parse_str("userSpaceOnUse").unwrap(),
932 MarkerUnits::UserSpaceOnUse
933 );
934 assert_eq!(
935 MarkerUnits::parse_str("strokeWidth").unwrap(),
936 MarkerUnits::StrokeWidth
937 );
938 }
939
940 #[test]
941 fn parsing_invalid_marker_orient_yields_error() {
942 assert!(MarkerOrient::parse_str("").is_err());
943 assert!(MarkerOrient::parse_str("blah").is_err());
944 assert!(MarkerOrient::parse_str("45blah").is_err());
945 }
946
947 #[test]
948 fn parses_marker_orient() {
949 assert_eq!(MarkerOrient::parse_str("auto").unwrap(), MarkerOrient::Auto);
950 assert_eq!(
951 MarkerOrient::parse_str("auto-start-reverse").unwrap(),
952 MarkerOrient::AutoStartReverse
953 );
954
955 assert_eq!(
956 MarkerOrient::parse_str("0").unwrap(),
957 MarkerOrient::Angle(Angle::new(0.0))
958 );
959 assert_eq!(
960 MarkerOrient::parse_str("180").unwrap(),
961 MarkerOrient::Angle(Angle::from_degrees(180.0))
962 );
963 assert_eq!(
964 MarkerOrient::parse_str("180deg").unwrap(),
965 MarkerOrient::Angle(Angle::from_degrees(180.0))
966 );
967 assert_eq!(
968 MarkerOrient::parse_str("-400grad").unwrap(),
969 MarkerOrient::Angle(Angle::from_degrees(-360.0))
970 );
971 assert_eq!(
972 MarkerOrient::parse_str("1rad").unwrap(),
973 MarkerOrient::Angle(Angle::new(1.0))
974 );
975 }
976}
977
978#[cfg(test)]
979mod directionality_tests {
980 use pretty_assertions::assert_eq;
981
982 use super::*;
983 use crate::path_builder::PathBuilder;
984
985 fn setup_open_path() -> Segments {
987 let mut builder = PathBuilder::default();
988
989 builder.move_to(10.0, 10.0);
990 builder.line_to(20.0, 10.0);
991 builder.line_to(20.0, 20.0);
992
993 Segments::from(&builder.into_path())
994 }
995
996 #[test]
997 fn path_to_segments_handles_open_path() {
998 let expected_segments: Segments = Segments(vec![
999 Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1000 Segment::line(20.0, 10.0, 20.0, 20.0, false, true),
1001 ]);
1002
1003 assert_eq!(setup_open_path(), expected_segments);
1004 }
1005
1006 fn setup_multiple_open_subpaths() -> Segments {
1007 let mut builder = PathBuilder::default();
1008
1009 builder.move_to(10.0, 10.0);
1010 builder.line_to(20.0, 10.0);
1011 builder.line_to(20.0, 20.0);
1012
1013 builder.move_to(30.0, 30.0);
1014 builder.line_to(40.0, 30.0);
1015 builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
1016 builder.line_to(80.0, 90.0);
1017
1018 Segments::from(&builder.into_path())
1019 }
1020
1021 #[test]
1022 fn path_to_segments_handles_multiple_open_subpaths() {
1023 let expected_segments: Segments = Segments(vec![
1024 Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1025 Segment::line(20.0, 10.0, 20.0, 20.0, false, true),
1026 Segment::line(30.0, 30.0, 40.0, 30.0, true, false),
1027 Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0, false, false),
1028 Segment::line(70.0, 70.0, 80.0, 90.0, false, true),
1029 ]);
1030
1031 assert_eq!(setup_multiple_open_subpaths(), expected_segments);
1032 }
1033
1034 fn setup_closed_subpath() -> Segments {
1036 let mut builder = PathBuilder::default();
1037
1038 builder.move_to(10.0, 10.0);
1039 builder.line_to(20.0, 10.0);
1040 builder.line_to(20.0, 20.0);
1041 builder.close_path();
1042
1043 Segments::from(&builder.into_path())
1044 }
1045
1046 #[test]
1047 fn path_to_segments_handles_closed_subpath() {
1048 let expected_segments: Segments = Segments(vec![
1049 Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1050 Segment::line(20.0, 10.0, 20.0, 20.0, false, false),
1051 Segment::line(20.0, 20.0, 10.0, 10.0, false, true),
1052 ]);
1053
1054 assert_eq!(setup_closed_subpath(), expected_segments);
1055 }
1056
1057 fn setup_multiple_closed_subpaths() -> Segments {
1060 let mut builder = PathBuilder::default();
1061
1062 builder.move_to(10.0, 10.0);
1063 builder.line_to(20.0, 10.0);
1064 builder.line_to(20.0, 20.0);
1065 builder.close_path();
1066
1067 builder.move_to(30.0, 30.0);
1068 builder.line_to(40.0, 30.0);
1069 builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
1070 builder.line_to(80.0, 90.0);
1071 builder.close_path();
1072
1073 Segments::from(&builder.into_path())
1074 }
1075
1076 #[test]
1077 fn path_to_segments_handles_multiple_closed_subpaths() {
1078 let expected_segments: Segments = Segments(vec![
1079 Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1080 Segment::line(20.0, 10.0, 20.0, 20.0, false, false),
1081 Segment::line(20.0, 20.0, 10.0, 10.0, false, true),
1082 Segment::line(30.0, 30.0, 40.0, 30.0, true, false),
1083 Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0, false, false),
1084 Segment::line(70.0, 70.0, 80.0, 90.0, false, false),
1085 Segment::line(80.0, 90.0, 30.0, 30.0, false, true),
1086 ]);
1087
1088 assert_eq!(setup_multiple_closed_subpaths(), expected_segments);
1089 }
1090
1091 fn setup_no_moveto_after_closepath() -> Segments {
1094 let mut builder = PathBuilder::default();
1095
1096 builder.move_to(10.0, 10.0);
1097 builder.line_to(20.0, 10.0);
1098 builder.line_to(20.0, 20.0);
1099 builder.close_path();
1100
1101 builder.line_to(40.0, 30.0);
1102
1103 Segments::from(&builder.into_path())
1104 }
1105
1106 #[test]
1107 fn path_to_segments_handles_no_moveto_after_closepath() {
1108 let expected_segments: Segments = Segments(vec![
1109 Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1110 Segment::line(20.0, 10.0, 20.0, 20.0, false, false),
1111 Segment::line(20.0, 20.0, 10.0, 10.0, false, false),
1112 Segment::line(10.0, 10.0, 40.0, 30.0, false, true),
1113 ]);
1114
1115 assert_eq!(setup_no_moveto_after_closepath(), expected_segments);
1116 }
1117
1118 #[test]
1153 fn degenerate_segment_has_no_directionality() {
1154 let s = Segment::degenerate(1.0, 2.0);
1155 assert!(s.get_directionalities().is_none());
1156 }
1157
1158 #[test]
1159 fn line_segment_has_directionality() {
1160 let s = Segment::line(1.0, 2.0, 3.0, 4.0, false, false);
1161 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1162 assert_eq!((2.0, 2.0), (v1x, v1y));
1163 assert_eq!((2.0, 2.0), (v2x, v2y));
1164 }
1165
1166 #[test]
1167 fn line_segment_with_coincident_ends_has_no_directionality() {
1168 let s = Segment::line(1.0, 2.0, 1.0, 2.0, false, false);
1169 assert!(s.get_directionalities().is_none());
1170 }
1171
1172 #[test]
1173 fn curve_has_directionality() {
1174 let s = Segment::curve(1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 20.0, 33.0, false, false);
1175 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1176 assert_eq!((2.0, 3.0), (v1x, v1y));
1177 assert_eq!((12.0, 20.0), (v2x, v2y));
1178 }
1179
1180 #[test]
1181 fn curves_with_loops_and_coincident_ends_have_directionality() {
1182 let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 1.0, 2.0, false, false);
1183 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1184 assert_eq!((2.0, 2.0), (v1x, v1y));
1185 assert_eq!((-4.0, -4.0), (v2x, v2y));
1186
1187 let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, false, false);
1188 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1189 assert_eq!((2.0, 2.0), (v1x, v1y));
1190 assert_eq!((-2.0, -2.0), (v2x, v2y));
1191
1192 let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 1.0, 2.0, false, false);
1193 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1194 assert_eq!((2.0, 2.0), (v1x, v1y));
1195 assert_eq!((-2.0, -2.0), (v2x, v2y));
1196 }
1197
1198 #[test]
1199 fn curve_with_coincident_control_points_has_no_directionality() {
1200 let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0, false, false);
1201 assert!(s.get_directionalities().is_none());
1202 }
1203
1204 #[test]
1205 fn curve_with_123_coincident_has_directionality() {
1206 let s = Segment::curve(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 20.0, 40.0, false, false);
1207 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1208 assert_eq!((20.0, 40.0), (v1x, v1y));
1209 assert_eq!((20.0, 40.0), (v2x, v2y));
1210 }
1211
1212 #[test]
1213 fn curve_with_234_coincident_has_directionality() {
1214 let s = Segment::curve(20.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false, false);
1215 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1216 assert_eq!((-20.0, -40.0), (v1x, v1y));
1217 assert_eq!((-20.0, -40.0), (v2x, v2y));
1218 }
1219
1220 #[test]
1221 fn curve_with_12_34_coincident_has_directionality() {
1222 let s = Segment::curve(20.0, 40.0, 20.0, 40.0, 60.0, 70.0, 60.0, 70.0, false, false);
1223 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1224 assert_eq!((40.0, 30.0), (v1x, v1y));
1225 assert_eq!((40.0, 30.0), (v2x, v2y));
1226 }
1227}
1228
1229#[cfg(test)]
1230mod marker_tests {
1231 use pretty_assertions::assert_eq;
1232
1233 use super::*;
1234 use crate::path_builder::PathBuilder;
1235
1236 #[test]
1237 fn emits_for_open_subpath() {
1238 let mut builder = PathBuilder::default();
1239 builder.move_to(0.0, 0.0);
1240 builder.line_to(1.0, 0.0);
1241 builder.line_to(1.0, 1.0);
1242 builder.line_to(0.0, 1.0);
1243
1244 let v = emit_markers_for_path(&builder.into_path());
1245
1246 assert_eq!(
1247 v,
1248 vec![
1249 MarkerSpec {
1250 marker_type: MarkerType::Start,
1251 x: 0.0,
1252 y: 0.0,
1253 angle: Angle::new(0.0)
1254 },
1255 MarkerSpec {
1256 marker_type: MarkerType::Middle,
1257 x: 1.0,
1258 y: 0.0,
1259 angle: Angle::from_vector(1.0, 1.0)
1260 },
1261 MarkerSpec {
1262 marker_type: MarkerType::Middle,
1263 x: 1.0,
1264 y: 1.0,
1265 angle: Angle::from_vector(-1.0, 1.0)
1266 },
1267 MarkerSpec {
1268 marker_type: MarkerType::End,
1269 x: 0.0,
1270 y: 1.0,
1271 angle: Angle::from_vector(-1.0, 0.0)
1272 },
1273 ]
1274 );
1275 }
1276
1277 #[test]
1278 fn emits_for_closed_subpath() {
1279 let mut builder = PathBuilder::default();
1280 builder.move_to(0.0, 0.0);
1281 builder.line_to(1.0, 0.0);
1282 builder.line_to(1.0, 1.0);
1283 builder.line_to(0.0, 1.0);
1284 builder.close_path();
1285
1286 let v = emit_markers_for_path(&builder.into_path());
1287
1288 assert_eq!(
1289 v,
1290 vec![
1291 MarkerSpec {
1292 marker_type: MarkerType::Start,
1293 x: 0.0,
1294 y: 0.0,
1295 angle: Angle::new(0.0)
1296 },
1297 MarkerSpec {
1298 marker_type: MarkerType::Middle,
1299 x: 1.0,
1300 y: 0.0,
1301 angle: Angle::from_vector(1.0, 1.0)
1302 },
1303 MarkerSpec {
1304 marker_type: MarkerType::Middle,
1305 x: 1.0,
1306 y: 1.0,
1307 angle: Angle::from_vector(-1.0, 1.0)
1308 },
1309 MarkerSpec {
1310 marker_type: MarkerType::Middle,
1311 x: 0.0,
1312 y: 1.0,
1313 angle: Angle::from_vector(-1.0, -1.0)
1314 },
1315 MarkerSpec {
1316 marker_type: MarkerType::End,
1317 x: 0.0,
1318 y: 0.0,
1319 angle: Angle::from_vector(1.0, -1.0)
1320 },
1321 ]
1322 );
1323 }
1324
1325 #[test]
1326 fn bug_1218_missing_end_markers_in_subpaths() {
1327 let mut builder = PathBuilder::default();
1328 builder
1329 .parse("M 0,0 h 10 h 10 M 0,6 h 10 h 10 M 0,12 h 10 h 10")
1330 .unwrap();
1331 let path = builder.into_path();
1332
1333 let specs = emit_markers_for_path(&path);
1334
1335 assert_eq!(
1336 specs,
1337 vec![
1338 MarkerSpec {
1339 marker_type: MarkerType::Start,
1340 x: 0.0,
1341 y: 0.0,
1342 angle: Angle::new(0.0)
1343 },
1344 MarkerSpec {
1345 marker_type: MarkerType::Middle,
1346 x: 10.0,
1347 y: 0.0,
1348 angle: Angle::new(0.0)
1349 },
1350 MarkerSpec {
1351 marker_type: MarkerType::Middle,
1352 x: 20.0,
1353 y: 0.0,
1354 angle: Angle::new(0.0)
1355 },
1356 MarkerSpec {
1357 marker_type: MarkerType::Middle,
1358 x: 0.0,
1359 y: 6.0,
1360 angle: Angle::new(0.0)
1361 },
1362 MarkerSpec {
1363 marker_type: MarkerType::Middle,
1364 x: 10.0,
1365 y: 6.0,
1366 angle: Angle::new(0.0)
1367 },
1368 MarkerSpec {
1369 marker_type: MarkerType::Middle,
1370 x: 20.0,
1371 y: 6.0,
1372 angle: Angle::new(0.0)
1373 },
1374 MarkerSpec {
1375 marker_type: MarkerType::Middle,
1376 x: 0.0,
1377 y: 12.0,
1378 angle: Angle::new(0.0)
1379 },
1380 MarkerSpec {
1381 marker_type: MarkerType::Middle,
1382 x: 10.0,
1383 y: 12.0,
1384 angle: Angle::new(0.0)
1385 },
1386 MarkerSpec {
1387 marker_type: MarkerType::End,
1388 x: 20.0,
1389 y: 12.0,
1390 angle: Angle::new(0.0)
1391 }
1392 ]
1393 );
1394 }
1395
1396 #[test]
1397 fn does_not_duplicate_end_marker() {
1398 let mut builder = PathBuilder::default();
1399 builder.parse("M 0,0 h 10 v 10 h -10 Z").unwrap();
1400 let path = builder.into_path();
1401
1402 let specs = emit_markers_for_path(&path);
1403
1404 assert_eq!(
1405 specs,
1406 vec![
1407 MarkerSpec {
1408 marker_type: MarkerType::Start,
1409 x: 0.0,
1410 y: 0.0,
1411 angle: Angle::new(0.0)
1412 },
1413 MarkerSpec {
1414 marker_type: MarkerType::Middle,
1415 x: 10.0,
1416 y: 0.0,
1417 angle: Angle::from_degrees(45.0)
1418 },
1419 MarkerSpec {
1420 marker_type: MarkerType::Middle,
1421 x: 10.0,
1422 y: 10.0,
1423 angle: Angle::from_degrees(135.0)
1424 },
1425 MarkerSpec {
1426 marker_type: MarkerType::Middle,
1427 x: 0.0,
1428 y: 10.0,
1429 angle: Angle::from_degrees(225.0)
1430 },
1431 MarkerSpec {
1432 marker_type: MarkerType::End,
1433 x: 0.0,
1434 y: 0.0,
1435 angle: Angle::from_degrees(315.0)
1436 },
1437 ]
1438 );
1439 }
1440}