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::{compute_path_extents, 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
948036
#[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
948466
    fn new(path: Rc<SvgPath>, markers: Markers) -> ShapeDef {
39
948466
        ShapeDef { path, markers }
40
948466
    }
41
}
42

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

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

            
59
952362
    let is_visible = values.is_visible();
60
948343
    let paint_order = values.paint_order();
61

            
62
948273
    let stroke = Stroke::new(values, &params);
63

            
64
1900006
    let stroke_paint = values.stroke().0.resolve(
65
        acquired_nodes,
66
951929
        values.stroke_opacity().0,
67
948711
        values.color().0,
68
948925
        cascaded.context_fill.clone(),
69
951929
        cascaded.context_stroke.clone(),
70
        session,
71
951929
    );
72

            
73
1897203
    let fill_paint = values.fill().0.resolve(
74
        acquired_nodes,
75
951192
        values.fill_opacity().0,
76
948455
        values.color().0,
77
948953
        cascaded.context_fill.clone(),
78
951192
        cascaded.context_stroke.clone(),
79
        session,
80
951192
    );
81

            
82
948458
    let fill_rule = values.fill_rule();
83
948238
    let clip_rule = values.clip_rule();
84
948087
    let shape_rendering = values.shape_rendering();
85

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

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

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

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

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

            
118
950450
    let extents = compute_path_extents(&shape_def.path)?;
119

            
120
948534
    let normalize_values = NormalizeValues::new(values);
121

            
122
947818
    let stroke_paint = stroke_paint.to_user_space(&extents, viewport, &normalize_values);
123
949004
    let fill_paint = fill_paint.to_user_space(&extents, viewport, &normalize_values);
124

            
125
948719
    let shape = Box::new(Shape {
126
948719
        path: shape_def.path,
127
948719
        extents,
128
        is_visible,
129
        paint_order,
130
948719
        stroke,
131
948719
        stroke_paint,
132
        fill_paint,
133
        fill_rule,
134
        clip_rule,
135
        shape_rendering,
136
948719
        marker_start,
137
948719
        marker_mid,
138
948719
        marker_end,
139
948434
    });
140

            
141
948434
    let elt = node.borrow_element();
142
947753
    let stacking_ctx = StackingContext::new(
143
        session,
144
        acquired_nodes,
145
947954
        &elt,
146
947812
        values.transform(),
147
947753
        None,
148
        values,
149
    );
150

            
151
948630
    Ok(Layer {
152
948630
        kind: LayerKind::Shape(shape),
153
        stacking_ctx,
154
    })
