1
//! Basic SVG shapes: the `path`, `polygon`, `polyline`, `line`,
2
//! `rect`, `circle`, `ellipse` elements.
3

            
4
use cssparser::{Parser, Token};
5
use markup5ever::{expanded_name, local_name, namespace_url, ns};
6
use std::ops::Deref;
7
use std::rc::Rc;
8

            
9
use crate::bbox::BoundingBox;
10
use crate::document::AcquiredNodes;
11
use crate::drawing_ctx::{DrawingCtx, Viewport};
12
use crate::element::{set_attribute, ElementTrait};
13
use crate::error::*;
14
use crate::iri::Iri;
15
use crate::is_element_of_type;
16
use crate::layout::{Layer, LayerKind, Marker, Shape, StackingContext, Stroke};
17
use crate::length::*;
18
use crate::node::{CascadedValues, Node, NodeBorrow};
19
use crate::parsers::{optional_comma, Parse, ParseValue};
20
use crate::path_builder::{LargeArc, Path as SvgPath, PathBuilder, Sweep};
21
use crate::properties::ComputedValues;
22
use crate::rsvg_log;
23
use crate::session::Session;
24
use crate::xml::Attributes;
25

            
26
947747
#[derive(PartialEq)]
27
enum Markers {
28
    No,
29
    Yes,
30
}
31

            
32
struct ShapeDef {
33
    path: Rc<SvgPath>,
34
    markers: Markers,
35
}
36

            
37
impl ShapeDef {
38
948230
    fn new(path: Rc<SvgPath>, markers: Markers) -> ShapeDef {
39
948230
        ShapeDef { path, markers }
40
948230
    }
41
}
42

            
43
trait BasicShape {
44
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef;
45
}
46

            
47
952412
fn draw_basic_shape(
48
    basic_shape: &dyn BasicShape,
49
    node: &Node,
50
    acquired_nodes: &mut AcquiredNodes<'_>,
51
    cascaded: &CascadedValues<'_>,
52
    viewport: &Viewport,
53
    draw_ctx: &mut DrawingCtx,
54
    clipping: bool,
55
) -> Result<BoundingBox, InternalRenderingError> {
56
952412
    let values = cascaded.get();
57
952412
    let params = NormalizeParams::new(values, viewport);
58
952412
    let shape_def = basic_shape.make_shape(&params, values);
59

            
60
952412
    let is_visible = values.is_visible();
61
948094
    let paint_order = values.paint_order();
62

            
63
948069
    let stroke = Stroke::new(values, &params);
64

            
65
952362
    let session = draw_ctx.session();
66

            
67
1895850
    let stroke_paint = values.stroke().0.resolve(
68
        acquired_nodes,
69
952300
        values.stroke_opacity().0,
70
948676
        values.color().0,
71
948902
        cascaded.context_fill.clone(),
72
952300
        cascaded.context_stroke.clone(),
73
        session,
74
952300
    );
75

            
76
1896814
    let fill_paint = values.fill().0.resolve(
77
        acquired_nodes,
78
952078
        values.fill_opacity().0,
79
948718
        values.color().0,
80
948912
        cascaded.context_fill.clone(),
81
952078
        cascaded.context_stroke.clone(),
82
        session,
83
952078
    );
84

            
85
947903
    let fill_rule = values.fill_rule();
86
947876
    let clip_rule = values.clip_rule();
87
947785
    let shape_rendering = values.shape_rendering();
88

            
89
    let marker_start_node;
90
    let marker_mid_node;
91
    let marker_end_node;
92

            
93
951842
    if shape_def.markers == Markers::Yes {
94
1856
        marker_start_node = acquire_marker(session, acquired_nodes, &values.marker_start().0);
95
1856
        marker_mid_node = acquire_marker(session, acquired_nodes, &values.marker_mid().0);
96
1856
        marker_end_node = acquire_marker(session, acquired_nodes, &values.marker_end().0);
97
    } else {
98
945894
        marker_start_node = None;
99
945894
        marker_mid_node = None;
100
945894
        marker_end_node = None;
101
    }
102

            
103
951842
    let marker_start = Marker {
104
951842
        node_ref: marker_start_node,
105
951842
        context_stroke: stroke_paint.clone(),
106
951842
        context_fill: fill_paint.clone(),
107
    };
108

            
109
951842
    let marker_mid = Marker {
110
951842
        node_ref: marker_mid_node,
111
951842
        context_stroke: stroke_paint.clone(),
112
951842
        context_fill: fill_paint.clone(),
113
    };
114

            
115
951842
    let marker_end = Marker {
116
951842
        node_ref: marker_end_node,
117
951842
        context_stroke: stroke_paint.clone(),
118
951842
        context_fill: fill_paint.clone(),
119
    };
120

            
121
951842
    let extents = draw_ctx.compute_path_extents(&shape_def.path)?;
122

            
123
948883
    let normalize_values = NormalizeValues::new(values);
124

            
125
948045
    let stroke_paint = stroke_paint.to_user_space(&extents, viewport, &normalize_values);
126
950096
    let fill_paint = fill_paint.to_user_space(&extents, viewport, &normalize_values);
127

            
128
948778
    let shape = Box::new(Shape {
129
951414
        path: shape_def.path,
130
951414
        extents,
131
        is_visible,
132
        paint_order,
133
951414
        stroke,
134
951414
        stroke_paint,
135
        fill_paint,
136
        fill_rule,
137
        clip_rule,
138
        shape_rendering,
139
951414
        marker_start,
140
951414
        marker_mid,
141
951414
        marker_end,
142
950096
    });
143

            
144
950096
    let elt = node.borrow_element();
145
949184
    let stacking_ctx = StackingContext::new(
146
950060
        draw_ctx.session(),
147
        acquired_nodes,
148
948290
        &elt,
149
948261
        values.transform(),
150
949184
        None,
151
        values,
152
    );
153

            
154
948699
    let layer = Layer {
155
948699
        kind: LayerKind::Shape(shape),
156
        stacking_ctx,
157
    };
158

            
159
948699
    draw_ctx.draw_layer(&layer, acquired_nodes, clipping, viewport)
160
948699
}
161

            
162
macro_rules! impl_draw {
163
    () => {
164
948423
        fn draw(
165
            &self,
166
            node: &Node,
167
            acquired_nodes: &mut AcquiredNodes<'_>,
168
            cascaded: &CascadedValues<'_>,
169
            viewport: &Viewport,
170
            draw_ctx: &mut DrawingCtx,
171
            clipping: bool,
172
        ) -> Result<BoundingBox, InternalRenderingError> {
173
948423
            draw_basic_shape(
174
                self,
175
                node,
176
                acquired_nodes,
177
                cascaded,
178
                viewport,
179
                draw_ctx,
180
                clipping,
181
            )
182
948423
        }
183
    };
184
}
185

            
186
5568
fn acquire_marker(
187
    session: &Session,
188
    acquired_nodes: &mut AcquiredNodes<'_>,
189
    iri: &Iri,
190
) -> Option<Node> {
191
5701
    iri.get().and_then(|id| {
192
532
        acquired_nodes
193
133
            .acquire(id)
194
133
            .map_err(|e| {
195
                rsvg_log!(session, "cannot render marker: {}", e);
196
            })
197
            .ok()
198
266
            .and_then(|acquired| {
199
133
                let node = acquired.get();
200

            
201
266
                if is_element_of_type!(node, Marker) {
202
133
                    Some(node.clone())
203
                } else {
204
                    rsvg_log!(session, "{} is not a marker element", id);
205
                    None
206
                }
207
133
            })
208
133
    })
209
5568
}
210

            
211
275
fn make_ellipse(cx: f64, cy: f64, rx: f64, ry: f64) -> SvgPath {
212
275
    let mut builder = PathBuilder::default();
213

            
214
    // Per the spec, rx and ry must be nonnegative
215
275
    if rx <= 0.0 || ry <= 0.0 {
216
7
        return builder.into_path();
217
    }
218

            
219
    // 4/3 * (1-cos 45°)/sin 45° = 4/3 * sqrt(2) - 1
220
275
    let arc_magic: f64 = 0.5522847498;
221

            
222
    // approximate an ellipse using 4 Bézier curves
223

            
224
268
    builder.move_to(cx + rx, cy);
225

            
226
268
    builder.curve_to(
227
268
        cx + rx,
228
268
        cy + arc_magic * ry,
229
268
        cx + arc_magic * rx,
230
268
        cy + ry,
231
        cx,
232
268
        cy + ry,
233
    );
234

            
235
268
    builder.curve_to(
236
268
        cx - arc_magic * rx,
237
268
        cy + ry,
238
268
        cx - rx,
239
268
        cy + arc_magic * ry,
240
268
        cx - rx,
241
        cy,
242
    );
243

            
244
268
    builder.curve_to(
245
268
        cx - rx,
246
268
        cy - arc_magic * ry,
247
268
        cx - arc_magic * rx,
248
268
        cy - ry,
249
        cx,
250
268
        cy - ry,
251
    );
252

            
253
268
    builder.curve_to(
254
268
        cx + arc_magic * rx,
255
268
        cy - ry,
256
268
        cx + rx,
257
268
        cy - arc_magic * ry,
258
268
        cx + rx,
259
        cy,
260
    );
261

            
262
268
    builder.close_path();
263

            
264
268
    builder.into_path()
265
275
}
266

            
267
3574
#[derive(Default)]
268
pub struct Path {
269
1787
    path: Rc<SvgPath>,
270
}
271

            
272
impl ElementTrait for Path {
273
1787
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
274
9737
        for (attr, value) in attrs.iter() {
275
9739
            if attr.expanded() == expanded_name!("", "d") {
276
1787
                let mut builder = PathBuilder::default();
277
1787
                if let Err(e) = builder.parse(value) {
278
                    // Creating a partial path is OK per the spec; we don't throw away the partial
279
                    // result in case of an error.
280

            
281
10
                    rsvg_log!(session, "could not parse path: {}", e);
282
                }
283
1787
                self.path = Rc::new(builder.into_path());
284
1787
            }
285
7950
        }
286
1785
    }
