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

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

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

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

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

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

            
60
950305
    let is_visible = values.is_visible();
61
948024
    let paint_order = values.paint_order();
62

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

            
65
1898138
    let stroke_paint = values.stroke().0.resolve(
66
        acquired_nodes,
67
949958
        values.stroke_opacity().0,
68
948393
        values.color().0,
69
948953
        cascaded.context_fill.clone(),
70
949958
        cascaded.context_stroke.clone(),
71
        session,
72
949958
    );
73

            
74
1896981
    let fill_paint = values.fill().0.resolve(
75
        acquired_nodes,
76
949642
        values.fill_opacity().0,
77
948575
        values.color().0,
78
948942
        cascaded.context_fill.clone(),
79
949642
        cascaded.context_stroke.clone(),
80
        session,
81
949642
    );
82

            
83
948741
    let fill_rule = values.fill_rule();
84
948645
    let clip_rule = values.clip_rule();
85
948558
    let shape_rendering = values.shape_rendering();
86

            
87
    let marker_start_node;
88
    let marker_mid_node;
89
    let marker_end_node;
90

            
91
949276
    if shape_def.markers == Markers::Yes {
92
1851
        marker_start_node = acquire_marker(session, acquired_nodes, &values.marker_start().0);
93
1851
        marker_mid_node = acquire_marker(session, acquired_nodes, &values.marker_mid().0);
94
1851
        marker_end_node = acquire_marker(session, acquired_nodes, &values.marker_end().0);
95
    } else {
96
946589
        marker_start_node = None;
97
946589
        marker_mid_node = None;
98
946589
        marker_end_node = None;
99
    }
100

            
101
949274
    let marker_start = Marker {
102
949274
        node_ref: marker_start_node,
103
949274
        context_stroke: stroke_paint.clone(),
104
949274
        context_fill: fill_paint.clone(),
105
    };
106

            
107
949274
    let marker_mid = Marker {
108
949274
        node_ref: marker_mid_node,
109
949274
        context_stroke: stroke_paint.clone(),
110
949274
        context_fill: fill_paint.clone(),
111
    };
112

            
113
949274
    let marker_end = Marker {
114
949274
        node_ref: marker_end_node,
115
949274
        context_stroke: stroke_paint.clone(),
116
949274
        context_fill: fill_paint.clone(),
117
    };
118

            
119
949274
    let normalize_values = NormalizeValues::new(values);
120

            
121
948495
    let path = validate_path(
122
        &shape_def.path,
123
        &stroke,
124
        viewport,
125
        &normalize_values,
126
948502
        &stroke_paint,
127
948499
        &fill_paint,
128
    )?;
129

            
130
948358
    if let layout::Path::Invalid(ref reason) = path {
131
        rsvg_log!(session, "will not render {node}: {reason}");
132
    }
133

            
134
948358
    let shape = Box::new(Shape {
135
948358
        path,
136
        is_visible,
137
        paint_order,
138
948358
        stroke,
139
        fill_rule,
140
        clip_rule,
141
        shape_rendering,
142
948358
        marker_start,
143
948358
        marker_mid,
144
948358
        marker_end,
145
948358
    });
146

            
147
948358
    let elt = node.borrow_element();
148
948496
    let stacking_ctx = StackingContext::new(
149
        session,
150
        acquired_nodes,
151
948317
        &elt,
152
948057
        values.transform(),
153
948496
        None,
154
        values,
155
    );
156

            
157
948263
    Ok(Layer {
158
948263
        kind: LayerKind::Shape(shape),
159
        stacking_ctx,
160
    })