155
947670
}
156

            
157
macro_rules! impl_draw {
158
    () => {
159
948365
        fn layout(
160
            &self,
161
            node: &Node,
162
            acquired_nodes: &mut AcquiredNodes<'_>,
163
            cascaded: &CascadedValues<'_>,
164
            viewport: &Viewport,
165
            draw_ctx: &mut DrawingCtx,
166
            _clipping: bool,
167
        ) -> Result<Option<Layer>, InternalRenderingError> {
168
948365
            draw_basic_shape(
169
                self,
170
                node,
171
                acquired_nodes,
172
                cascaded,
173
                viewport,
174
948365
                &draw_ctx.session().clone(),
175
            )
176
            .map(Some)
177
945701
        }
178

            
179
948375
        fn draw(
180
            &self,
181
            node: &Node,
182
            acquired_nodes: &mut AcquiredNodes<'_>,
183
            cascaded: &CascadedValues<'_>,
184
            viewport: &Viewport,
185
948276
            draw_ctx: &mut DrawingCtx,
186
            clipping: bool,
187
        ) -> Result<BoundingBox, InternalRenderingError> {
188
1896750
            self.layout(node, acquired_nodes, cascaded, viewport, draw_ctx, clipping)
189
1896651
                .and_then(|layer| {
190
948276
                    draw_ctx.draw_layer(layer.as_ref().unwrap(), acquired_nodes, clipping, viewport)
191
947838
                })
192
948375
        }
193
    };
194
}
195

            
196
5567
fn acquire_marker(
197
    session: &Session,
198
    acquired_nodes: &mut AcquiredNodes<'_>,
199
    iri: &Iri,
200
) -> Option<Node> {
201
5700
    iri.get().and_then(|id| {
202
532
        acquired_nodes
203
133
            .acquire(id)
204
133
            .map_err(|e| {
205
                rsvg_log!(session, "cannot render marker: {}", e);
206
            })
207
            .ok()
208
266
            .and_then(|acquired| {
209
133
                let node = acquired.get();
210

            
211
266
                if is_element_of_type!(node, Marker) {
212
133
                    Some(node.clone())
213
                } else {
214
                    rsvg_log!(session, "{} is not a marker element", id);
215
                    None
216
                }
217
133
            })
218
133
    })
219
5567
}
220

            
221
275
fn make_ellipse(cx: f64, cy: f64, rx: f64, ry: f64) -> SvgPath {
222
275
    let mut builder = PathBuilder::default();
223

            
224
    // Per the spec, rx and ry must be nonnegative
225
275
    if rx <= 0.0 || ry <= 0.0 {
226
7
        return builder.into_path();
227
    }
228

            
229
    // 4/3 * (1-cos 45°)/sin 45° = 4/3 * sqrt(2) - 1
230
275
    let arc_magic: f64 = 0.5522847498;
231

            
232
    // approximate an ellipse using 4 Bézier curves
233

            
234
268
    builder.move_to(cx + rx, cy);
235

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

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

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

            
263
268
    builder.curve_to(
264
268
        cx + arc_magic * rx,
265
268
        cy - ry,
266
268
        cx + rx,
267
268
        cy - arc_magic * ry,
268
268
        cx + rx,
269
        cy,
270
    );
271

            
272
268
    builder.close_path();
273

            
274
268
    builder.into_path()
275
275
}
276

            
277
3574
#[derive(Default)]
278
pub struct Path {
279
1787
    path: Rc<SvgPath>,
280
}
281

            
282
impl ElementTrait for Path {
283
1787
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
284
9738
        for (attr, value) in attrs.iter() {
285
9739
            if attr.expanded() == expanded_name!("", "d") {
286
1789
                let mut builder = PathBuilder::default();
287
1788
                if let Err(e) = builder.parse(value) {
288
                    // Creating a partial path is OK per the spec; we don't throw away the partial
289
                    // result in case of an error.
290

            
291
10
                    rsvg_log!(session, "could not parse path: {}", e);
292
                }
293
1787
                self.path = Rc::new(builder.into_path());
294
1786
            }
295
7951
        }
296
1785
    }
297

            
298
    impl_draw!();
299
}
300

            
301
impl BasicShape for Path {
302
1652
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
303
1652
        ShapeDef::new(self.path.clone(), Markers::Yes)
304
1652
    }
305
}
306

            
307
/// List-of-points for polyline and polygon elements.
308
///
309
/// SVG1.1: <https://www.w3.org/TR/SVG/shapes.html#PointsBNF>
310
///
311
/// SVG2: <https://www.w3.org/TR/SVG/shapes.html#DataTypePoints>
312
146
#[derive(Debug, Default, PartialEq)]
313
73
struct Points(Vec<(f64, f64)>);
314

            
315
impl Deref for Points {
316
    type Target = [(f64, f64)];
317

            
318
109
    fn deref(&self) -> &[(f64, f64)] {
319
109
        &self.0
320
109
    }
321
}
322

            
323
impl Parse for Points {
324
441
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Points, ParseError<'i>> {
325
441
        let mut v = Vec::new();
326

            
327
        loop {
328
441
            let x = f64::parse(parser)?;
329
258
            optional_comma(parser);
330
258
            let y = f64::parse(parser)?;
331

            
332
256
            v.push((x, y));
333

            
334
256
            if parser.is_exhausted() {
335
                break;
336
            }
337

            
338
183
            match parser.next_including_whitespace() {
339
                Ok(&Token::WhiteSpace(_)) => (),
340
38
                _ => optional_comma(parser),
341
            }
342
183
        }
343

            
344
73
        Ok(Points(v))
345
75
    }