287

            
288
    impl_draw!();
289
}
290

            
291
impl BasicShape for Path {
292
1652
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
293
1652
        ShapeDef::new(self.path.clone(), Markers::Yes)
294
1652
    }
295
}
296

            
297
/// List-of-points for polyline and polygon elements.
298
///
299
/// SVG1.1: <https://www.w3.org/TR/SVG/shapes.html#PointsBNF>
300
///
301
/// SVG2: <https://www.w3.org/TR/SVG/shapes.html#DataTypePoints>
302
146
#[derive(Debug, Default, PartialEq)]
303
73
struct Points(Vec<(f64, f64)>);
304

            
305
impl Deref for Points {
306
    type Target = [(f64, f64)];
307

            
308
109
    fn deref(&self) -> &[(f64, f64)] {
309
109
        &self.0
310
109
    }
311
}
312

            
313
impl Parse for Points {
314
441
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Points, ParseError<'i>> {
315
441
        let mut v = Vec::new();
316

            
317
        loop {
318
441
            let x = f64::parse(parser)?;
319
258
            optional_comma(parser);
320
258
            let y = f64::parse(parser)?;
321

            
322
256
            v.push((x, y));
323

            
324
256
            if parser.is_exhausted() {
325
                break;
326
            }
327

            
328
183
            match parser.next_including_whitespace() {
329
                Ok(&Token::WhiteSpace(_)) => (),
330
38
                _ => optional_comma(parser),
331
            }
332
183
        }
333

            
334
73
        Ok(Points(v))
335
75
    }