161
948263
}
162

            
163
macro_rules! impl_draw {
164
    () => {
165
948244
        fn layout(
166
            &self,
167
            node: &Node,
168
            acquired_nodes: &mut AcquiredNodes<'_>,
169
            cascaded: &CascadedValues<'_>,
170
            viewport: &Viewport,
171
            draw_ctx: &mut DrawingCtx,
172
            _clipping: bool,
173
        ) -> Result<Option<Layer>, InternalRenderingError> {
174
948244
            draw_basic_shape(
175
                self,
176
                node,
177
                acquired_nodes,
178
                cascaded,
179
                viewport,
180
948244
                &draw_ctx.session().clone(),
181
            )
182
            .map(Some)
183
946900
        }
184

            
185
948229
        fn draw(
186
            &self,
187
            node: &Node,
188
            acquired_nodes: &mut AcquiredNodes<'_>,
189
            cascaded: &CascadedValues<'_>,
190
            viewport: &Viewport,
191
            draw_ctx: &mut DrawingCtx,
192
            clipping: bool,
193
        ) -> Result<BoundingBox, InternalRenderingError> {
194
1896458
            self.layout(node, acquired_nodes, cascaded, viewport, draw_ctx, clipping)
195
1896938
                .and_then(|layer| {
196
948709
                    draw_ctx.draw_layer(layer.as_ref().unwrap(), acquired_nodes, clipping, viewport)
197
947981
                })
198
948229
        }
199
    };
200
}
201

            
202
5553
fn acquire_marker(
203
    session: &Session,
204
    acquired_nodes: &mut AcquiredNodes<'_>,
205
    iri: &Iri,
206
) -> Option<Node> {
207
5686
    iri.get().and_then(|id| {
208
399
        acquired_nodes
209
            .acquire(id)
210
133
            .map_err(|e| {
211
                rsvg_log!(session, "cannot render marker: {}", e);
212
            })
213
            .ok()
214
266
            .and_then(|acquired| {
215
133
                let node = acquired.get();
216

            
217
133
                if is_element_of_type!(node, Marker) {
218
133
                    Some(node.clone())
219
                } else {
220
                    rsvg_log!(session, "{} is not a marker element", id);
221
                    None
222
                }
223
133
            })
224
133
    })
225
5553
}
226

            
227
275
fn make_ellipse(cx: f64, cy: f64, rx: f64, ry: f64) -> SvgPath {
228
275
    let mut builder = PathBuilder::default();
229

            
230
    // Per the spec, rx and ry must be nonnegative
231
275
    if rx <= 0.0 || ry <= 0.0 {
232
7
        return builder.into_path();
233
    }
234

            
235
    // 4/3 * (1-cos 45°)/sin 45° = 4/3 * sqrt(2) - 1
236
268
    let arc_magic: f64 = 0.5522847498;
237

            
238
    // approximate an ellipse using 4 Bézier curves
239

            
240
268
    builder.move_to(cx + rx, cy);
241

            
242
268
    builder.curve_to(
243
268
        cx + rx,
244
268
        cy + arc_magic * ry,
245
268
        cx + arc_magic * rx,
246
268
        cy + ry,
247
        cx,
248
268
        cy + ry,
249
    );
250

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

            
260
268
    builder.curve_to(
261
268
        cx - rx,
262
268
        cy - arc_magic * ry,
263
268
        cx - arc_magic * rx,
264
268
        cy - ry,
265
        cx,
266
268
        cy - ry,
267
    );
268

            
269
268
    builder.curve_to(
270
268
        cx + arc_magic * rx,
271
268
        cy - ry,
272
268
        cx + rx,
273
268
        cy - arc_magic * ry,
274
268
        cx + rx,
275
        cy,
276
    );
277

            
278
268
    builder.close_path();
279

            
280
268
    builder.into_path()
281
275
}
282

            
283
3564
#[derive(Default)]
284
pub struct Path {
285
1782
    path: Rc<SvgPath>,
286
}
287

            
288
impl ElementTrait for Path {
289
1783
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
290
9702
        for (attr, value) in attrs.iter() {
291
9706
            if attr.expanded() == expanded_name!("", "d") {
292
1784
                let mut builder = PathBuilder::default();
293
1783
                if let Err(e) = builder.parse(value) {
294
                    // Creating a partial path is OK per the spec; we don't throw away the partial
295
                    // result in case of an error.
296

            
297
                    rsvg_log!(session, "could not parse path: {}", e);
298
                }
299
1783
                self.path = Rc::new(builder.into_path());
300
1783
            }
301
7919
        }
302
1779
    }
303

            
304
    impl_draw!();
305
}
306

            
307
impl BasicShape for Path {
308
1647
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
309
1647
        ShapeDef::new(self.path.clone(), Markers::Yes)
310
1647
    }
311
}
312

            
313
/// List-of-points for polyline and polygon elements.
314
///
315
/// SVG1.1: <https://www.w3.org/TR/SVG/shapes.html#PointsBNF>
316
///
317
/// SVG2: <https://www.w3.org/TR/SVG/shapes.html#DataTypePoints>
318
146
#[derive(Debug, Default, PartialEq)]
319
73
struct Points(Vec<(f64, f64)>);
320

            
321
impl Deref for Points {
322
    type Target = [(f64, f64)];
323

            
324
109
    fn deref(&self) -> &[(f64, f64)] {
325
109
        &self.0
326
109
    }
327
}
328

            
329
impl Parse for Points {
330
441
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Points, ParseError<'i>> {
331
441
        let mut v = Vec::new();
332

            
333
        loop {
334
441
            let x = f64::parse(parser)?;
335
258
            optional_comma(parser);
336
258
            let y = f64::parse(parser)?;
337

            
338
256
            v.push((x, y));
339

            
340
256
            if parser.is_exhausted() {
341
                break;
342
            }
343

            
344
183
            match parser.next_including_whitespace() {
345
                Ok(&Token::WhiteSpace(_)) => (),
346
38
                _ => optional_comma(parser),
347
            }
348
183
        }
349

            
350
73
        Ok(Points(v))
351
75
    }