346
}
347

            
348
75
fn make_poly(points: &Points, closed: bool) -> SvgPath {
349
75
    let mut builder = PathBuilder::default();
350

            
351
416
    for (i, &(x, y)) in points.iter().enumerate() {
352
341
        if i == 0 {
353
75
            builder.move_to(x, y);
354
        } else {
355
266
            builder.line_to(x, y);
356
        }
357
    }
358

            
359
75
    if closed && !points.is_empty() {
360
34
        builder.close_path();
361
    }
362

            
363
75
    builder.into_path()
364
75
}
365

            
366
52
#[derive(Default)]
367
pub struct Polygon {
368
26
    points: Points,
369
}
370

            
371
impl ElementTrait for Polygon {
372
26
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
373
79
        for (attr, value) in attrs.iter() {
374
53
            if attr.expanded() == expanded_name!("", "points") {
375
26
                set_attribute(&mut self.points, attr.parse(value), session);
376
            }
377
53
        }
378
26
    }
379

            
380
    impl_draw!();
381
}
382

            
383
impl BasicShape for Polygon {
384
34
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
385
34
        ShapeDef::new(Rc::new(make_poly(&self.points, true)), Markers::Yes)
386
34
    }
387
}
388

            
389
82
#[derive(Default)]
390
pub struct Polyline {
391
41
    points: Points,
392
}
393

            
394
impl ElementTrait for Polyline {
395
41
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
396
161
        for (attr, value) in attrs.iter() {
397
120
            if attr.expanded() == expanded_name!("", "points") {
398
41
                set_attribute(&mut self.points, attr.parse(value), session);
399
            }
400
120
        }
401
41
    }
402

            
403
    impl_draw!();
404
}
405

            
406
impl BasicShape for Polyline {
407
41
    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
408
41
        ShapeDef::new(Rc::new(make_poly(&self.points, false)), Markers::Yes)
409
41
    }
410
}
411

            
412
258
#[derive(Default)]
413
pub struct Line {
414
129
    x1: Length<Horizontal>,
415
129
    y1: Length<Vertical>,
416
129
    x2: Length<Horizontal>,
417
129
    y2: Length<Vertical>,
418
}
419

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

            
433
    impl_draw!();