336
}
337

            
338
75
fn make_poly(points: &Points, closed: bool) -> SvgPath {
339
75
    let mut builder = PathBuilder::default();
340

            
341
416
    for (i, &(x, y)) in points.iter().enumerate() {
342
341
        if i == 0 {
343
75
            builder.move_to(x, y);
344
        } else {
345
266
            builder.line_to(x, y);
346
        }
347
    }
348

            
349
75
    if closed && !points.is_empty() {
350
34
        builder.close_path();
351
    }
352

            
353
75
    builder.into_path()
354
75
}
355

            
356
52
#[derive(Default)]
357
pub struct Polygon {
358
26
    points: Points,
359
}
360

            
361
impl ElementTrait for Polygon {
362
26
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
363
79
        for (attr, value) in attrs.iter() {
364
53
            if attr.expanded() == expanded_name!("", "points") {
365
26
                set_attribute(&mut self.points, attr.parse(value), session);
366
            }
367
53
        }
368
26
    }
369

            
370
    impl_draw!();
371
}
372

            
373
impl BasicShape for Polygon {
374
34
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
375
34
        ShapeDef::new(Rc::new(make_poly(&self.points, true)), Markers::Yes)
376
34
    }
377
}
378

            
379
82
#[derive(Default)]
380
pub struct Polyline {
381
41
    points: Points,
382
}
383

            
384
impl ElementTrait for Polyline {
385
41
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
386
161
        for (attr, value) in attrs.iter() {
387
120
            if attr.expanded() == expanded_name!("", "points") {
388
41
                set_attribute(&mut self.points, attr.parse(value), session);
389
            }
390
120
        }
391
41
    }
