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::bbox::BoundingBox;
12use crate::borrow_element_as;
13use crate::document::AcquiredNodes;
14use crate::drawing_ctx::{DrawingCtx, Viewport};
15use crate::element::{DrawResult, ElementTrait, set_attribute};
16use crate::error::*;
17use crate::float_eq_cairo::ApproxEqCairo;
18use crate::layout::{self, Shape, StackingContext};
19use crate::length::*;
20use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
21use crate::parse_identifiers;
22use crate::parsers::{Parse, ParseValue};
23use crate::path_builder::{ArcParameterization, CubicBezierCurve, Path, PathCommand, arc_segment};
24use crate::rect::Rect;
25use crate::rsvg_log;
26use crate::session::Session;
27use crate::transform::Transform;
28use crate::viewbox::*;
29use crate::xml::Attributes;
30
31#[derive(Debug, Default, Copy, Clone, PartialEq)]
33enum MarkerUnits {
34 UserSpaceOnUse,
35 #[default]
36 StrokeWidth,
37}
38
39impl Parse for MarkerUnits {
40 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerUnits, ParseError<'i>> {
41 Ok(parse_identifiers!(
42 parser,
43 "userSpaceOnUse" => MarkerUnits::UserSpaceOnUse,
44 "strokeWidth" => MarkerUnits::StrokeWidth,
45 )?)
46 }
47}
48
49#[derive(Debug, Copy, Clone, PartialEq)]
51enum MarkerOrient {
52 Auto,
53 AutoStartReverse,
54 Angle(Angle),
55}
56
57impl Default for MarkerOrient {
58 #[inline]
59 fn default() -> MarkerOrient {
60 MarkerOrient::Angle(Angle::new(0.0))
61 }
62}
63
64impl Parse for MarkerOrient {
65 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerOrient, ParseError<'i>> {
66 if parser
67 .try_parse(|p| p.expect_ident_matching("auto"))
68 .is_ok()
69 {
70 return Ok(MarkerOrient::Auto);
71 }
72
73 if parser
74 .try_parse(|p| p.expect_ident_matching("auto-start-reverse"))
75 .is_ok()
76 {
77 Ok(MarkerOrient::AutoStartReverse)
78 } else {
79 Angle::parse(parser).map(MarkerOrient::Angle)
80 }
81 }
82}
83
84pub struct Marker {
85 units: MarkerUnits,
86 ref_x: Length<Horizontal>,
87 ref_y: Length<Vertical>,
88 width: ULength<Horizontal>,
89 height: ULength<Vertical>,
90 orient: MarkerOrient,
91 aspect: AspectRatio,
92 vbox: Option<ViewBox>,
93}
94
95impl Default for Marker {
96 fn default() -> Marker {
97 Marker {
98 units: MarkerUnits::default(),
99 ref_x: Default::default(),
100 ref_y: Default::default(),
101 width: ULength::<Horizontal>::parse_str("3").unwrap(),
103 height: ULength::<Vertical>::parse_str("3").unwrap(),
104 orient: MarkerOrient::default(),
105 aspect: AspectRatio::default(),
106 vbox: None,
107 }
108 }
109}
110
111impl Marker {
112 fn render(
113 &self,
114 node: &Node,
115 acquired_nodes: &mut AcquiredNodes<'_>,
116 viewport: &Viewport,
117 draw_ctx: &mut DrawingCtx,
118 xpos: f64,
119 ypos: f64,
120 computed_angle: Angle,
121 line_width: f64,
122 clipping: bool,
123 marker_type: MarkerType,
124 marker: &layout::Marker,
125 ) -> DrawResult {
126 let mut cascaded = CascadedValues::new_from_node(node);
127 cascaded.context_fill = Some(marker.context_fill.clone());
128 cascaded.context_stroke = Some(marker.context_stroke.clone());
129
130 let values = cascaded.get();
131
132 let params = NormalizeParams::new(values, viewport);
133
134 let marker_width = self.width.to_user(¶ms);
135 let marker_height = self.height.to_user(¶ms);
136
137 if marker_width.approx_eq_cairo(0.0) || marker_height.approx_eq_cairo(0.0) {
138 return Ok(viewport.empty_bbox());
141 }
142
143 let rotation = match self.orient {
144 MarkerOrient::Auto => computed_angle,
145 MarkerOrient::AutoStartReverse => {
146 if marker_type == MarkerType::Start {
147 computed_angle.flip()
148 } else {
149 computed_angle
150 }
151 }
152 MarkerOrient::Angle(a) => a,
153 };
154
155 let mut transform = Transform::new_translate(xpos, ypos).pre_rotate(rotation);
156
157 if self.units == MarkerUnits::StrokeWidth {
158 transform = transform.pre_scale(line_width, line_width);
159 }
160
161 let content_viewport = if let Some(vbox) = self.vbox {
162 if vbox.is_empty() {
163 return Ok(viewport.empty_bbox());
164 }
165
166 let r = self
167 .aspect
168 .compute(&vbox, &Rect::from_size(marker_width, marker_height));
169
170 let (vb_width, vb_height) = vbox.size();
171 transform = transform.pre_scale(r.width() / vb_width, r.height() / vb_height);
172
173 viewport.with_view_box(vb_width, vb_height)
174 } else {
175 viewport.with_view_box(marker_width, marker_height)
176 };
177
178 let content_params = NormalizeParams::new(values, &content_viewport);
179
180 transform = transform.pre_translate(
181 -self.ref_x.to_user(&content_params),
182 -self.ref_y.to_user(&content_params),
183 );
184
185 let clip_rect = if values.is_overflow() {
193 None
194 } else if let Some(vbox) = self.vbox {
195 Some(*vbox)
196 } else {
197 Some(Rect::from_size(marker_width, marker_height))
198 };
199
200 let elt = node.borrow_element();
201 let stacking_ctx = Box::new(StackingContext::new(
202 draw_ctx.session(),
203 acquired_nodes,
204 &elt,
205 transform,
206 clip_rect,
207 values,
208 ));
209
210 draw_ctx.with_discrete_layer(
211 &stacking_ctx,
212 acquired_nodes,
213 &content_viewport,
214 None,
215 clipping,
216 &mut |an, dc, new_viewport| {
217 node.draw_children(an, &cascaded, new_viewport, dc, clipping)
218 }, )
220 }
221}
222
223impl ElementTrait for Marker {
224 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
225 for (attr, value) in attrs.iter() {
226 match attr.expanded() {
227 expanded_name!("", "markerUnits") => {
228 set_attribute(&mut self.units, attr.parse(value), session)
229 }
230 expanded_name!("", "refX") => {
231 set_attribute(&mut self.ref_x, attr.parse(value), session)
232 }
233 expanded_name!("", "refY") => {
234 set_attribute(&mut self.ref_y, attr.parse(value), session)
235 }
236 expanded_name!("", "markerWidth") => {
237 set_attribute(&mut self.width, attr.parse(value), session)
238 }
239 expanded_name!("", "markerHeight") => {
240 set_attribute(&mut self.height, attr.parse(value), session)
241 }
242 expanded_name!("", "orient") => {
243 set_attribute(&mut self.orient, attr.parse(value), session)
244 }
245 expanded_name!("", "preserveAspectRatio") => {
246 set_attribute(&mut self.aspect, attr.parse(value), session)
247 }
248 expanded_name!("", "viewBox") => {
249 set_attribute(&mut self.vbox, attr.parse(value), session)
250 }
251 _ => (),
252 }
253 }
254 }
255}
256
257#[derive(Debug, PartialEq)]
259enum Segment {
260 Degenerate {
261 x: f64,
263 y: f64,
264 },
265
266 LineOrCurve {
267 x1: f64,
268 y1: f64,
269 x2: f64,
270 y2: f64,
271 x3: f64,
272 y3: f64,
273 x4: f64,
274 y4: f64,
275 },
276}
277
278impl Segment {
279 fn degenerate(x: f64, y: f64) -> Segment {
280 Segment::Degenerate { x, y }
281 }
282
283 fn curve(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Segment {
284 Segment::LineOrCurve {
285 x1,
286 y1,
287 x2,
288 y2,
289 x3,
290 y3,
291 x4,
292 y4,
293 }
294 }
295
296 fn line(x1: f64, y1: f64, x2: f64, y2: f64) -> Segment {
297 Segment::curve(x1, y1, x2, y2, x1, y1, x2, y2)
298 }
299
300 fn get_directionalities(&self) -> Option<(f64, f64, f64, f64)> {
306 match *self {
307 Segment::Degenerate { .. } => None,
308
309 Segment::LineOrCurve {
310 x1,
311 y1,
312 x2,
313 y2,
314 x3,
315 y3,
316 x4,
317 y4,
318 } => {
319 let coincide_1_and_2 = points_equal(x1, y1, x2, y2);
320 let coincide_1_and_3 = points_equal(x1, y1, x3, y3);
321 let coincide_1_and_4 = points_equal(x1, y1, x4, y4);
322 let coincide_2_and_3 = points_equal(x2, y2, x3, y3);
323 let coincide_2_and_4 = points_equal(x2, y2, x4, y4);
324 let coincide_3_and_4 = points_equal(x3, y3, x4, y4);
325
326 if coincide_1_and_2 && coincide_1_and_3 && coincide_1_and_4 {
327 None
328 } else if coincide_1_and_2 && coincide_1_and_3 {
329 Some((x4 - x1, y4 - y1, x4 - x3, y4 - y3))
330 } else if coincide_1_and_2 && coincide_3_and_4 {
331 Some((x4 - x1, y4 - y1, x4 - x1, y4 - y1))
332 } else if coincide_2_and_3 && coincide_2_and_4 {
333 Some((x2 - x1, y2 - y1, x4 - x1, y4 - y1))
334 } else if coincide_1_and_2 {
335 Some((x3 - x1, y3 - y1, x4 - x3, y4 - y3))
336 } else if coincide_3_and_4 {
337 Some((x2 - x1, y2 - y1, x4 - x2, y4 - y2))
338 } else {
339 Some((x2 - x1, y2 - y1, x4 - x3, y4 - y3))
340 }
341 }
342 }
343 }
344}
345
346fn points_equal(x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
347 x1.approx_eq_cairo(x2) && y1.approx_eq_cairo(y2)
348}
349
350enum SegmentState {
351 Initial,
352 NewSubpath,
353 InSubpath,
354 ClosedSubpath,
355}
356
357#[derive(Debug, PartialEq)]
358struct Segments(Vec<Segment>);
359
360impl Deref for Segments {
361 type Target = [Segment];
362
363 fn deref(&self) -> &[Segment] {
364 &self.0
365 }
366}
367
368impl From<&Path> for Segments {
387 fn from(path: &Path) -> Segments {
388 let mut last_x: f64;
389 let mut last_y: f64;
390
391 let mut cur_x: f64 = 0.0;
392 let mut cur_y: f64 = 0.0;
393 let mut subpath_start_x: f64 = 0.0;
394 let mut subpath_start_y: f64 = 0.0;
395
396 let mut segments = Vec::new();
397 let mut state = SegmentState::Initial;
398
399 for path_command in path.iter() {
400 last_x = cur_x;
401 last_y = cur_y;
402
403 match path_command {
404 PathCommand::MoveTo(x, y) => {
405 cur_x = x;
406 cur_y = y;
407
408 subpath_start_x = cur_x;
409 subpath_start_y = cur_y;
410
411 match state {
412 SegmentState::Initial | SegmentState::InSubpath => {
413 state = SegmentState::NewSubpath;
417 }
418
419 SegmentState::NewSubpath => {
420 segments.push(Segment::degenerate(last_x, last_y));
424 state = SegmentState::NewSubpath;
425 }
426
427 SegmentState::ClosedSubpath => {
428 state = SegmentState::Initial;
437 }
438 }
439 }
440
441 PathCommand::LineTo(x, y) => {
442 cur_x = x;
443 cur_y = y;
444
445 segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
446
447 state = SegmentState::InSubpath;
448 }
449
450 PathCommand::CurveTo(curve) => {
451 let CubicBezierCurve {
452 pt1: (x2, y2),
453 pt2: (x3, y3),
454 to,
455 } = curve;
456 cur_x = to.0;
457 cur_y = to.1;
458
459 segments.push(Segment::curve(last_x, last_y, x2, y2, x3, y3, cur_x, cur_y));
460
461 state = SegmentState::InSubpath;
462 }
463
464 PathCommand::Arc(arc) => {
465 cur_x = arc.to.0;
466 cur_y = arc.to.1;
467
468 match arc.center_parameterization() {
469 ArcParameterization::CenterParameters {
470 center,
471 radii,
472 theta1,
473 delta_theta,
474 } => {
475 let rot = arc.x_axis_rotation;
476 let theta2 = theta1 + delta_theta;
477 let n_segs = (delta_theta / (PI * 0.5 + 0.001)).abs().ceil() as u32;
478 let d_theta = delta_theta / f64::from(n_segs);
479
480 let segment1 =
481 arc_segment(center, radii, rot, theta1, theta1 + d_theta);
482 let segment2 =
483 arc_segment(center, radii, rot, theta2 - d_theta, theta2);
484
485 let (x2, y2) = segment1.pt1;
486 let (x3, y3) = segment2.pt2;
487 segments
488 .push(Segment::curve(last_x, last_y, x2, y2, x3, y3, cur_x, cur_y));
489
490 state = SegmentState::InSubpath;
491 }
492 ArcParameterization::LineTo => {
493 segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
494
495 state = SegmentState::InSubpath;
496 }
497 ArcParameterization::Omit => {}
498 }
499 }
500
501 PathCommand::ClosePath => {
502 cur_x = subpath_start_x;
503 cur_y = subpath_start_y;
504
505 segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
506
507 state = SegmentState::ClosedSubpath;
508 }
509 }
510 }
511
512 if let SegmentState::NewSubpath = state {
513 segments.push(Segment::degenerate(cur_x, cur_y));
516 };
517
518 Segments(segments)
519 }
520}
521
522impl Segments {
547 fn find_incoming_angle_backwards(&self, start_index: usize) -> Option<Angle> {
548 for segment in self[..=start_index].iter().rev() {
551 match *segment {
552 Segment::Degenerate { .. } => {
553 return None; }
555
556 Segment::LineOrCurve { .. } => match segment.get_directionalities() {
557 Some((_, _, v2x, v2y)) => {
558 return Some(Angle::from_vector(v2x, v2y));
559 }
560 None => {
561 continue;
562 }
563 },
564 }
565 }
566
567 None
568 }
569
570 fn find_outgoing_angle_forwards(&self, start_index: usize) -> Option<Angle> {
571 for segment in &self[start_index..] {
574 match *segment {
575 Segment::Degenerate { .. } => {
576 return None; }
578
579 Segment::LineOrCurve { .. } => match segment.get_directionalities() {
580 Some((v1x, v1y, _, _)) => {
581 return Some(Angle::from_vector(v1x, v1y));
582 }
583 None => {
584 continue;
585 }
586 },
587 }
588 }
589
590 None
591 }
592}
593
594#[derive(Debug, Copy, Clone, PartialEq)]
596enum MarkerType {
597 Start,
598 Middle,
599 End,
600}
601
602fn emit_marker_by_node(
603 viewport: &Viewport,
604 draw_ctx: &mut DrawingCtx,
605 acquired_nodes: &mut AcquiredNodes<'_>,
606 marker: &layout::Marker,
607 xpos: f64,
608 ypos: f64,
609 computed_angle: Angle,
610 line_width: f64,
611 clipping: bool,
612 marker_type: MarkerType,
613) -> DrawResult {
614 match acquired_nodes.acquire_ref(marker.node_ref.as_ref().unwrap()) {
615 Ok(acquired) => {
616 let node = acquired.get();
617
618 let marker_elt = borrow_element_as!(node, Marker);
619
620 marker_elt.render(
621 node,
622 acquired_nodes,
623 viewport,
624 draw_ctx,
625 xpos,
626 ypos,
627 computed_angle,
628 line_width,
629 clipping,
630 marker_type,
631 marker,
632 )
633 }
634
635 Err(e) => {
636 rsvg_log!(draw_ctx.session(), "could not acquire marker: {}", e);
637 Ok(viewport.empty_bbox())
638 }
639 }
640}
641
642#[derive(Debug, Copy, Clone, PartialEq)]
643enum MarkerEndpoint {
644 Start,
645 End,
646}
647
648fn emit_marker<E>(
649 segment: &Segment,
650 endpoint: MarkerEndpoint,
651 marker_type: MarkerType,
652 orient: Angle,
653 emit_fn: &mut E,
654) -> DrawResult
655where
656 E: FnMut(MarkerType, f64, f64, Angle) -> DrawResult,
657{
658 let (x, y) = match *segment {
659 Segment::Degenerate { x, y } => (x, y),
660
661 Segment::LineOrCurve { x1, y1, x4, y4, .. } => match endpoint {
662 MarkerEndpoint::Start => (x1, y1),
663 MarkerEndpoint::End => (x4, y4),
664 },
665 };
666
667 emit_fn(marker_type, x, y, orient)
668}
669
670pub fn render_markers_for_shape(
671 shape: &Shape,
672 viewport: &Viewport,
673 draw_ctx: &mut DrawingCtx,
674 acquired_nodes: &mut AcquiredNodes<'_>,
675 clipping: bool,
676) -> DrawResult {
677 if shape.stroke.width.approx_eq_cairo(0.0) {
678 return Ok(viewport.empty_bbox());
679 }
680
681 if shape.marker_start.node_ref.is_none()
682 && shape.marker_mid.node_ref.is_none()
683 && shape.marker_end.node_ref.is_none()
684 {
685 return Ok(viewport.empty_bbox());
686 }
687
688 emit_markers_for_path(
689 &shape.path.path,
690 viewport.empty_bbox(),
691 &mut |marker_type: MarkerType, x: f64, y: f64, computed_angle: Angle| {
692 let marker = match marker_type {
693 MarkerType::Start => &shape.marker_start,
694 MarkerType::Middle => &shape.marker_mid,
695 MarkerType::End => &shape.marker_end,
696 };
697
698 if marker.node_ref.is_some() {
699 emit_marker_by_node(
700 viewport,
701 draw_ctx,
702 acquired_nodes,
703 marker,
704 x,
705 y,
706 computed_angle,
707 shape.stroke.width,
708 clipping,
709 marker_type,
710 )
711 } else {
712 Ok(viewport.empty_bbox())
713 }
714 },
715 )
716}
717
718fn emit_markers_for_path<E>(
719 path: &Path,
720 empty_bbox: Box<BoundingBox>,
721 emit_fn: &mut E,
722) -> DrawResult
723where
724 E: FnMut(MarkerType, f64, f64, Angle) -> DrawResult,
725{
726 enum SubpathState {
727 NoSubpath,
728 InSubpath,
729 }
730
731 let mut bbox = empty_bbox;
732
733 let segments = Segments::from(path);
735
736 let mut subpath_state = SubpathState::NoSubpath;
737
738 for (i, segment) in segments.iter().enumerate() {
739 match *segment {
740 Segment::Degenerate { .. } => {
741 if let SubpathState::InSubpath = subpath_state {
742 assert!(i > 0);
743
744 let angle = segments
746 .find_incoming_angle_backwards(i - 1)
747 .unwrap_or_else(|| Angle::new(0.0));
748 let marker_bbox = emit_marker(
749 &segments[i - 1],
750 MarkerEndpoint::End,
751 MarkerType::End,
752 angle,
753 emit_fn,
754 )?;
755 bbox.insert(&marker_bbox);
756 }
757
758 let marker_bbox = emit_marker(
760 segment,
761 MarkerEndpoint::Start,
762 MarkerType::Middle,
763 Angle::new(0.0),
764 emit_fn,
765 )?;
766 bbox.insert(&marker_bbox);
767
768 subpath_state = SubpathState::NoSubpath;
769 }
770
771 Segment::LineOrCurve { .. } => {
772 match subpath_state {
774 SubpathState::NoSubpath => {
775 let angle = segments
776 .find_outgoing_angle_forwards(i)
777 .unwrap_or_else(|| Angle::new(0.0));
778 let marker_bbox = emit_marker(
779 segment,
780 MarkerEndpoint::Start,
781 MarkerType::Start,
782 angle,
783 emit_fn,
784 )?;
785 bbox.insert(&marker_bbox);
786
787 subpath_state = SubpathState::InSubpath;
788 }
789
790 SubpathState::InSubpath => {
791 assert!(i > 0);
792
793 let incoming = segments.find_incoming_angle_backwards(i - 1);
794 let outgoing = segments.find_outgoing_angle_forwards(i);
795
796 let angle = match (incoming, outgoing) {
797 (Some(incoming), Some(outgoing)) => incoming.bisect(outgoing),
798 (Some(incoming), _) => incoming,
799 (_, Some(outgoing)) => outgoing,
800 _ => Angle::new(0.0),
801 };
802
803 let marker_bbox = emit_marker(
804 segment,
805 MarkerEndpoint::Start,
806 MarkerType::Middle,
807 angle,
808 emit_fn,
809 )?;
810 bbox.insert(&marker_bbox);
811 }
812 }
813 }
814 }
815 }
816
817 if !segments.is_empty() {
819 let segment = &segments[segments.len() - 1];
820 if let Segment::LineOrCurve { .. } = *segment {
821 let incoming = segments
822 .find_incoming_angle_backwards(segments.len() - 1)
823 .unwrap_or_else(|| Angle::new(0.0));
824
825 let angle = {
826 if let PathCommand::ClosePath = path.iter().nth(segments.len()).unwrap() {
827 let outgoing = segments
828 .find_outgoing_angle_forwards(0)
829 .unwrap_or_else(|| Angle::new(0.0));
830 incoming.bisect(outgoing)
831 } else {
832 incoming
833 }
834 };
835
836 let marker_bbox = emit_marker(
837 segment,
838 MarkerEndpoint::End,
839 MarkerType::End,
840 angle,
841 emit_fn,
842 )?;
843 bbox.insert(&marker_bbox);
844 }
845 }
846
847 Ok(bbox)
848}
849
850#[cfg(test)]
851mod parser_tests {
852 use super::*;
853
854 #[test]
855 fn parsing_invalid_marker_units_yields_error() {
856 assert!(MarkerUnits::parse_str("").is_err());
857 assert!(MarkerUnits::parse_str("foo").is_err());
858 }
859
860 #[test]
861 fn parses_marker_units() {
862 assert_eq!(
863 MarkerUnits::parse_str("userSpaceOnUse").unwrap(),
864 MarkerUnits::UserSpaceOnUse
865 );
866 assert_eq!(
867 MarkerUnits::parse_str("strokeWidth").unwrap(),
868 MarkerUnits::StrokeWidth
869 );
870 }
871
872 #[test]
873 fn parsing_invalid_marker_orient_yields_error() {
874 assert!(MarkerOrient::parse_str("").is_err());
875 assert!(MarkerOrient::parse_str("blah").is_err());
876 assert!(MarkerOrient::parse_str("45blah").is_err());
877 }
878
879 #[test]
880 fn parses_marker_orient() {
881 assert_eq!(MarkerOrient::parse_str("auto").unwrap(), MarkerOrient::Auto);
882 assert_eq!(
883 MarkerOrient::parse_str("auto-start-reverse").unwrap(),
884 MarkerOrient::AutoStartReverse
885 );
886
887 assert_eq!(
888 MarkerOrient::parse_str("0").unwrap(),
889 MarkerOrient::Angle(Angle::new(0.0))
890 );
891 assert_eq!(
892 MarkerOrient::parse_str("180").unwrap(),
893 MarkerOrient::Angle(Angle::from_degrees(180.0))
894 );
895 assert_eq!(
896 MarkerOrient::parse_str("180deg").unwrap(),
897 MarkerOrient::Angle(Angle::from_degrees(180.0))
898 );
899 assert_eq!(
900 MarkerOrient::parse_str("-400grad").unwrap(),
901 MarkerOrient::Angle(Angle::from_degrees(-360.0))
902 );
903 assert_eq!(
904 MarkerOrient::parse_str("1rad").unwrap(),
905 MarkerOrient::Angle(Angle::new(1.0))
906 );
907 }
908}
909
910#[cfg(test)]
911mod directionality_tests {
912 use super::*;
913 use crate::path_builder::PathBuilder;
914
915 fn setup_open_path() -> Segments {
917 let mut builder = PathBuilder::default();
918
919 builder.move_to(10.0, 10.0);
920 builder.line_to(20.0, 10.0);
921 builder.line_to(20.0, 20.0);
922
923 Segments::from(&builder.into_path())
924 }
925
926 #[test]
927 fn path_to_segments_handles_open_path() {
928 let expected_segments: Segments = Segments(vec![
929 Segment::line(10.0, 10.0, 20.0, 10.0),
930 Segment::line(20.0, 10.0, 20.0, 20.0),
931 ]);
932
933 assert_eq!(setup_open_path(), expected_segments);
934 }
935
936 fn setup_multiple_open_subpaths() -> Segments {
937 let mut builder = PathBuilder::default();
938
939 builder.move_to(10.0, 10.0);
940 builder.line_to(20.0, 10.0);
941 builder.line_to(20.0, 20.0);
942
943 builder.move_to(30.0, 30.0);
944 builder.line_to(40.0, 30.0);
945 builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
946 builder.line_to(80.0, 90.0);
947
948 Segments::from(&builder.into_path())
949 }
950
951 #[test]
952 fn path_to_segments_handles_multiple_open_subpaths() {
953 let expected_segments: Segments = Segments(vec![
954 Segment::line(10.0, 10.0, 20.0, 10.0),
955 Segment::line(20.0, 10.0, 20.0, 20.0),
956 Segment::line(30.0, 30.0, 40.0, 30.0),
957 Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
958 Segment::line(70.0, 70.0, 80.0, 90.0),
959 ]);
960
961 assert_eq!(setup_multiple_open_subpaths(), expected_segments);
962 }
963
964 fn setup_closed_subpath() -> Segments {
966 let mut builder = PathBuilder::default();
967
968 builder.move_to(10.0, 10.0);
969 builder.line_to(20.0, 10.0);
970 builder.line_to(20.0, 20.0);
971 builder.close_path();
972
973 Segments::from(&builder.into_path())
974 }
975
976 #[test]
977 fn path_to_segments_handles_closed_subpath() {
978 let expected_segments: Segments = Segments(vec![
979 Segment::line(10.0, 10.0, 20.0, 10.0),
980 Segment::line(20.0, 10.0, 20.0, 20.0),
981 Segment::line(20.0, 20.0, 10.0, 10.0),
982 ]);
983
984 assert_eq!(setup_closed_subpath(), expected_segments);
985 }
986
987 fn setup_multiple_closed_subpaths() -> Segments {
990 let mut builder = PathBuilder::default();
991
992 builder.move_to(10.0, 10.0);
993 builder.line_to(20.0, 10.0);
994 builder.line_to(20.0, 20.0);
995 builder.close_path();
996
997 builder.move_to(30.0, 30.0);
998 builder.line_to(40.0, 30.0);
999 builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
1000 builder.line_to(80.0, 90.0);
1001 builder.close_path();
1002
1003 Segments::from(&builder.into_path())
1004 }
1005
1006 #[test]
1007 fn path_to_segments_handles_multiple_closed_subpaths() {
1008 let expected_segments: Segments = Segments(vec![
1009 Segment::line(10.0, 10.0, 20.0, 10.0),
1010 Segment::line(20.0, 10.0, 20.0, 20.0),
1011 Segment::line(20.0, 20.0, 10.0, 10.0),
1012 Segment::line(30.0, 30.0, 40.0, 30.0),
1013 Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
1014 Segment::line(70.0, 70.0, 80.0, 90.0),
1015 Segment::line(80.0, 90.0, 30.0, 30.0),
1016 ]);
1017
1018 assert_eq!(setup_multiple_closed_subpaths(), expected_segments);
1019 }
1020
1021 fn setup_no_moveto_after_closepath() -> Segments {
1024 let mut builder = PathBuilder::default();
1025
1026 builder.move_to(10.0, 10.0);
1027 builder.line_to(20.0, 10.0);
1028 builder.line_to(20.0, 20.0);
1029 builder.close_path();
1030
1031 builder.line_to(40.0, 30.0);
1032
1033 Segments::from(&builder.into_path())
1034 }
1035
1036 #[test]
1037 fn path_to_segments_handles_no_moveto_after_closepath() {
1038 let expected_segments: Segments = Segments(vec![
1039 Segment::line(10.0, 10.0, 20.0, 10.0),
1040 Segment::line(20.0, 10.0, 20.0, 20.0),
1041 Segment::line(20.0, 20.0, 10.0, 10.0),
1042 Segment::line(10.0, 10.0, 40.0, 30.0),
1043 ]);
1044
1045 assert_eq!(setup_no_moveto_after_closepath(), expected_segments);
1046 }
1047
1048 #[test]
1083 fn degenerate_segment_has_no_directionality() {
1084 let s = Segment::degenerate(1.0, 2.0);
1085 assert!(s.get_directionalities().is_none());
1086 }
1087
1088 #[test]
1089 fn line_segment_has_directionality() {
1090 let s = Segment::line(1.0, 2.0, 3.0, 4.0);
1091 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1092 assert_eq!((2.0, 2.0), (v1x, v1y));
1093 assert_eq!((2.0, 2.0), (v2x, v2y));
1094 }
1095
1096 #[test]
1097 fn line_segment_with_coincident_ends_has_no_directionality() {
1098 let s = Segment::line(1.0, 2.0, 1.0, 2.0);
1099 assert!(s.get_directionalities().is_none());
1100 }
1101
1102 #[test]
1103 fn curve_has_directionality() {
1104 let s = Segment::curve(1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 20.0, 33.0);
1105 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1106 assert_eq!((2.0, 3.0), (v1x, v1y));
1107 assert_eq!((12.0, 20.0), (v2x, v2y));
1108 }
1109
1110 #[test]
1111 fn curves_with_loops_and_coincident_ends_have_directionality() {
1112 let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 1.0, 2.0);
1113 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1114 assert_eq!((2.0, 2.0), (v1x, v1y));
1115 assert_eq!((-4.0, -4.0), (v2x, v2y));
1116
1117 let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0);
1118 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1119 assert_eq!((2.0, 2.0), (v1x, v1y));
1120 assert_eq!((-2.0, -2.0), (v2x, v2y));
1121
1122 let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 1.0, 2.0);
1123 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1124 assert_eq!((2.0, 2.0), (v1x, v1y));
1125 assert_eq!((-2.0, -2.0), (v2x, v2y));
1126 }
1127
1128 #[test]
1129 fn curve_with_coincident_control_points_has_no_directionality() {
1130 let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0);
1131 assert!(s.get_directionalities().is_none());
1132 }
1133
1134 #[test]
1135 fn curve_with_123_coincident_has_directionality() {
1136 let s = Segment::curve(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 20.0, 40.0);
1137 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1138 assert_eq!((20.0, 40.0), (v1x, v1y));
1139 assert_eq!((20.0, 40.0), (v2x, v2y));
1140 }
1141
1142 #[test]
1143 fn curve_with_234_coincident_has_directionality() {
1144 let s = Segment::curve(20.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
1145 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1146 assert_eq!((-20.0, -40.0), (v1x, v1y));
1147 assert_eq!((-20.0, -40.0), (v2x, v2y));
1148 }
1149
1150 #[test]
1151 fn curve_with_12_34_coincident_has_directionality() {
1152 let s = Segment::curve(20.0, 40.0, 20.0, 40.0, 60.0, 70.0, 60.0, 70.0);
1153 let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1154 assert_eq!((40.0, 30.0), (v1x, v1y));
1155 assert_eq!((40.0, 30.0), (v2x, v2y));
1156 }
1157}
1158
1159#[cfg(test)]
1160mod marker_tests {
1161 use super::*;
1162 use crate::path_builder::PathBuilder;
1163
1164 #[test]
1165 fn emits_for_open_subpath() {
1166 let mut builder = PathBuilder::default();
1167 builder.move_to(0.0, 0.0);
1168 builder.line_to(1.0, 0.0);
1169 builder.line_to(1.0, 1.0);
1170 builder.line_to(0.0, 1.0);
1171
1172 let mut v = Vec::new();
1173
1174 assert!(
1175 emit_markers_for_path(
1176 &builder.into_path(),
1177 Box::new(BoundingBox::new()),
1178 &mut |marker_type: MarkerType,
1179 x: f64,
1180 y: f64,
1181 computed_angle: Angle|
1182 -> DrawResult {
1183 v.push((marker_type, x, y, computed_angle));
1184 Ok(Box::new(BoundingBox::new()))
1185 }
1186 )
1187 .is_ok()
1188 );
1189
1190 assert_eq!(
1191 v,
1192 vec![
1193 (MarkerType::Start, 0.0, 0.0, Angle::new(0.0)),
1194 (MarkerType::Middle, 1.0, 0.0, Angle::from_vector(1.0, 1.0)),
1195 (MarkerType::Middle, 1.0, 1.0, Angle::from_vector(-1.0, 1.0)),
1196 (MarkerType::End, 0.0, 1.0, Angle::from_vector(-1.0, 0.0)),
1197 ]
1198 );
1199 }
1200
1201 #[test]
1202 fn emits_for_closed_subpath() {
1203 let mut builder = PathBuilder::default();
1204 builder.move_to(0.0, 0.0);
1205 builder.line_to(1.0, 0.0);
1206 builder.line_to(1.0, 1.0);
1207 builder.line_to(0.0, 1.0);
1208 builder.close_path();
1209
1210 let mut v = Vec::new();
1211
1212 assert!(
1213 emit_markers_for_path(
1214 &builder.into_path(),
1215 Box::new(BoundingBox::new()),
1216 &mut |marker_type: MarkerType,
1217 x: f64,
1218 y: f64,
1219 computed_angle: Angle|
1220 -> DrawResult {
1221 v.push((marker_type, x, y, computed_angle));
1222 Ok(Box::new(BoundingBox::new()))
1223 }
1224 )
1225 .is_ok()
1226 );
1227
1228 assert_eq!(
1229 v,
1230 vec![
1231 (MarkerType::Start, 0.0, 0.0, Angle::new(0.0)),
1232 (MarkerType::Middle, 1.0, 0.0, Angle::from_vector(1.0, 1.0)),
1233 (MarkerType::Middle, 1.0, 1.0, Angle::from_vector(-1.0, 1.0)),
1234 (MarkerType::Middle, 0.0, 1.0, Angle::from_vector(-1.0, -1.0)),
1235 (MarkerType::End, 0.0, 0.0, Angle::from_vector(1.0, -1.0)),
1236 ]
1237 );
1238 }
1239}