1
//! Plot line charts
2

3
//! # Examples
4

5
//! ```
6
//! # use plotlib::repr::Plot;
7
//! # use plotlib::view::ContinuousView;
8
//! // y=x^2 between 0 and 10
9
//! let l = Plot::new(vec![(0., 1.), (2., 1.5), (3., 1.2), (4., 1.1)]);
10
//! let v = ContinuousView::new().add(l);
11
//! ```
12

13
use std::f64;
14

15
use svg;
16
use svg::node;
17
use svg::Node;
18

19
use crate::axis;
20
use crate::repr::ContinuousRepresentation;
21
use crate::style::*;
22
use crate::svg_render;
23

24
/// Representation of any plot with points in the XY plane, visualized as points and/or with lines
25
/// in-between.
26
#[derive(Debug, Clone)]
27
pub struct Plot {
28
    pub data: Vec<(f64, f64)>,
29
    /// None if no lines should be displayed
30
    pub line_style: Option<LineStyle>,
31
    /// None if no points should be displayed
32
    pub point_style: Option<PointStyle>,
33
    pub legend: Option<String>,
34
}
35

36
impl Plot {
37 1
    pub fn new(data: Vec<(f64, f64)>) -> Self {
38
        Plot {
39
            data,
40
            line_style: None,
41
            point_style: None,
42
            legend: None,
43
        }
44
    }
45

46 0
    pub fn from_function<F: Fn(f64) -> f64>(f: F, lower: f64, upper: f64) -> Self {
47 0
        let sampling = (upper - lower) / 200.;
48 0
        let samples = (0..)
49 0
            .map(|x| lower + (f64::from(x) * sampling))
50 0
            .take_while(|&x| x <= upper);
51 0
        let values = samples.map(|s| (s, f(s))).collect();
52
        Plot {
53
            data: values,
54
            line_style: None,
55
            point_style: None,
56
            legend: None,
57
        }
58
    }
59

60 0
    pub fn line_style(mut self, other: LineStyle) -> Self {
61 0
        if let Some(ref mut self_style) = self.line_style {
62 0
            self_style.overlay(&other);
63
        } else {
64 0
            self.line_style = Some(other);
65
        }
66 0
        self
67
    }
68 1
    pub fn point_style(mut self, other: PointStyle) -> Self {
69 1
        if let Some(ref mut self_style) = self.point_style {
70 0
            self_style.overlay(&other);
71
        } else {
72 1
            self.point_style = Some(other);
73
        }
74 1
        self
75
    }
76 0
    pub fn legend(mut self, legend: String) -> Self {
77 0
        self.legend = Some(legend);
78 0
        self
79
    }
80

81 1
    fn x_range(&self) -> (f64, f64) {
82 1
        let mut min = f64::INFINITY;
83 1
        let mut max = f64::NEG_INFINITY;
84 1
        for &(x, _) in &self.data {
85 1
            min = min.min(x);
86 1
            max = max.max(x);
87
        }
88 1
        (min, max)
89
    }
90

91 1
    fn y_range(&self) -> (f64, f64) {
92 1
        let mut min = f64::INFINITY;
93 1
        let mut max = f64::NEG_INFINITY;
94 1
        for &(_, y) in &self.data {
95 1
            min = min.min(y);
96 1
            max = max.max(y);
97
        }
98 1
        (min, max)
99
    }
100
}
101

102
impl ContinuousRepresentation for Plot {
103 1
    fn range(&self, dim: u32) -> (f64, f64) {
104 1
        match dim {
105 1
            0 => self.x_range(),
106 1
            1 => self.y_range(),
107 0
            _ => panic!("Axis out of range"),
108
        }
109
    }
110

111 1
    fn to_svg(
112
        &self,
113
        x_axis: &axis::ContinuousAxis,
114
        y_axis: &axis::ContinuousAxis,
115
        face_width: f64,
116
        face_height: f64,
117
    ) -> svg::node::element::Group {
118 1
        let mut group = node::element::Group::new();
119 1
        if let Some(ref line_style) = self.line_style {
120 0
            group.append(svg_render::draw_face_line(
121 0
                &self.data,
122 0
                x_axis,
123 0
                y_axis,
124 0
                face_width,
125 0
                face_height,
126 0
                line_style,
127
            ))
128
        }
129 1
        if let Some(ref point_style) = self.point_style {
130 1
            group.append(svg_render::draw_face_points(
131 1
                &self.data,
132 1
                x_axis,
133 1
                y_axis,
134 1
                face_width,
135 1
                face_height,
136 1
                point_style,
137
            ))
138
        }
139 1
        group
140
    }
141 1
    fn legend_svg(&self) -> Option<svg::node::element::Group> {
142
        // TODO: add points
143
        // TODO: can we use common functionality with svg_render?
144 1
        self.legend.as_ref().map(|legend| {
145 0
            let legend = legend.clone();
146

147 0
            let mut group = node::element::Group::new();
148 0
            const FONT_SIZE: f32 = 12.0;
149

150
            // Draw legend text
151 0
            let legend_text = node::element::Text::new()
152 0
                .set("x", 0)
153 0
                .set("y", 0)
154 0
                .set("text-anchor", "start")
155 0
                .set("font-size", FONT_SIZE)
156 0
                .add(node::Text::new(legend));
157 0
            group.append(legend_text);
158

159 0
            if let Some(ref style) = self.line_style {
160 0
                let line = node::element::Line::new()
161 0
                    .set("x1", -10)
162 0
                    .set("y1", -FONT_SIZE / 2. + 2.)
163 0
                    .set("x2", -3)
164 0
                    .set("y2", -FONT_SIZE / 2. + 2.)
165 0
                    .set("stroke-width", style.get_width())
166 0
                    .set("stroke", style.get_colour());
167 0
                group.append(line);
168
            }
169

170 0
            group
171
        })
172
    }
173

174 0
    fn to_text(
175
        &self,
176
        _x_axis: &axis::ContinuousAxis,
177
        _y_axis: &axis::ContinuousAxis,
178
        _face_width: u32,
179
        _face_height: u32,
180
    ) -> String {
181 0
        "".into()
182
    }
183
}

Read our documentation on viewing source code .

Loading