352
}
353

            
354
75
fn make_poly(points: &Points, closed: bool) -> SvgPath {
355
75
    let mut builder = PathBuilder::default();
356

            
357
416
    for (i, &(x, y)) in points.iter().enumerate() {
358
341
        if i == 0 {
359
75
            builder.move_to(x, y);
360
        } else {
361
266
            builder.line_to(x, y);
362
        }
363
    }
364

            
365
75
    if closed && !points.is_empty() {
366
34
        builder.close_path();
367
    }
368

            
369
75
    builder.into_path()
370
75
}
371

            
372
52
#[derive(Default)]
373
pub struct Polygon {
374
26
    points: Points,
375
}
376

            
377
impl ElementTrait for Polygon {
378
26
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
379
79
        for (attr, value) in attrs.iter() {
380
53
            if attr.expanded() == expanded_name!("", "points") {
381
26
                set_attribute(&mut self.points, attr.parse(value), session);
382
            }
383
53
        }
384
26
    }
385

            
386
    impl_draw!();
387
}
388

            
389
impl BasicShape for Polygon {
390
34
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
391
34
        ShapeDef::new(Rc::new(make_poly(&self.points, true)), Markers::Yes)
392
34
    }
393
}
394

            
395
82
#[derive(Default)]
396
pub struct Polyline {
397
41
    points: Points,
398
}
399

            
400
impl ElementTrait for Polyline {
401
41
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
402
161
        for (attr, value) in attrs.iter() {
403
120
            if attr.expanded() == expanded_name!("", "points") {
404
41
                set_attribute(&mut self.points, attr.parse(value), session);
405
            }
406
120
        }
407
41
    }
408

            
409
    impl_draw!();
410
}
411

            
412
impl BasicShape for Polyline {
413
41
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
414
41
        ShapeDef::new(Rc::new(make_poly(&self.points, false)), Markers::Yes)
415
41
    }
416
}
417

            
418
258
#[derive(Default)]
419
pub struct Line {
420
129
    x1: Length<Horizontal>,
421
129
    y1: Length<Vertical>,
422
129
    x2: Length<Horizontal>,
423
129
    y2: Length<Vertical>,
424
}
425

            
426
impl ElementTrait for Line {
427
129
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
428
734
        for (attr, value) in attrs.iter() {
429
605
            match attr.expanded() {
430
126
                expanded_name!("", "x1") => set_attribute(&mut self.x1, attr.parse(value), session),
431
125
                expanded_name!("", "y1") => set_attribute(&mut self.y1, attr.parse(value), session),
432
129
                expanded_name!("", "x2") => set_attribute(&mut self.x2, attr.parse(value), session),
433
125
                expanded_name!("", "y2") => set_attribute(&mut self.y2, attr.parse(value), session),
434
                _ => (),
435
            }
436
605
        }
437
129
    }
438

            
439
    impl_draw!();
