1
//! The `marker` element, and geometry computations for markers.
2

            
3
use std::f64::consts::*;
4
use std::ops::Deref;
5

            
6
use cssparser::Parser;
7
use markup5ever::{expanded_name, local_name, ns};
8

            
9
use crate::angle::Angle;
10
use crate::aspect_ratio::*;
11
use crate::bbox::BoundingBox;
12
use crate::borrow_element_as;
13
use crate::document::AcquiredNodes;
14
use crate::drawing_ctx::{DrawingCtx, Viewport};
15
use crate::element::{set_attribute, ElementTrait};
16
use crate::error::*;
17
use crate::float_eq_cairo::ApproxEqCairo;
18
use crate::layout::{self, Shape, StackingContext};
19
use crate::length::*;
20
use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
21
use crate::parse_identifiers;
22
use crate::parsers::{Parse, ParseValue};
23
use crate::path_builder::{arc_segment, ArcParameterization, CubicBezierCurve, Path, PathCommand};
24
use crate::rect::Rect;
25
use crate::rsvg_log;
26
use crate::session::Session;
27
use crate::transform::Transform;
28
use crate::viewbox::*;
29
use crate::xml::Attributes;
30

            
31
// markerUnits attribute: https://www.w3.org/TR/SVG/painting.html#MarkerElement
32
#[derive(Debug, Default, Copy, Clone, PartialEq)]
33
enum MarkerUnits {
34
    UserSpaceOnUse,
35
    #[default]
36
    StrokeWidth,
37
}
38

            
39
impl Parse for MarkerUnits {
40
517
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerUnits, ParseError<'i>> {
41
517
        Ok(parse_identifiers!(
42
517
            parser,
43
516
            "userSpaceOnUse" => MarkerUnits::UserSpaceOnUse,
44
439
            "strokeWidth" => MarkerUnits::StrokeWidth,
45
1
        )?)
46
517
    }
47
}
48

            
49
// orient attribute: https://www.w3.org/TR/SVG/painting.html#MarkerElement
50
#[derive(Debug, Copy, Clone, PartialEq)]
51
enum MarkerOrient {
52
    Auto,
53
    AutoStartReverse,
54
    Angle(Angle),
55
}
56

            
57
impl Default for MarkerOrient {
58
    #[inline]
59
1824
    fn default() -> MarkerOrient {
60
1824
        MarkerOrient::Angle(Angle::new(0.0))
61
1824
    }
62
}
63

            
64
impl Parse for MarkerOrient {
65
1340
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerOrient, ParseError<'i>> {
66
1340
        if parser
67
1340
            .try_parse(|p| p.expect_ident_matching("auto"))
68
1340
            .is_ok()
69
        {
70
1084
            return Ok(MarkerOrient::Auto);
71
256
        }
72
256

            
73
256
        if parser
74
256
            .try_parse(|p| p.expect_ident_matching("auto-start-reverse"))
75
256
            .is_ok()
76
        {
77
20
            Ok(MarkerOrient::AutoStartReverse)
78
        } else {
79
236
            Angle::parse(parser).map(MarkerOrient::Angle)
80
        }
81
1340
    }