434
}
435

            
436
impl BasicShape for Line {
437
129
    fn make_shape(&self, params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
438
129
        let mut builder = PathBuilder::default();
439

            
440
129
        let x1 = self.x1.to_user(params);
441
129
        let y1 = self.y1.to_user(params);
442
129
        let x2 = self.x2.to_user(params);
443
129
        let y2 = self.y2.to_user(params);
444

            
445
129
        builder.move_to(x1, y1);
446
129
        builder.line_to(x2, y2);
447

            
448
129
        ShapeDef::new(Rc::new(builder.into_path()), Markers::Yes)
449
129
    }
450
}
451

            
452
/// The `<rect>` element.
453
///
454
/// Note that its x/y/width/height/rx/ry are properties in SVG2, so they are
455
/// defined as part of [the properties machinery](properties.rs).
456
1002231
#[derive(Default)]
457
pub struct Rect {}
458

            
459
impl ElementTrait for Rect {
460
    impl_draw!();
461
}
462

            
463
impl BasicShape for Rect {
464
    #[allow(clippy::many_single_char_names)]
465
945659
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
466
945659
        let x = values.x().0.to_user(params);
467
945659
        let y = values.y().0.to_user(params);
468

            
469
945659
        let w = match values.width().0 {
470
945656
            LengthOrAuto::Length(l) => l.to_user(params),
471
3
            LengthOrAuto::Auto => 0.0,
472
        };
473
945659
        let h = match values.height().0 {
474
945656
            LengthOrAuto::Length(l) => l.to_user(params),
475
3
            LengthOrAuto::Auto => 0.0,
476
        };
477

            
478
945659
        let norm_rx = match values.rx().0 {
479
50
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
480
945609
            LengthOrAuto::Auto => None,
481
        };
482
945659
        let norm_ry = match values.ry().0 {
483
111
            LengthOrAuto::Length(l) => Some(l.to_user(params)),
484
945548
            LengthOrAuto::Auto => None,
485
        };
486

            
487
        let mut rx;
488
        let mut ry;
489

            
490
945659
        match (norm_rx, norm_ry) {
491
945546
            (None, None) => {
492
945546
                rx = 0.0;
493
945546
                ry = 0.0;
494
            }
495

            
496
5
            (Some(_rx), None) => {
497
5
                rx = _rx;
498
5
                ry = _rx;
499
5
            }
500

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

            
506
103
            (Some(_rx), Some(_ry)) => {
507
103
                rx = _rx;
508
103
                ry = _ry;
509
103
            }
510
        }
511

            
512
947493
        let mut builder = PathBuilder::default();
513

            
514
        // Per the spec, w,h must be >= 0
515
945659
        if w <= 0.0 || h <= 0.0 {
516
917
            return ShapeDef::new(Rc::new(builder.into_path()), Markers::No);
517
        }
518

            
519
946576
        let half_w = w / 2.0;
520
946576
        let half_h = h / 2.0;
521

            
522
946576
        if rx > half_w {
523
4
            rx = half_w;
524
        }
525

            
526
946576
        if ry > half_h {
527
4
            ry = half_h;
528
        }
529

            
530
946576
        if rx == 0.0 {
531
946482
            ry = 0.0;
532
94
        } else if ry == 0.0 {
533
            rx = 0.0;
534
        }
535

            
536
946576
        if rx == 0.0 {
537
            // Easy case, no rounded corners
538
946482
            builder.move_to(x, y);
539
946479
            builder.line_to(x + w, y);
540
946557
            builder.line_to(x + w, y + h);
541
946500
            builder.line_to(x, y + h);
542
946491
            builder.line_to(x, y);
543
        } else {
544
            /* Hard case, rounded corners
545
             *
546
             *      (top_x1, top_y)                   (top_x2, top_y)
547
             *     *--------------------------------*
548
             *    /                                  \
549
             *   * (left_x, left_y1)                  * (right_x, right_y1)
550
             *   |                                    |
551
             *   |                                    |
552
             *   |                                    |
553
             *   |                                    |
554
             *   |                                    |
555
             *   |                                    |
556
             *   |                                    |
557
             *   |                                    |
558
             *   |                                    |
559
             *   * (left_x, left_y2)                  * (right_x, right_y2)
560
             *    \                                  /
561
             *     *--------------------------------*
562
             *      (bottom_x1, bottom_y)            (bottom_x2, bottom_y)
563
             */
564

            
565
94
            let top_x1 = x + rx;
566
94
            let top_x2 = x + w - rx;
567
            let top_y = y;
568

            
569
            let bottom_x1 = top_x1;
570
            let bottom_x2 = top_x2;
571
94
            let bottom_y = y + h;
572

            
573
            let left_x = x;
574
94
            let left_y1 = y + ry;
575
94
            let left_y2 = y + h - ry;
576

            
577
94
            let right_x = x + w;
578
            let right_y1 = left_y1;
579
            let right_y2 = left_y2;
580

            
581
94
            builder.move_to(top_x1, top_y);
582
94
            builder.line_to(top_x2, top_y);
583

            
584
94
            builder.arc(
585
                top_x2,
586
                top_y,
587
94
                rx,
588
94
                ry,
589
                0.0,
590
94
                LargeArc(false),
591
94
                Sweep::Positive,
592
                right_x,
593
                right_y1,
594
            );
595

            
596
94
            builder.line_to(right_x, right_y2);
597

            
598
94
            builder.arc(
599
                right_x,
600
                right_y2,
601
94
                rx,
602
94
                ry,
603
                0.0,
604
94
                LargeArc(false),
605
94
                Sweep::Positive,
606
                bottom_x2,
607
                bottom_y,
608
            );
609

            
610
94
            builder.line_to(bottom_x1, bottom_y);
611

            
612
94
            builder.arc(
613
                bottom_x1,
614
                bottom_y,
615
94
                rx,
616
94
                ry,
617
                0.0,
618
94
                LargeArc(false),
619
94
                Sweep::Positive,
620
                left_x,
621
                left_y2,
622
            );
623

            
624
94
            builder.line_to(left_x, left_y1);
625

            
626
94
            builder.arc(
627
                left_x,
628
                left_y1,
629
94
                rx,
630
94
                ry,
631
                0.0,
632
94
                LargeArc(false),
633
94
                Sweep::Positive,
634
                top_x1,
635
                top_y,
636
            );
637
        }
638

            
639
946645
        builder.close_path();
640

            
641
946640
        ShapeDef::new(Rc::new(builder.into_path()), Markers::No)
642
947771
    }