392

            
393
    impl_draw!();
394
}
395

            
396
impl BasicShape for Polyline {
397
41
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
398
41
        ShapeDef::new(Rc::new(make_poly(&self.points, false)), Markers::Yes)
399
41
    }
400
}
401

            
402
258
#[derive(Default)]
403
pub struct Line {
404
129
    x1: Length<Horizontal>,
405
129
    y1: Length<Vertical>,
406
129
    x2: Length<Horizontal>,
407
129
    y2: Length<Vertical>,
408
}
409

            
410
impl ElementTrait for Line {
411
129
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
412
734
        for (attr, value) in attrs.iter() {
413
605
            match attr.expanded() {
414
126
                expanded_name!("", "x1") => set_attribute(&mut self.x1, attr.parse(value), session),
415
125
                expanded_name!("", "y1") => set_attribute(&mut self.y1, attr.parse(value), session),
416
129
                expanded_name!("", "x2") => set_attribute(&mut self.x2, attr.parse(value), session),
417
125
                expanded_name!("", "y2") => set_attribute(&mut self.y2, attr.parse(value), session),
418
                _ => (),
419
            }
420
605
        }
421
129
    }
422

            
423
    impl_draw!();
424
}
425

            
426
impl BasicShape for Line {
427
129
    fn make_shape(&self, params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
428
129
        let mut builder = PathBuilder::default();
429

            
430
129
        let x1 = self.x1.to_user(params);
431
129
        let y1 = self.y1.to_user(params);
432
129
        let x2 = self.x2.to_user(params);
433
129
        let y2 = self.y2.to_user(params);
434

            
435
129
        builder.move_to(x1, y1);
436
129
        builder.line_to(x2, y2);
437

            
438
129
        ShapeDef::new(Rc::new(builder.into_path()), Markers::Yes)
439
129
    }
440
}
441

            
442
/// The `<rect>` element.
443
///
444
/// Note that its x/y/width/height/rx/ry are properties in SVG2, so they are
445
/// defined as part of [the properties machinery](properties.rs).
446
1002230
#[derive(Default)]
447
pub struct Rect {}
448

            
449
impl ElementTrait for Rect {
450
    impl_draw!();
451
}
452

            
453
impl BasicShape for Rect {
454
    #[allow(clippy::many_single_char_names)]
455
945797
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
456
945797
        let x = values.x().0.to_user(params);
457
945797
        let y = values.y().0.to_user(params);
458

            
459
945797
        let w = match values.width().0 {
460
945794
            LengthOrAuto::Length(l) => l.to_user(params),
461
3
            LengthOrAuto::Auto => 0.0,
462
        };
463
945797
        let h = match values.height().0 {
464
945794
            LengthOrAuto::Length(l) => l.to_user(params),
465
3
            LengthOrAuto::Auto => 0.0,
466
        };
467

            
468
945797
        let norm_rx = match values.rx().0 {
469
27
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
470
945770
            LengthOrAuto::Auto => None,
471
        };
472
945797
        let norm_ry = match values.ry().0 {
473
104
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
474
945693
            LengthOrAuto::Auto => None,
475
        };
476

            
477
        let mut rx;
478
        let mut ry;
479

            
480
945797
        match (norm_rx, norm_ry) {
481
945684
            (None, None) => {
482
945684
                rx = 0.0;
483
945684
                ry = 0.0;
484
            }
485

            
486
5
            (Some(_rx), None) => {
487
5
                rx = _rx;
488
5
                ry = _rx;
489
5
            }
490

            
491
5
            (None, Some(_ry)) => {
492
5
                rx = _ry;
493
5
                ry = _ry;
494
5
            }
495

            
496
103
            (Some(_rx), Some(_ry)) => {
497
103
                rx = _rx;
498
103
                ry = _ry;
499
103
            }
500
        }
501

            
502
947749
        let mut builder = PathBuilder::default();
503

            
504
        // Per the spec, w,h must be >= 0
505
945797
        if w <= 0.0 || h <= 0.0 {
506
1008
            return ShapeDef::new(Rc::new(builder.into_path()), Markers::No);
507
        }
508

            
509
946741
        let half_w = w / 2.0;
510
946741
        let half_h = h / 2.0;
511

            
512
946741
        if rx > half_w {
513
4
            rx = half_w;
514
        }
515

            
516
946741
        if ry > half_h {
517
4
            ry = half_h;
518
        }
519

            
520
946741
        if rx == 0.0 {
521
946647
            ry = 0.0;
522
94
        } else if ry == 0.0 {
523
            rx = 0.0;
524
        }
525

            
526
946741
        if rx == 0.0 {
527
            // Easy case, no rounded corners
528
946647
            builder.move_to(x, y);
529
946457
            builder.line_to(x + w, y);
530
946447
            builder.line_to(x + w, y + h);
531
946528
            builder.line_to(x, y + h);
532
946484
            builder.line_to(x, y);
533
        } else {
534
            /* Hard case, rounded corners
535
             *
536
             *      (top_x1, top_y)                   (top_x2, top_y)
537
             *     *--------------------------------*
538
             *    /                                  \
539
             *   * (left_x, left_y1)                  * (right_x, right_y1)
540
             *   |                                    |
541
             *   |                                    |
542
             *   |                                    |
543
             *   |                                    |
544
             *   |                                    |
545
             *   |                                    |
546
             *   |                                    |
547
             *   |                                    |
548
             *   |                                    |
549
             *   * (left_x, left_y2)                  * (right_x, right_y2)
550
             *    \                                  /
551
             *     *--------------------------------*
552
             *      (bottom_x1, bottom_y)            (bottom_x2, bottom_y)
553
             */
554

            
555
94
            let top_x1 = x + rx;
556
94
            let top_x2 = x + w - rx;
557
            let top_y = y;
558

            
559
            let bottom_x1 = top_x1;
560
            let bottom_x2 = top_x2;
561
94
            let bottom_y = y + h;
562

            
563
            let left_x = x;
564
94
            let left_y1 = y + ry;
565
94
            let left_y2 = y + h - ry;
566

            
567
94
            let right_x = x + w;
568
            let right_y1 = left_y1;
569
            let right_y2 = left_y2;
570

            
571
94
            builder.move_to(top_x1, top_y);
572
94
            builder.line_to(top_x2, top_y);
573

            
574
94
            builder.arc(
575
                top_x2,
576
                top_y,
577
94
                rx,
578
94
                ry,
579
                0.0,
580
94
                LargeArc(false),
581
94
                Sweep::Positive,
582
                right_x,
583
                right_y1,
584
            );
585

            
586
94
            builder.line_to(right_x, right_y2);
587

            
588
94
            builder.arc(
589
                right_x,
590
                right_y2,
591
94
                rx,
592
94
                ry,
593
                0.0,
594
94
                LargeArc(false),
595
94
                Sweep::Positive,
596
                bottom_x2,
597
                bottom_y,
598
            );
599

            
600
94
            builder.line_to(bottom_x1, bottom_y);
601

            
602
94
            builder.arc(
603
                bottom_x1,
604
                bottom_y,
605
94
                rx,
606
94
                ry,
607
                0.0,
608
94
                LargeArc(false),
609
94
                Sweep::Positive,
610
                left_x,
611
                left_y2,
612
            );
613

            
614
94
            builder.line_to(left_x, left_y1);
615

            
616
94
            builder.arc(
617
                left_x,
618
                left_y1,
619
94
                rx,
620
94
                ry,
621
                0.0,
622
94
                LargeArc(false),
623
94
                Sweep::Positive,
624
                top_x1,
625
                top_y,
626
            );
627
        }
628

            
629
946627
        builder.close_path();
630

            
631
946615
        ShapeDef::new(Rc::new(builder.into_path()), Markers::No)
632
947907
    }