440
}
441

            
442
impl BasicShape for Line {
443
129
    fn make_shape(&self, params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
444
129
        let mut builder = PathBuilder::default();
445

            
446
129
        let x1 = self.x1.to_user(params);
447
129
        let y1 = self.y1.to_user(params);
448
129
        let x2 = self.x2.to_user(params);
449
129
        let y2 = self.y2.to_user(params);
450

            
451
129
        builder.move_to(x1, y1);
452
129
        builder.line_to(x2, y2);
453

            
454
129
        ShapeDef::new(Rc::new(builder.into_path()), Markers::Yes)
455
129
    }
456
}
457

            
458
/// The `<rect>` element.
459
///
460
/// Note that its x/y/width/height/rx/ry are properties in SVG2, so they are
461
/// defined as part of [the properties machinery](properties.rs).
462
1002264
#[derive(Default)]
463
pub struct Rect {}
464

            
465
impl ElementTrait for Rect {
466
    impl_draw!();
467
}
468

            
469
impl BasicShape for Rect {
470
    #[allow(clippy::many_single_char_names)]
471
945904
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
472
945904
        let x = values.x().0.to_user(params);
473
945904
        let y = values.y().0.to_user(params);
474

            
475
945904
        let w = match values.width().0 {
476
945901
            LengthOrAuto::Length(l) => l.to_user(params),
477
3
            LengthOrAuto::Auto => 0.0,
478
        };
479
945904
        let h = match values.height().0 {
480
945901
            LengthOrAuto::Length(l) => l.to_user(params),
481
3
            LengthOrAuto::Auto => 0.0,
482
        };
483

            
484
945904
        let norm_rx = match values.rx().0 {
485
24
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
486
945880
            LengthOrAuto::Auto => None,
487
        };
488
945904
        let norm_ry = match values.ry().0 {
489
114
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
490
945790
            LengthOrAuto::Auto => None,
491
        };
492

            
493
        let mut rx;
494
        let mut ry;
495

            
496
945904
        match (norm_rx, norm_ry) {
497
945791
            (None, None) => {
498
945791
                rx = 0.0;
499
945791
                ry = 0.0;
500
            }
501

            
502
5
            (Some(_rx), None) => {
503
5
                rx = _rx;
504
5
                ry = _rx;
505
5
            }
506

            
507
5
            (None, Some(_ry)) => {
508
5
                rx = _ry;
509
5
                ry = _ry;
510
5
            }
511

            
512
103
            (Some(_rx), Some(_ry)) => {
513
103
                rx = _rx;
514
103
                ry = _ry;
515
103
            }
516
        }
517

            
518
947570
        let mut builder = PathBuilder::default();
519

            
520
        // Per the spec, w,h must be >= 0
521
945904
        if w <= 0.0 || h <= 0.0 {
522
836
            return ShapeDef::new(Rc::new(builder.into_path()), Markers::No);
523
        }
524

            
525
946740
        let half_w = w / 2.0;
526
946740
        let half_h = h / 2.0;
527

            
528
946740
        if rx > half_w {
529
4
            rx = half_w;
530
        }
531

            
532
946740
        if ry > half_h {
533
4
            ry = half_h;
534
        }
535

            
536
946740
        if rx == 0.0 {
537
946646
            ry = 0.0;
538
94
        } else if ry == 0.0 {
539
            rx = 0.0;
540
        }
541

            
542
946740
        if rx == 0.0 {
543
            // Easy case, no rounded corners
544
946646
            builder.move_to(x, y);
545
946562
            builder.line_to(x + w, y);
546
946514
            builder.line_to(x + w, y + h);
547
946493
            builder.line_to(x, y + h);
548
946492
            builder.line_to(x, y);
549
        } else {
550
            /* Hard case, rounded corners
551
             *
552
             *      (top_x1, top_y)                   (top_x2, top_y)
553
             *     *--------------------------------*
554
             *    /                                  \
555
             *   * (left_x, left_y1)                  * (right_x, right_y1)
556
             *   |                                    |
557
             *   |                                    |
558
             *   |                                    |
559
             *   |                                    |
560
             *   |                                    |
561
             *   |                                    |
562
             *   |                                    |
563
             *   |                                    |
564
             *   |                                    |
565
             *   * (left_x, left_y2)                  * (right_x, right_y2)
566
             *    \                                  /
567
             *     *--------------------------------*
568
             *      (bottom_x1, bottom_y)            (bottom_x2, bottom_y)
569
             */
570

            
571
94
            let top_x1 = x + rx;
572
94
            let top_x2 = x + w - rx;
573
            let top_y = y;
574

            
575
            let bottom_x1 = top_x1;
576
            let bottom_x2 = top_x2;
577
94
            let bottom_y = y + h;
578

            
579
            let left_x = x;
580
94
            let left_y1 = y + ry;
581
94
            let left_y2 = y + h - ry;
582

            
583
94
            let right_x = x + w;
584
            let right_y1 = left_y1;
585
            let right_y2 = left_y2;
586

            
587
94
            builder.move_to(top_x1, top_y);
588
94
            builder.line_to(top_x2, top_y);
589

            
590
94
            builder.arc(
591
                top_x2,
592
                top_y,
593
94
                rx,
594
94
                ry,
595
                0.0,
596
                LargeArc(false),
597
94
                Sweep::Positive,
598
                right_x,
599
                right_y1,
600
            );
601

            
602
94
            builder.line_to(right_x, right_y2);
603

            
604
94
            builder.arc(
605
                right_x,
606
                right_y2,
607
94
                rx,
608
94
                ry,
609
                0.0,
610
                LargeArc(false),
611
94
                Sweep::Positive,
612
                bottom_x2,
613
                bottom_y,
614
            );
615

            
616
94
            builder.line_to(bottom_x1, bottom_y);
617

            
618
94
            builder.arc(
619
                bottom_x1,
620
                bottom_y,
621
94
                rx,
622
94
                ry,
623
                0.0,
624
                LargeArc(false),
625
94
                Sweep::Positive,
626
                left_x,
627
                left_y2,
628
            );
629

            
630
94
            builder.line_to(left_x, left_y1);
631

            
632
94
            builder.arc(
633
                left_x,
634
                left_y1,
635
94
                rx,
636
94
                ry,
637
                0.0,
638
                LargeArc(false),
639
94
                Sweep::Positive,
640
                top_x1,
641
                top_y,
642
            );
643
        }
644

            
645
946605
        builder.close_path();
646

            
647
946579
        ShapeDef::new(Rc::new(builder.into_path()), Markers::No)
648
947692
    }