643
}
644

            
645
/// The `<circle>` element.
646
///
647
/// Note that its cx/cy/r are properties in SVG2, so they are
648
/// defined as part of [the properties machinery](properties.rs).
649
339
#[derive(Default)]
650
pub struct Circle {}
651

            
652
impl ElementTrait for Circle {
653
    impl_draw!();
654
}
655

            
656
impl BasicShape for Circle {
657
254
    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
658
254
        let cx = values.cx().0.to_user(params);
659
254
        let cy = values.cy().0.to_user(params);
660
254
        let r = values.r().0.to_user(params);
661

            
662
254
        ShapeDef::new(Rc::new(make_ellipse(cx, cy, r, r)), Markers::No)
663
254
    }
664
}
665

            
666
/// The `<ellipse>` element.
667
///
668
/// Note that its cx/cy/rx/ry are properties in SVG2, so they are
669
/// defined as part of [the properties machinery](properties.rs).
670
20
#[derive(Default)]
671
pub struct Ellipse {}
672

            
673
impl ElementTrait for Ellipse {
674
    impl_draw!();
675
}
676

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

            
690
        let rx;
691
        let ry;
692

            
693
21
        match (norm_rx, norm_ry) {
694
1
            (None, None) => {
695
1
                rx = 0.0;
696
1
                ry = 0.0;
697
            }
698

            
699
1
            (Some(_rx), None) => {
700
1
                rx = _rx;
701
1
                ry = _rx;
702
1
            }
703

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

            
709
18
            (Some(_rx), Some(_ry)) => {
710
18
                rx = _rx;
711
18
                ry = _ry;
712
18
            }
713
        }
714

            
715
21
        ShapeDef::new(Rc::new(make_ellipse(cx, cy, rx, ry)), Markers::No)
716
21
    }
717
}
718

            
719
#[cfg(test)]
720
mod tests {
721
    use super::*;
722

            
723
    #[test]
724
2
    fn parses_points() {
725
2
        assert_eq!(
726
1
            Points::parse_str(" 1 2 ").unwrap(),
727
1
            Points(vec![(1.0, 2.0)])
728
        );
729
2
        assert_eq!(
730
1
            Points::parse_str("1 2 3 4").unwrap(),
731
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
732
        );
733
2
        assert_eq!(
734
1
            Points::parse_str("1,2,3,4").unwrap(),
735
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
736
        );
737
2
        assert_eq!(
738
1
            Points::parse_str("1,2 3,4").unwrap(),
739
1
            Points(vec![(1.0, 2.0), (3.0, 4.0)])
740
        );
741
2
        assert_eq!(
742
1
            Points::parse_str("1,2 -3,4").unwrap(),
743
1
            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
744
        );
745
2
        assert_eq!(
746
1
            Points::parse_str("1,2,-3,4").unwrap(),
747
1
            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
748
        );
749
2
    }
750

            
751
    #[test]
752
2
    fn errors_on_invalid_points() {
753
1
        assert!(Points::parse_str("-1-2-3-4").is_err());
754
1
        assert!(Points::parse_str("1 2-3,-4").is_err());
755
2
    }
756
}