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
 95
 96
 97
 98
 99
100
101
102
103
//! Chart functionality!

use super::axis::Axis;
use super::curve::Curve;
use crate::tsdb::{Aggregation, Sample, SampleMetrics};

/// A single 2D-chart
#[derive(Clone)]
pub struct Chart {
    /// An optional title for the plot
    pub title: Option<String>,

    pub x_axis: Axis,
    pub y_axis: Axis,

    /// To show grid or not.
    pub grid: bool,

    /// The curves in the plot
    pub curves: Vec<Curve>,
}

impl Default for Chart {
    fn default() -> Self {
        Chart {
            title: None,
            x_axis: Axis::default(),
            y_axis: Axis::default(),
            grid: true,
            curves: vec![],
        }
    }
}

impl Chart {
    /// Set the title of the chart!
    pub fn set_title(&mut self, title: &str) {
        self.title = Some(title.to_string());
    }

    pub fn set_xlabel(&mut self, label: &str) {
        self.x_axis.label = Some(label.to_string());
    }

    pub fn set_ylabel(&mut self, label: &str) {
        self.y_axis.label = Some(label.to_string());
    }

    /// Drop a new curve into the mix!
    pub fn add_curve(&mut self, curve: Curve) {
        self.curves.push(curve);
    }

    /// Zoom horizontally.
    pub fn zoom_horizontal(&mut self, amount: f64) {
        let domain = self.x_axis.domain();
        let step = domain * amount;
        let x1 = self.x_axis.begin() - step;
        let x2 = self.x_axis.end() + step;
        self.x_axis.set_limits(x1, x2);
    }

    /// Perform vertical zooming
    pub fn zoom_vertical(&mut self, amount: f64) {
        let domain = self.y_axis.domain();
        let step = domain * amount;
        let y1 = self.y_axis.begin() - step;
        let y2 = self.y_axis.end() + step;
        self.y_axis.set_limits(y1, y2);
    }

    /// Perform a bit of horizontal panning
    pub fn pan_horizontal(&mut self, amount: f64) {
        let domain = self.x_axis.domain();
        let step = domain * amount;
        let x1 = self.x_axis.begin() + step;
        let x2 = self.x_axis.end() + step;
        self.x_axis.set_limits(x1, x2);
    }

    /// Perform vertical pan motion on the plot.
    pub fn pan_vertical(&mut self, amount: f64) {
        let domain = self.y_axis.domain();
        let step = domain * amount;
        let y1 = self.y_axis.begin() + step;
        let y2 = self.y_axis.end() + step;
        self.y_axis.set_limits(y1, y2);
    }

    /// Adjust scale ranges so we fit all data in view.
    pub fn autoscale(&mut self) {
        // Retrieve info from all curves:
        let summaries: Vec<Aggregation<Sample, SampleMetrics>> =
            self.curves.iter().filter_map(|c| c.summary()).collect();

        if let Some(summary) = Aggregation::from_aggregations(&summaries) {
            self.x_axis
                .set_limits(summary.timespan.start.amount, summary.timespan.end.amount);
            self.y_axis
                .set_limits(summary.metrics().min, summary.metrics().max);
        }
    }
}