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
use crate::text_render;
24

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

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

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

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

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

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

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

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

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

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

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

171 0
            group
172
        })
173
    }
174

175 0
    fn to_text(
176
        &self,
177
        x_axis: &axis::ContinuousAxis,
178
        y_axis: &axis::ContinuousAxis,
179
        face_width: u32,
180
        face_height: u32,
181
    ) -> String {
182 0
        let face_lines = if let Some(line_style) = &self.line_style {
183
            unimplemented!("Text rendering does not yet support line plots")
184
        } else {
185 0
            text_render::empty_face(face_width, face_height)
186
        };
187 0
        let face_points = if let Some(point_style) = &self.point_style {
188
            text_render::render_face_points(
189 0
                &self.data,
190 0
                x_axis,
191 0
                y_axis,
192 0
                face_width,
193 0
                face_height,
194 0
                &point_style,
195
            )
196
        } else {
197 0
            text_render::empty_face(face_width, face_height)
198
        };
199 0
        text_render::overlay(&face_lines, &face_points, 0, 0)
200
    }
201
}

Read our documentation on viewing source code .

Loading