1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! SVG [`Output`] for `Views`

use svg::node::element::path::{Command, Position};
use svg::{node::element::path::Data, node::element::Path, Document, Node};

use crate::views::Transform;

///
pub struct Output {
    svg: Document,
    transform: Transform,
    data: Data,
    rgba: [u8; 4],
}

impl Output {
    /// Creates a Scalable Vector Graphics `Output`.
    pub fn new(width: f32, height: f32) -> Self {
        Self {
            svg: Document::new()
                .set("viewBox", (0, 0, width, height))
                .set("width", width)
                .set("height", height),
            transform: Default::default(),
            data: Default::default(),
            rgba: [0; 4],
        }
    }

    fn end_current_node(&mut self) {
        let data = std::mem::take(&mut self.data);

        let fill = format!(
            "#{:02x}{:02x}{:02x}{:02x}",
            self.rgba[0], self.rgba[1], self.rgba[2], self.rgba[3]
        );

        let array = self.transform.to_array();
        let transform = format!(
            "matrix({} {} {} {} {} {})",
            array[0], array[1], array[2], array[3], array[4], array[5]
        );

        self.svg.append(
            Path::new()
                .set("transform", transform)
                .set("fill", fill)
                .set("d", data),
        );
    }

    /// Consumes the `Output` and returns the constructed SVG string.
    pub fn into_inner(mut self) -> String {
        self.end_current_node();
        self.svg.to_string()
    }
}

impl super::Output for Output {
    fn begin(&mut self, x: f32, y: f32, rgba: [u8; 4], transform: &Transform) {
        if !self.data.is_empty() && (rgba != self.rgba || !transform.approx_eq(&self.transform)) {
            self.end_current_node();
        }

        self.rgba = rgba;
        self.transform = *transform;

        self.data
            .append(Command::Move(Position::Absolute, (x, y).into()));
    }

    fn line_to(&mut self, x: f32, y: f32) {
        self.data
            .append(Command::Line(Position::Absolute, (x, y).into()));
    }

    fn quadratic_bezier_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
        self.data.append(Command::QuadraticCurve(
            Position::Absolute,
            (x1, y1, x, y).into(),
        ));
    }

    fn cubic_bezier_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
        self.data.append(Command::CubicCurve(
            Position::Absolute,
            (x1, y1, x2, y2, x, y).into(),
        ));
    }

    fn close(&mut self) {
        self.data.append(Command::Close);
    }
}