633
}
634

            
635
/// The `<circle>` element.
636
///
637
/// Note that its cx/cy/r are properties in SVG2, so they are
638
/// defined as part of [the properties machinery](properties.rs).
639
339
#[derive(Default)]
640
pub struct Circle {}
641

            
642
impl ElementTrait for Circle {
643
    impl_draw!();
644
}
645

            
646
impl BasicShape for Circle {
647
254
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
648
254
        let cx = values.cx().0.to_user(params);
649
254
        let cy = values.cy().0.to_user(params);
650
254
        let r = values.r().0.to_user(params);
651

            
652
254
        ShapeDef::new(Rc::new(make_ellipse(cx, cy, r, r)), Markers::No)
653
254
    }
654
}
655

            
656
/// The `<ellipse>` element.
657
///
658
/// Note that its cx/cy/rx/ry are properties in SVG2, so they are
659
/// defined as part of [the properties machinery](properties.rs).
660
20
#[derive(Default)]
661
pub struct Ellipse {}
662

            
663
impl ElementTrait for Ellipse {
664
    impl_draw!();
665
}
666

            
667
impl BasicShape for Ellipse {
668
21
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
669
21
        let cx = values.cx().0.to_user(params);
670
21
        let cy = values.cy().0.to_user(params);
671
21
        let norm_rx = match values.rx().0 {
672
19
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
673
2
            LengthOrAuto::Auto => None,
674
        };
675
21
        let norm_ry = match values.ry().0 {
676
19
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
677
2
            LengthOrAuto::Auto => None,
678
        };
679

            
680
        let rx;
681
        let ry;
682

            
683
21
        match (norm_rx, norm_ry) {
684
1
            (None, None) => {
685
1
                rx = 0.0;
686
1
                ry = 0.0;
687
            }
688

            
689
1
            (Some(_rx), None) => {
690
1
                rx = _rx;
691
1
                ry = _rx;
692
1
            }
693

            
694
1
            (None, Some(_ry)) => {
695
1
                rx = _ry;
696
1
                ry = _ry;
697
1
            }
698

            
699
18
            (Some(_rx), Some(_ry)) => {
700
18
                rx = _rx;
701
18
                ry = _ry;
702
18
            }
703
        }
704

            
705
21
        ShapeDef::new(Rc::new(make_ellipse(cx, cy, rx, ry)), Markers::No)
706
21
    }
707
}
708

            
709
#[cfg(test)]
710
mod tests {
711
    use super::*;
712

            
713
    #[test]
714
2
    fn parses_points() {
715
2
        assert_eq!(
716
1
            Points::parse_str(" 1 2 ").unwrap(),
717
1
            Points(vec![(1.0, 2.0)])
718
        );
719
2
        assert_eq!(
720
1
            Points::parse_str("1 2 3 4").unwrap(),
721
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
722
        );
723
2
        assert_eq!(
724
1
            Points::parse_str("1,2,3,4").unwrap(),
725
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
726
        );
727
2
        assert_eq!(
728
1
            Points::parse_str("1,2 3,4").unwrap(),
729
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
730
        );
731
2
        assert_eq!(
732
1
            Points::parse_str("1,2 -3,4").unwrap(),
733
1
            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
734
        );
735
2
        assert_eq!(
736
1
            Points::parse_str("1,2,-3,4").unwrap(),
737
1
            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
738
        );
739
2
    }
740

            
741
    #[test]
742
2
    fn errors_on_invalid_points() {
743
1
        assert!(Points::parse_str("-1-2-3-4").is_err());
744
1
        assert!(Points::parse_str("1 2-3,-4").is_err());
745
2
    }
746
}