82
}
83

            
84
pub 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

            
95
impl Default for Marker {
96
1824
    fn default() -> Marker {
97
1824
        Marker {
98
1824
            units: MarkerUnits::default(),
99
1824
            ref_x: Default::default(),
100
1824
            ref_y: Default::default(),
101
1824
            // the following two are per the spec
102
1824
            width: ULength::<Horizontal>::parse_str("3").unwrap(),
103
1824
            height: ULength::<Vertical>::parse_str("3").unwrap(),
104
1824
            orient: MarkerOrient::default(),
105
1824
            aspect: AspectRatio::default(),
106
1824
            vbox: None,
107
1824
        }
108
1824
    }
109
}
110

            
111
impl Marker {
112
3686
    fn render(
113
3686
        &self,
114
3686
        node: &Node,
115
3686
        acquired_nodes: &mut AcquiredNodes<'_>,
116
3686
        viewport: &Viewport,
117
3686
        draw_ctx: &mut DrawingCtx,
118
3686
        xpos: f64,
119
3686
        ypos: f64,
120
3686
        computed_angle: Angle,
121
3686
        line_width: f64,
122
3686
        clipping: bool,
123
3686
        marker_type: MarkerType,
124
3686
        marker: &layout::Marker,
125
3686
    ) -> Result<BoundingBox, InternalRenderingError> {
126
3686
        let mut cascaded = CascadedValues::new_from_node(node);
127
3686
        cascaded.context_fill = Some(marker.context_fill.clone());
128
3686
        cascaded.context_stroke = Some(marker.context_stroke.clone());
129
3686

            
130
3686
        let values = cascaded.get();
131
3686

            
132
3686
        let params = NormalizeParams::new(values, viewport);
133
3686

            
134
3686
        let marker_width = self.width.to_user(&params);
135
3686
        let marker_height = self.height.to_user(&params);
136
3686

            
137
3686
        if marker_width.approx_eq_cairo(0.0) || marker_height.approx_eq_cairo(0.0) {
138
            // markerWidth or markerHeight set to 0 disables rendering of the element
139
            // https://www.w3.org/TR/SVG/painting.html#MarkerWidthAttribute
140
            return Ok(viewport.empty_bbox());
141
3686
        }
142

            
143
3686
        let rotation = match self.orient {
144
627
            MarkerOrient::Auto => computed_angle,
145
            MarkerOrient::AutoStartReverse => {
146
38
                if marker_type == MarkerType::Start {
147
19
                    computed_angle.flip()
148
                } else {
149
19
                    computed_angle
150
                }
151
            }
152
3021
            MarkerOrient::Angle(a) => a,
153
        };
154

            
155
3686
        let mut transform = Transform::new_translate(xpos, ypos).pre_rotate(rotation);
156
3686

            
157
3686
        if self.units == MarkerUnits::StrokeWidth {
158
3325
            transform = transform.pre_scale(line_width, line_width);
159
3325
        }
160

            
161
3686
        let content_viewport = if let Some(vbox) = self.vbox {
162
2945
            if vbox.is_empty() {
163
19
                return Ok(viewport.empty_bbox());
164
2926
            }
165
2926

            
166
2926
            let r = self
167
2926
                .aspect
168
2926
                .compute(&vbox, &Rect::from_size(marker_width, marker_height));
169
2926

            
170
2926
            let (vb_width, vb_height) = vbox.size();
171
2926
            transform = transform.pre_scale(r.width() / vb_width, r.height() / vb_height);
172
2926

            
173
2926
            viewport.with_view_box(vb_width, vb_height)
174
        } else {
175
741
            viewport.with_view_box(marker_width, marker_height)
176
        };
177

            
178
3667
        let content_params = NormalizeParams::new(values, &content_viewport);
179
3667

            
180
3667
        transform = transform.pre_translate(
181
3667
            -self.ref_x.to_user(&content_params),
182
3667
            -self.ref_y.to_user(&content_params),
183
3667
        );
184

            
185
        // FIXME: This is the only place in the code where we pass a Some(rect) to
186
        // StackingContext::new() for its clip_rect argument.  The effect is to clip to
187
        // the viewport that the current marker should establish, but this code for
188
        // drawing markers does not yet use the layout_viewport argument in the call to
189
        // with_discrete_layer() below and instead does things by hand.  We should do all
190
        // this with the layout_viewport instead of clip_rect, and then we can remove the
191
        // clip_rect field in StackingContext.
192
3667
        let clip_rect = if values.is_overflow() {
193
171
            None
194
3496
        } else if let Some(vbox) = self.vbox {
195
2812
            Some(*vbox)
196
        } else {
197
684
            Some(Rect::from_size(marker_width, marker_height))
198
        };
199

            
200
3667
        let elt = node.borrow_element();
201
3667
        let stacking_ctx = Box::new(StackingContext::new(
202
3667
            draw_ctx.session(),
203
3667
            acquired_nodes,
204
3667
            &elt,
205
3667
            transform,
206
3667
            clip_rect,
207
3667
            values,
208
3667
        ));
209
3667

            
210
3667
        draw_ctx.with_discrete_layer(
211
3667
            &stacking_ctx,
212
3667
            acquired_nodes,
213
3667
            &content_viewport,
214
3667
            None,
215
3667
            clipping,
216
3667
            &mut |an, dc, new_viewport| {
217
3667
                node.draw_children(an, &cascaded, new_viewport, dc, clipping)
218
3667
            }, // content_viewport
219
3667
        )
220
3686
    }
221
}
222

            
223
impl ElementTrait for Marker {
224
1824
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
225
12654
        for (attr, value) in attrs.iter() {
226
12654
            match attr.expanded() {
227
                expanded_name!("", "markerUnits") => {
228
513
                    set_attribute(&mut self.units, attr.parse(value), session)
229
                }
230
                expanded_name!("", "refX") => {
231
1653
                    set_attribute(&mut self.ref_x, attr.parse(value), session)
232
                }
233
                expanded_name!("", "refY") => {
234
1653
                    set_attribute(&mut self.ref_y, attr.parse(value), session)
235
                }
236
                expanded_name!("", "markerWidth") => {
237
836
                    set_attribute(&mut self.width, attr.parse(value), session)
238
                }
239
                expanded_name!("", "markerHeight") => {
240
836
                    set_attribute(&mut self.height, attr.parse(value), session)
241
                }
242
                expanded_name!("", "orient") => {
243
1330
                    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
646
                    set_attribute(&mut self.vbox, attr.parse(value), session)
250
                }
251
5187
                _ => (),
252
            }
253
        }
254
1824
    }