649
}
650

            
651
/// The `<circle>` element.
652
///
653
/// Note that its cx/cy/r are properties in SVG2, so they are
654
/// defined as part of [the properties machinery](properties.rs).
655
339
#[derive(Default)]
656
pub struct Circle {}
657

            
658
impl ElementTrait for Circle {
659
    impl_draw!();
660
}
661

            
662
impl BasicShape for Circle {
663
254
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
664
254
        let cx = values.cx().0.to_user(params);
665
254
        let cy = values.cy().0.to_user(params);
666
254
        let r = values.r().0.to_user(params);
667

            
668
254
        ShapeDef::new(Rc::new(make_ellipse(cx, cy, r, r)), Markers::No)
669
254
    }
670
}
671

            
672
/// The `<ellipse>` element.
673
///
674
/// Note that its cx/cy/rx/ry are properties in SVG2, so they are
675
/// defined as part of [the properties machinery](properties.rs).
676
20
#[derive(Default)]
677
pub struct Ellipse {}
678

            
679
impl ElementTrait for Ellipse {
680
    impl_draw!();
681
}
682

            
683
impl BasicShape for Ellipse {
684
21
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
685
21
        let cx = values.cx().0.to_user(params);
686
21
        let cy = values.cy().0.to_user(params);
687
21
        let norm_rx = match values.rx().0 {
688
19
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
689
2
            LengthOrAuto::Auto => None,
690
        };
691
21
        let norm_ry = match values.ry().0 {
692
19
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
693
2
            LengthOrAuto::Auto => None,
694
        };
695

            
696
        let rx;
697
        let ry;
698

            
699
21
        match (norm_rx, norm_ry) {
700
1
            (None, None) => {
701
1
                rx = 0.0;
702
1
                ry = 0.0;
703
            }
704

            
705
1
            (Some(_rx), None) => {
706
1
                rx = _rx;
707
1
                ry = _rx;
708
1
            }
709

            
710
1
            (None, Some(_ry)) => {
711
1
                rx = _ry;
712
1
                ry = _ry;
713
1
            }
714

            
715
18
            (Some(_rx), Some(_ry)) => {
716
18
                rx = _rx;
717
18
                ry = _ry;
718
18
            }
719
        }
720

            
721
21
        ShapeDef::new(Rc::new(make_ellipse(cx, cy, rx, ry)), Markers::No)
722
21
    }
723
}
724

            
725
#[cfg(test)]
726
mod tests {
727
    use super::*;
728

            
729
    #[test]
730
2
    fn parses_points() {
731
2
        assert_eq!(
732
1
            Points::parse_str(" 1 2 ").unwrap(),
733
1
            Points(vec![(1.0, 2.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
        assert_eq!(
740
1
            Points::parse_str("1,2,3,4").unwrap(),
741
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
742
        );
743
2
        assert_eq!(
744
1
            Points::parse_str("1,2 3,4").unwrap(),
745
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
746
        );
747
2
        assert_eq!(
748
1
            Points::parse_str("1,2 -3,4").unwrap(),
749
1
            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
750
        );
751
2
        assert_eq!(
752
1
            Points::parse_str("1,2,-3,4").unwrap(),
753
1
            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
754
        );
755
2
    }
756

            
757
    #[test]
758
2
    fn errors_on_invalid_points() {
759
1
        assert!(Points::parse_str("-1-2-3-4").is_err());
760
1
        assert!(Points::parse_str("1 2-3,-4").is_err());
761
2
    }
762
}