255
}
256

            
257
// Machinery to figure out marker orientations
258
#[derive(Debug, PartialEq)]
259
enum Segment {
260
    Degenerate {
261
        // A single lone point
262
        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

            
278
impl Segment {
279
1
    fn degenerate(x: f64, y: f64) -> Segment {
280
1
        Segment::Degenerate { x, y }
281
1
    }
282

            
283
3213
    fn curve(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Segment {
284
3213
        Segment::LineOrCurve {
285
3213
            x1,
286
3213
            y1,
287
3213
            x2,
288
3213
            y2,
289
3213
            x3,
290
3213
            y3,
291
3213
            x4,
292
3213
            y4,
293
3213
        }
294
3213
    }
295

            
296
2802
    fn line(x1: f64, y1: f64, x2: f64, y2: f64) -> Segment {
297
2802
        Segment::curve(x1, y1, x2, y2, x1, y1, x2, y2)
298
2802
    }
299

            
300
    // If the segment has directionality, returns two vectors (v1x, v1y, v2x, v2y); otherwise,
301
    // returns None.  The vectors are the tangents at the beginning and at the end of the segment,
302
    // respectively.  A segment does not have directionality if it is degenerate (i.e. a single
303
    // point) or a zero-length segment, i.e. where all four control points are coincident (the first
304
    // and last control points may coincide, but the others may define a loop - thus nonzero length)
305
6828
    fn get_directionalities(&self) -> Option<(f64, f64, f64, f64)> {
306
6828
        match *self {
307
1
            Segment::Degenerate { .. } => None,
308

            
309
            Segment::LineOrCurve {
310
6827
                x1,
311
6827
                y1,
312
6827
                x2,
313
6827
                y2,
314
6827
                x3,
315
6827
                y3,
316
6827
                x4,
317
6827
                y4,
318
6827
            } => {
319
6827
                let coincide_1_and_2 = points_equal(x1, y1, x2, y2);
320
6827
                let coincide_1_and_3 = points_equal(x1, y1, x3, y3);
321
6827
                let coincide_1_and_4 = points_equal(x1, y1, x4, y4);
322
6827
                let coincide_2_and_3 = points_equal(x2, y2, x3, y3);
323
6827
                let coincide_2_and_4 = points_equal(x2, y2, x4, y4);
324
6827
                let coincide_3_and_4 = points_equal(x3, y3, x4, y4);
325
6827

            
326
6827
                if coincide_1_and_2 && coincide_1_and_3 && coincide_1_and_4 {
327
344
                    None
328
6483
                } else if coincide_1_and_2 && coincide_1_and_3 {
329
39
                    Some((x4 - x1, y4 - y1, x4 - x3, y4 - y3))
330
6444
                } else if coincide_1_and_2 && coincide_3_and_4 {
331
39
                    Some((x4 - x1, y4 - y1, x4 - x1, y4 - y1))
332
6405
                } else if coincide_2_and_3 && coincide_2_and_4 {
333
39
                    Some((x2 - x1, y2 - y1, x4 - x1, y4 - y1))
334
6366
                } else if coincide_1_and_2 {
335
39
                    Some((x3 - x1, y3 - y1, x4 - x3, y4 - y3))
336
6327
                } else if coincide_3_and_4 {
337
39
                    Some((x2 - x1, y2 - y1, x4 - x2, y4 - y2))
338
                } else {
339
6288
                    Some((x2 - x1, y2 - y1, x4 - x3, y4 - y3))
340
                }
341
            }
342
        }
343
6828
    }
344
}
345

            
346
40962
fn points_equal(x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
347
40962
    x1.approx_eq_cairo(x2) && y1.approx_eq_cairo(y2)
348
40962
}
349

            
350
enum SegmentState {
351
    Initial,
352
    NewSubpath,
353
    InSubpath,
354
    ClosedSubpath,
355
}
356

            
357
#[derive(Debug, PartialEq)]
358
struct Segments(Vec<Segment>);
359

            
360
impl Deref for Segments {
361
    type Target = [Segment];
362

            
363
13631
    fn deref(&self) -> &[Segment] {
364
13631
        &self.0
365
13631
    }
366
}
367

            
368
// This converts a path builder into a vector of curveto-like segments.
369
// Each segment can be:
370
//
371
// 1. Segment::Degenerate => the segment is actually a single point (x, y)
372
//
373
// 2. Segment::LineOrCurve => either a lineto or a curveto (or the effective
374
// lineto that results from a closepath).
375
// We have the following points:
376
//       P1 = (x1, y1)
377
//       P2 = (x2, y2)
378
//       P3 = (x3, y3)
379
//       P4 = (x4, y4)
380
//
381
// The start and end points are P1 and P4, respectively.
382
// The tangent at the start point is given by the vector (P2 - P1).
383
// The tangent at the end point is given by the vector (P4 - P3).
384
// The tangents also work if the segment refers to a lineto (they will
385
// both just point in the same direction).
386
impl From<&Path> for Segments {
387
1185
    fn from(path: &Path) -> Segments {
388
1185
        let mut last_x: f64;
389
1185
        let mut last_y: f64;
390
1185

            
391
1185
        let mut cur_x: f64 = 0.0;
392
1185
        let mut cur_y: f64 = 0.0;
393
1185
        let mut subpath_start_x: f64 = 0.0;
394
1185
        let mut subpath_start_y: f64 = 0.0;
395
1185

            
396
1185
        let mut segments = Vec::new();
397
1185
        let mut state = SegmentState::Initial;
398

            
399
4483
        for path_command in path.iter() {
400
4483
            last_x = cur_x;
401
4483
            last_y = cur_y;
402
4483

            
403
4483
            match path_command {
404
1301
                PathCommand::MoveTo(x, y) => {
405
1301
                    cur_x = x;
406
1301
                    cur_y = y;
407
1301

            
408
1301
                    subpath_start_x = cur_x;
409
1301
                    subpath_start_y = cur_y;
410
1301

            
411
1301
                    match state {
412
1186
                        SegmentState::Initial | SegmentState::InSubpath => {
413
1186
                            // Ignore the very first moveto in a sequence (Initial state),
414
1186
                            // or if we were already drawing within a subpath, start
415
1186
                            // a new subpath.
416
1186
                            state = SegmentState::NewSubpath;
417
1186
                        }
418

            
419
                        SegmentState::NewSubpath => {
420
                            // We had just begun a new subpath (i.e. from a moveto) and we got
421
                            // another moveto?  Output a stray point for the
422
                            // previous moveto.
423
                            segments.push(Segment::degenerate(last_x, last_y));
424
                            state = SegmentState::NewSubpath;
425
                        }
426

            
427
115
                        SegmentState::ClosedSubpath => {
428
115
                            // Cairo outputs a moveto after every closepath, so that subsequent
429
115
                            // lineto/curveto commands will start at the closed vertex.
430
115
                            // We don't want to actually emit a point (a degenerate segment) in
431
115
                            // that artificial-moveto case.
432
115
                            //
433
115
                            // We'll reset to the Initial state so that a subsequent "real"
434
115
                            // moveto will be handled as the beginning of a new subpath, or a
435
115
                            // degenerate point, as usual.
436
115
                            state = SegmentState::Initial;
437
115
                        }
438
                    }
439
                }
440

            
441
2377
                PathCommand::LineTo(x, y) => {
442
2377
                    cur_x = x;
443
2377
                    cur_y = y;
444
2377

            
445
2377
                    segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
446
2377

            
447
2377
                    state = SegmentState::InSubpath;
448
2377
                }
449

            
450
325
                PathCommand::CurveTo(curve) => {
451
325
                    let CubicBezierCurve {
452
325
                        pt1: (x2, y2),
453
325
                        pt2: (x3, y3),
454
325
                        to,
455
325
                    } = curve;
456
325
                    cur_x = to.0;
457
325
                    cur_y = to.1;
458
325

            
459
325
                    segments.push(Segment::curve(last_x, last_y, x2, y2, x3, y3, cur_x, cur_y));
460
325

            
461
325
                    state = SegmentState::InSubpath;
462
325
                }
463

            
464
76
                PathCommand::Arc(arc) => {
465
76
                    cur_x = arc.to.0;
466
76
                    cur_y = arc.to.1;
467
76

            
468
76
                    match arc.center_parameterization() {
469
                        ArcParameterization::CenterParameters {
470
76
                            center,
471
76
                            radii,
472
76
                            theta1,
473
76
                            delta_theta,
474
76
                        } => {
475
76
                            let rot = arc.x_axis_rotation;
476
76
                            let theta2 = theta1 + delta_theta;
477
76
                            let n_segs = (delta_theta / (PI * 0.5 + 0.001)).abs().ceil() as u32;
478
76
                            let d_theta = delta_theta / f64::from(n_segs);
479
76

            
480
76
                            let segment1 =
481
76
                                arc_segment(center, radii, rot, theta1, theta1 + d_theta);
482
76
                            let segment2 =
483
76
                                arc_segment(center, radii, rot, theta2 - d_theta, theta2);
484
76

            
485
76
                            let (x2, y2) = segment1.pt1;
486
76
                            let (x3, y3) = segment2.pt2;
487
76
                            segments
488
76
                                .push(Segment::curve(last_x, last_y, x2, y2, x3, y3, cur_x, cur_y));
489
76

            
490
76
                            state = SegmentState::InSubpath;
491
76
                        }
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
404
                PathCommand::ClosePath => {
502
404
                    cur_x = subpath_start_x;
503
404
                    cur_y = subpath_start_y;
504
404

            
505
404
                    segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
506
404

            
507
404
                    state = SegmentState::ClosedSubpath;
508
404
                }
509
            }
510
        }
511

            
512
1185
        if let SegmentState::NewSubpath = state {
513
            // Output a lone point if we started a subpath with a moveto
514
            // command, but there are no subsequent commands.
515
            segments.push(Segment::degenerate(cur_x, cur_y));
516
1185
        };
517

            
518
1185
        Segments(segments)
519
1185
    }
520
}
521

            
522
// The SVG spec 1.1 says http://www.w3.org/TR/SVG/implnote.html#PathElementImplementationNotes
523
// Certain line-capping and line-joining situations and markers
524
// require that a path segment have directionality at its start and
525
// end points. Zero-length path segments have no directionality. In
526
// these cases, the following algorithm is used to establish
527
// directionality:  to determine the directionality of the start
528
// point of a zero-length path segment, go backwards in the path
529
// data specification within the current subpath until you find a
530
// segment which has directionality at its end point (e.g., a path
531
// segment with non-zero length) and use its ending direction;
532
// otherwise, temporarily consider the start point to lack
533
// directionality. Similarly, to determine the directionality of the
534
// end point of a zero-length path segment, go forwards in the path
535
// data specification within the current subpath until you find a
536
// segment which has directionality at its start point (e.g., a path
537
// segment with non-zero length) and use its starting direction;
538
// otherwise, temporarily consider the end point to lack
539
// directionality. If the start point has directionality but the end
540
// point doesn't, then the end point uses the start point's
541
// directionality. If the end point has directionality but the start
542
// point doesn't, then the start point uses the end point's
543
// directionality. Otherwise, set the directionality for the path
544
// segment's start and end points to align with the positive x-axis
545
// in user space.
546
impl Segments {
547
3161
    fn find_incoming_angle_backwards(&self, start_index: usize) -> Option<Angle> {
548
        // "go backwards ... within the current subpath until ... segment which has directionality
549
        // at its end point"
550
3313
        for segment in self[..=start_index].iter().rev() {
551
3313
            match *segment {
552
                Segment::Degenerate { .. } => {
553
                    return None; // reached the beginning of the subpath as we ran into a standalone point
554
                }
555

            
556
3313
                Segment::LineOrCurve { .. } => match segment.get_directionalities() {
557
3142
                    Some((_, _, v2x, v2y)) => {
558
3142
                        return Some(Angle::from_vector(v2x, v2y));
559
                    }
560
                    None => {
561
171
                        continue;
562
                    }
563
                },
564
            }
565
        }
566

            
567
19
        None
568
3161
    }
569

            
570
3390
    fn find_outgoing_angle_forwards(&self, start_index: usize) -> Option<Angle> {
571
        // "go forwards ... within the current subpath until ... segment which has directionality at
572
        // its start point"
573
3504
        for segment in &self[start_index..] {
574
3504
            match *segment {
575
                Segment::Degenerate { .. } => {
576
                    return None; // reached the end of a subpath as we ran into a standalone point
577
                }
578

            
579
3504
                Segment::LineOrCurve { .. } => match segment.get_directionalities() {
580
3333
                    Some((v1x, v1y, _, _)) => {
581
3333
                        return Some(Angle::from_vector(v1x, v1y));
582
                    }
583
                    None => {
584
171
                        continue;
585
                    }
586
                },
587
            }
588
        }
589

            
590
57
        None
591
3390
    }
592
}
593

            
594
// From SVG's marker-start, marker-mid, marker-end properties
595
#[derive(Debug, Copy, Clone, PartialEq)]
596
enum MarkerType {
597
    Start,
598
    Middle,
599
    End,
600
}
601

            
602
3686
fn emit_marker_by_node(
603
3686
    viewport: &Viewport,
604
3686
    draw_ctx: &mut DrawingCtx,
605
3686
    acquired_nodes: &mut AcquiredNodes<'_>,
606
3686
    marker: &layout::Marker,
607
3686
    xpos: f64,
608
3686
    ypos: f64,
609
3686
    computed_angle: Angle,
610
3686
    line_width: f64,
611
3686
    clipping: bool,
612
3686
    marker_type: MarkerType,
613
3686
) -> Result<BoundingBox, InternalRenderingError> {
614
3686
    match acquired_nodes.acquire_ref(marker.node_ref.as_ref().unwrap()) {
615
3686
        Ok(acquired) => {
616
3686
            let node = acquired.get();
617
3686

            
618
3686
            let marker_elt = borrow_element_as!(node, Marker);
619
3686

            
620
3686
            marker_elt.render(
621
3686
                node,
622
3686
                acquired_nodes,
623
3686
                viewport,
624
3686
                draw_ctx,
625
3686
                xpos,
626
3686
                ypos,
627
3686
                computed_angle,
628
3686
                line_width,
629
3686
                clipping,
630
3686
                marker_type,
631
3686
                marker,
632
3686
            )
633
        }
634

            
635
        Err(e) => {
636
            rsvg_log!(draw_ctx.session(), "could not acquire marker: {}", e);
637
            Ok(viewport.empty_bbox())
638
        }
639
    }
640
3686
}
641

            
642
#[derive(Debug, Copy, Clone, PartialEq)]
643
enum MarkerEndpoint {
644
    Start,
645
    End,
646
}
647

            
648
4341
fn emit_marker<E>(
649
4341
    segment: &Segment,
650
4341
    endpoint: MarkerEndpoint,
651
4341
    marker_type: MarkerType,
652
4341
    orient: Angle,
653
4341
    emit_fn: &mut E,
654
4341
) -> Result<BoundingBox, InternalRenderingError>
655
4341
where
656
4341
    E: FnMut(MarkerType, f64, f64, Angle) -> Result<BoundingBox, InternalRenderingError>,
657
4341
{
658
4341
    let (x, y) = match *segment {
659
        Segment::Degenerate { x, y } => (x, y),
660

            
661
4341
        Segment::LineOrCurve { x1, y1, x4, y4, .. } => match endpoint {
662
3161
            MarkerEndpoint::Start => (x1, y1),
663
1180
            MarkerEndpoint::End => (x4, y4),
664
        },
665
    };
666

            
667
4341
    emit_fn(marker_type, x, y, orient)
668
4341
}
669

            
670
18028169
pub fn render_markers_for_shape(
671
18028169
    shape: &Shape,
672
18028169
    viewport: &Viewport,
673
18028169
    draw_ctx: &mut DrawingCtx,
674
18028169
    acquired_nodes: &mut AcquiredNodes<'_>,
675
18028169
    clipping: bool,
676
18028169
) -> Result<BoundingBox, InternalRenderingError> {
677
18028169
    if shape.stroke.width.approx_eq_cairo(0.0) {
678
361
        return Ok(viewport.empty_bbox());
679
18027808
    }
680
18027808

            
681
18027808
    if shape.marker_start.node_ref.is_none()
682
18026934
        && shape.marker_mid.node_ref.is_none()
683
18026839
        && shape.marker_end.node_ref.is_none()
684
    {
685
18026630
        return Ok(viewport.empty_bbox());
686
1178
    }
687
1178

            
688
1178
    emit_markers_for_path(
689
1178
        &shape.path.path,
690
1178
        viewport.empty_bbox(),
691
4332
        &mut |marker_type: MarkerType, x: f64, y: f64, computed_angle: Angle| {
692
4332
            let marker = match marker_type {
693
1178
                MarkerType::Start => &shape.marker_start,
694
1976
                MarkerType::Middle => &shape.marker_mid,
695
1178
                MarkerType::End => &shape.marker_end,
696
            };
697

            
698
4332
            if marker.node_ref.is_some() {
699
3686
                emit_marker_by_node(
700
3686
                    viewport,
701
3686
                    draw_ctx,
702
3686
                    acquired_nodes,
703
3686
                    marker,
704
3686
                    x,
705
3686
                    y,
706
3686
                    computed_angle,
707
3686
                    shape.stroke.width,
708
3686
                    clipping,
709
3686
                    marker_type,
710
3686
                )
711
            } else {
712
646
                Ok(viewport.empty_bbox())
713
            }
714
4332
        },
715
1178
    )
716
18028169
}
717

            
718
1180
fn emit_markers_for_path<E>(
719
1180
    path: &Path,
720
1180
    empty_bbox: BoundingBox,
721
1180
    emit_fn: &mut E,
722
1180
) -> Result<BoundingBox, InternalRenderingError>
723
1180
where
724
1180
    E: FnMut(MarkerType, f64, f64, Angle) -> Result<BoundingBox, InternalRenderingError>,
725
1180
{
726
    enum SubpathState {
727
        NoSubpath,
728
        InSubpath,
729
    }
730

            
731
1180
    let mut bbox = empty_bbox;
732
1180

            
733
1180
    // Convert the path to a list of segments and bare points
734
1180
    let segments = Segments::from(path);
735
1180

            
736
1180
    let mut subpath_state = SubpathState::NoSubpath;
737

            
738
3161
    for (i, segment) in segments.iter().enumerate() {
739
3161
        match *segment {
740
            Segment::Degenerate { .. } => {
741
                if let SubpathState::InSubpath = subpath_state {
742
                    assert!(i > 0);
743

            
744
                    // Got a lone point after a subpath; render the subpath's end marker first
745
                    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
                // Render marker for the lone point; no directionality
759
                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
                // Not a degenerate segment
773
3161
                match subpath_state {
774
                    SubpathState::NoSubpath => {
775
1180
                        let angle = segments
776
1180
                            .find_outgoing_angle_forwards(i)
777
1180
                            .unwrap_or_else(|| Angle::new(0.0));
778
1180
                        let marker_bbox = emit_marker(
779
1180
                            segment,
780
1180
                            MarkerEndpoint::Start,
781
1180
                            MarkerType::Start,
782
1180
                            angle,
783
1180
                            emit_fn,
784
1180
                        )?;
785
1180
                        bbox.insert(&marker_bbox);
786
1180

            
787
1180
                        subpath_state = SubpathState::InSubpath;
788
                    }
789

            
790
                    SubpathState::InSubpath => {
791
1981
                        assert!(i > 0);
792

            
793
1981
                        let incoming = segments.find_incoming_angle_backwards(i - 1);
794
1981
                        let outgoing = segments.find_outgoing_angle_forwards(i);
795

            
796
1981
                        let angle = match (incoming, outgoing) {
797
1943
                            (Some(incoming), Some(outgoing)) => incoming.bisect(outgoing),
798
38
                            (Some(incoming), _) => incoming,
799
                            (_, Some(outgoing)) => outgoing,
800
                            _ => Angle::new(0.0),
801
                        };
802

            
803
1981
                        let marker_bbox = emit_marker(
804
1981
                            segment,
805
1981
                            MarkerEndpoint::Start,
806
1981
                            MarkerType::Middle,
807
1981
                            angle,
808
1981
                            emit_fn,
809
1981
                        )?;
810
1981
                        bbox.insert(&marker_bbox);
811
                    }
812
                }
813
            }
814
        }
815
    }
816

            
817
    // Finally, render the last point
818
1180
    if !segments.is_empty() {
819
1180
        let segment = &segments[segments.len() - 1];
820
1180
        if let Segment::LineOrCurve { .. } = *segment {
821
1180
            let incoming = segments
822
1180
                .find_incoming_angle_backwards(segments.len() - 1)
823
1180
                .unwrap_or_else(|| Angle::new(0.0));
824

            
825
1180
            let angle = {
826
1180
                if let PathCommand::ClosePath = path.iter().nth(segments.len()).unwrap() {
827
229
                    let outgoing = segments
828
229
                        .find_outgoing_angle_forwards(0)
829
229
                        .unwrap_or_else(|| Angle::new(0.0));
830
229
                    incoming.bisect(outgoing)
831
                } else {
832
951
                    incoming
833
                }
834
            };
835

            
836
1180
            let marker_bbox = emit_marker(
837
1180
                segment,
838
1180
                MarkerEndpoint::End,
839
1180
                MarkerType::End,
840
1180
                angle,
841
1180
                emit_fn,
842
1180
            )?;
843
1180
            bbox.insert(&marker_bbox);
844
        }
845
    }
846

            
847
1180
    Ok(bbox)
848
1180
}
849

            
850
#[cfg(test)]
851
mod parser_tests {
852
    use super::*;
853

            
854
    #[test]
855
1
    fn parsing_invalid_marker_units_yields_error() {
856
1
        assert!(MarkerUnits::parse_str("").is_err());
857
1
        assert!(MarkerUnits::parse_str("foo").is_err());
858
1
    }
859

            
860
    #[test]
861
1
    fn parses_marker_units() {
862
1
        assert_eq!(
863
1
            MarkerUnits::parse_str("userSpaceOnUse").unwrap(),
864
1
            MarkerUnits::UserSpaceOnUse
865
1
        );
866
1
        assert_eq!(
867
1
            MarkerUnits::parse_str("strokeWidth").unwrap(),
868
1
            MarkerUnits::StrokeWidth
869
1
        );
870
1
    }
871

            
872
    #[test]
873
1
    fn parsing_invalid_marker_orient_yields_error() {
874
1
        assert!(MarkerOrient::parse_str("").is_err());
875
1
        assert!(MarkerOrient::parse_str("blah").is_err());
876
1
        assert!(MarkerOrient::parse_str("45blah").is_err());
877
1
    }
878

            
879
    #[test]
880
1
    fn parses_marker_orient() {
881
1
        assert_eq!(MarkerOrient::parse_str("auto").unwrap(), MarkerOrient::Auto);
882
1
        assert_eq!(
883
1
            MarkerOrient::parse_str("auto-start-reverse").unwrap(),
884
1
            MarkerOrient::AutoStartReverse
885
1
        );
886

            
887
1
        assert_eq!(
888
1
            MarkerOrient::parse_str("0").unwrap(),
889
1
            MarkerOrient::Angle(Angle::new(0.0))
890
1
        );
891
1
        assert_eq!(
892
1
            MarkerOrient::parse_str("180").unwrap(),
893
1
            MarkerOrient::Angle(Angle::from_degrees(180.0))
894
1
        );
895
1
        assert_eq!(
896
1
            MarkerOrient::parse_str("180deg").unwrap(),
897
1
            MarkerOrient::Angle(Angle::from_degrees(180.0))
898
1
        );
899
1
        assert_eq!(
900
1
            MarkerOrient::parse_str("-400grad").unwrap(),
901
1
            MarkerOrient::Angle(Angle::from_degrees(-360.0))
902
1
        );
903
1
        assert_eq!(
904
1
            MarkerOrient::parse_str("1rad").unwrap(),
905
1
            MarkerOrient::Angle(Angle::new(1.0))
906
1
        );
907
1
    }
908
}
909

            
910
#[cfg(test)]
911
mod directionality_tests {
912
    use super::*;
913
    use crate::path_builder::PathBuilder;
914

            
915
    // Single open path; the easy case
916
1
    fn setup_open_path() -> Segments {
917
1
        let mut builder = PathBuilder::default();
918
1

            
919
1
        builder.move_to(10.0, 10.0);
920
1
        builder.line_to(20.0, 10.0);
921
1
        builder.line_to(20.0, 20.0);
922
1

            
923
1
        Segments::from(&builder.into_path())
924
1
    }
925

            
926
    #[test]
927
1
    fn path_to_segments_handles_open_path() {
928
1
        let expected_segments: Segments = Segments(vec![
929
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
930
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
931
1
        ]);
932
1

            
933
1
        assert_eq!(setup_open_path(), expected_segments);
934
1
    }
935

            
936
1
    fn setup_multiple_open_subpaths() -> Segments {
937
1
        let mut builder = PathBuilder::default();
938
1

            
939
1
        builder.move_to(10.0, 10.0);
940
1
        builder.line_to(20.0, 10.0);
941
1
        builder.line_to(20.0, 20.0);
942
1

            
943
1
        builder.move_to(30.0, 30.0);
944
1
        builder.line_to(40.0, 30.0);
945
1
        builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
946
1
        builder.line_to(80.0, 90.0);
947
1

            
948
1
        Segments::from(&builder.into_path())
949
1
    }
950

            
951
    #[test]
952
1
    fn path_to_segments_handles_multiple_open_subpaths() {
953
1
        let expected_segments: Segments = Segments(vec![
954
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
955
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
956
1
            Segment::line(30.0, 30.0, 40.0, 30.0),
957
1
            Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
958
1
            Segment::line(70.0, 70.0, 80.0, 90.0),
959
1
        ]);
960
1

            
961
1
        assert_eq!(setup_multiple_open_subpaths(), expected_segments);
962
1
    }
963

            
964
    // Closed subpath; must have a line segment back to the first point
965
1
    fn setup_closed_subpath() -> Segments {
966
1
        let mut builder = PathBuilder::default();
967
1

            
968
1
        builder.move_to(10.0, 10.0);
969
1
        builder.line_to(20.0, 10.0);
970
1
        builder.line_to(20.0, 20.0);
971
1
        builder.close_path();
972
1

            
973
1
        Segments::from(&builder.into_path())
974
1
    }
975

            
976
    #[test]
977
1
    fn path_to_segments_handles_closed_subpath() {
978
1
        let expected_segments: Segments = Segments(vec![
979
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
980
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
981
1
            Segment::line(20.0, 20.0, 10.0, 10.0),
982
1
        ]);
983
1

            
984
1
        assert_eq!(setup_closed_subpath(), expected_segments);
985
1
    }
986

            
987
    // Multiple closed subpaths; each must have a line segment back to their
988
    // initial points, with no degenerate segments between subpaths.
989
1
    fn setup_multiple_closed_subpaths() -> Segments {
990
1
        let mut builder = PathBuilder::default();
991
1

            
992
1
        builder.move_to(10.0, 10.0);
993
1
        builder.line_to(20.0, 10.0);
994
1
        builder.line_to(20.0, 20.0);
995
1
        builder.close_path();
996
1

            
997
1
        builder.move_to(30.0, 30.0);
998
1
        builder.line_to(40.0, 30.0);
999
1
        builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
1
        builder.line_to(80.0, 90.0);
1
        builder.close_path();
1

            
1
        Segments::from(&builder.into_path())
1
    }
    #[test]
1
    fn path_to_segments_handles_multiple_closed_subpaths() {
1
        let expected_segments: Segments = Segments(vec![
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
1
            Segment::line(20.0, 20.0, 10.0, 10.0),
1
            Segment::line(30.0, 30.0, 40.0, 30.0),
1
            Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
1
            Segment::line(70.0, 70.0, 80.0, 90.0),
1
            Segment::line(80.0, 90.0, 30.0, 30.0),
1
        ]);
1

            
1
        assert_eq!(setup_multiple_closed_subpaths(), expected_segments);
1
    }
    // A lineto follows the first closed subpath, with no moveto to start the second subpath.
    // The lineto must start at the first point of the first subpath.
1
    fn setup_no_moveto_after_closepath() -> Segments {
1
        let mut builder = PathBuilder::default();
1

            
1
        builder.move_to(10.0, 10.0);
1
        builder.line_to(20.0, 10.0);
1
        builder.line_to(20.0, 20.0);
1
        builder.close_path();
1

            
1
        builder.line_to(40.0, 30.0);
1

            
1
        Segments::from(&builder.into_path())
1
    }
    #[test]
1
    fn path_to_segments_handles_no_moveto_after_closepath() {
1
        let expected_segments: Segments = Segments(vec![
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
1
            Segment::line(20.0, 20.0, 10.0, 10.0),
1
            Segment::line(10.0, 10.0, 40.0, 30.0),
1
        ]);
1

            
1
        assert_eq!(setup_no_moveto_after_closepath(), expected_segments);
1
    }
    // Sequence of moveto; should generate degenerate points.
    // This test is not enabled right now!  We create the
    // path fixtures with Cairo, and Cairo compresses
    // sequences of moveto into a single one.  So, we can't
    // really test this, as we don't get the fixture we want.
    //
    // Eventually we'll probably have to switch librsvg to
    // its own internal path representation which should
    // allow for unelided path commands, and which should
    // only build a cairo_path_t for the final rendering step.
    //
    // fn setup_sequence_of_moveto () -> Segments {
    // let mut builder = PathBuilder::default ();
    //
    // builder.move_to (10.0, 10.0);
    // builder.move_to (20.0, 20.0);
    // builder.move_to (30.0, 30.0);
    // builder.move_to (40.0, 40.0);
    //
    // Segments::from(&builder.into_path())
    // }
    //
    // #[test]
    // fn path_to_segments_handles_sequence_of_moveto () {
    // let expected_segments: Segments = Segments(vec! [
    // Segment::degenerate(10.0, 10.0),
    // Segment::degenerate(20.0, 20.0),
    // Segment::degenerate(30.0, 30.0),
    // Segment::degenerate(40.0, 40.0),
    // ]);
    //
    // assert_eq!(setup_sequence_of_moveto(), expected_segments);
    // }
    #[test]
1
    fn degenerate_segment_has_no_directionality() {
1
        let s = Segment::degenerate(1.0, 2.0);
1
        assert!(s.get_directionalities().is_none());
1
    }
    #[test]
1
    fn line_segment_has_directionality() {
1
        let s = Segment::line(1.0, 2.0, 3.0, 4.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((2.0, 2.0), (v2x, v2y));
1
    }
    #[test]
1
    fn line_segment_with_coincident_ends_has_no_directionality() {
1
        let s = Segment::line(1.0, 2.0, 1.0, 2.0);
1
        assert!(s.get_directionalities().is_none());
1
    }
    #[test]
1
    fn curve_has_directionality() {
1
        let s = Segment::curve(1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 20.0, 33.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 3.0), (v1x, v1y));
1
        assert_eq!((12.0, 20.0), (v2x, v2y));
1
    }
    #[test]
1
    fn curves_with_loops_and_coincident_ends_have_directionality() {
1
        let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 1.0, 2.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((-4.0, -4.0), (v2x, v2y));
1
        let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((-2.0, -2.0), (v2x, v2y));
1
        let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 1.0, 2.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((-2.0, -2.0), (v2x, v2y));
1
    }
    #[test]
1
    fn curve_with_coincident_control_points_has_no_directionality() {
1
        let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0);
1
        assert!(s.get_directionalities().is_none());
1
    }
    #[test]
1
    fn curve_with_123_coincident_has_directionality() {
1
        let s = Segment::curve(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 20.0, 40.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((20.0, 40.0), (v1x, v1y));
1
        assert_eq!((20.0, 40.0), (v2x, v2y));
1
    }
    #[test]
1
    fn curve_with_234_coincident_has_directionality() {
1
        let s = Segment::curve(20.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((-20.0, -40.0), (v1x, v1y));
1
        assert_eq!((-20.0, -40.0), (v2x, v2y));
1
    }
    #[test]
1
    fn curve_with_12_34_coincident_has_directionality() {
1
        let s = Segment::curve(20.0, 40.0, 20.0, 40.0, 60.0, 70.0, 60.0, 70.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((40.0, 30.0), (v1x, v1y));
1
        assert_eq!((40.0, 30.0), (v2x, v2y));
1
    }
}
#[cfg(test)]
mod marker_tests {
    use super::*;
    use crate::path_builder::PathBuilder;
    #[test]
1
    fn emits_for_open_subpath() {
1
        let mut builder = PathBuilder::default();
1
        builder.move_to(0.0, 0.0);
1
        builder.line_to(1.0, 0.0);
1
        builder.line_to(1.0, 1.0);
1
        builder.line_to(0.0, 1.0);
1

            
1
        let mut v = Vec::new();
1

            
1
        assert!(emit_markers_for_path(
1
            &builder.into_path(),
1
            BoundingBox::new(),
1
            &mut |marker_type: MarkerType,
                  x: f64,
                  y: f64,
                  computed_angle: Angle|
4
             -> Result<BoundingBox, InternalRenderingError> {
4
                v.push((marker_type, x, y, computed_angle));
4
                Ok(BoundingBox::new())
4
            }
1
        )
1
        .is_ok());
1
        assert_eq!(
1
            v,
1
            vec![
1
                (MarkerType::Start, 0.0, 0.0, Angle::new(0.0)),
1
                (MarkerType::Middle, 1.0, 0.0, Angle::from_vector(1.0, 1.0)),
1
                (MarkerType::Middle, 1.0, 1.0, Angle::from_vector(-1.0, 1.0)),
1
                (MarkerType::End, 0.0, 1.0, Angle::from_vector(-1.0, 0.0)),
1
            ]
1
        );
1
    }
    #[test]
1
    fn emits_for_closed_subpath() {
1
        let mut builder = PathBuilder::default();
1
        builder.move_to(0.0, 0.0);
1
        builder.line_to(1.0, 0.0);
1
        builder.line_to(1.0, 1.0);
1
        builder.line_to(0.0, 1.0);
1
        builder.close_path();
1

            
1
        let mut v = Vec::new();
1

            
1
        assert!(emit_markers_for_path(
1
            &builder.into_path(),
1
            BoundingBox::new(),
1
            &mut |marker_type: MarkerType,
                  x: f64,
                  y: f64,
                  computed_angle: Angle|
5
             -> Result<BoundingBox, InternalRenderingError> {
5
                v.push((marker_type, x, y, computed_angle));
5
                Ok(BoundingBox::new())
5
            }
1
        )
1
        .is_ok());
1
        assert_eq!(
1
            v,
1
            vec![
1
                (MarkerType::Start, 0.0, 0.0, Angle::new(0.0)),
1
                (MarkerType::Middle, 1.0, 0.0, Angle::from_vector(1.0, 1.0)),
1
                (MarkerType::Middle, 1.0, 1.0, Angle::from_vector(-1.0, 1.0)),
1
                (MarkerType::Middle, 0.0, 1.0, Angle::from_vector(-1.0, -1.0)),
1
                (MarkerType::End, 0.0, 0.0, Angle::from_vector(1.0, -1.0)),
1
            ]
1
        );
1
    }
}