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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*!
The `view` module provides structures for showing data in various ways.
*/

use std;
use std::f64;

use svg;
use svg::Node;

use representation::Representation;
use axis;
use svg_render;
use text_render;

/// Standard 1-dimensional view with a continuous x-axis
#[derive(Default)]
pub struct View<'a> {
    pub representations: Vec<&'a Representation>,
    x_range: Option<axis::Range>,
    y_range: Option<axis::Range>,
    x_label: Option<String>,
    y_label: Option<String>,
}

impl<'a> View<'a> {
    /**
    Create an empty view
    */
    pub fn new() -> View<'a> {
        View {
            representations: vec![],
            x_range: None,
            y_range: None,
            x_label: None,
            y_label: None,
        }
    }

    /**
    Add a representation to the view
    */
    pub fn add(mut self, repr: &'a Representation) -> Self {
        self.representations.push(repr);
        self
    }

    /**
    Set the x range for the view
    */
    pub fn x_range(mut self, min: f64, max: f64) -> Self {
        self.x_range = Some(axis::Range::new(min, max));
        self
    }

    /**
    Set the y range for the view
    */
    pub fn y_range(mut self, min: f64, max: f64) -> Self {
        self.y_range = Some(axis::Range::new(min, max));
        self
    }

    pub fn x_label<T>(mut self, value: T) -> Self
    where
        T: Into<String>,
    {
        self.x_label = Some(value.into());
        self
    }

    pub fn y_label<T>(mut self, value: T) -> Self
    where
        T: Into<String>,
    {
        self.y_label = Some(value.into());
        self
    }

    fn default_x_range(&self) -> axis::Range {
        let mut x_min = f64::INFINITY;
        let mut x_max = f64::NEG_INFINITY;
        for repr in &self.representations {
            let (this_x_min, this_x_max) = repr.range(0);
            x_min = x_min.min(this_x_min);
            x_max = x_max.max(this_x_max);
        }
        axis::Range::new(x_min, x_max)
    }

    fn default_y_range(&self) -> axis::Range {
        let mut y_min = f64::INFINITY;
        let mut y_max = f64::NEG_INFINITY;
        for repr in &self.representations {
            let (this_y_min, this_y_max) = repr.range(1);
            y_min = y_min.min(this_y_min);
            y_max = y_max.max(this_y_max);
        }
        axis::Range::new(y_min, y_max)
    }

    fn create_axes(&self) -> (axis::Axis, axis::Axis) {
        let default_x_range = self.default_x_range();
        let x_range = self.x_range.as_ref().unwrap_or(&default_x_range);

        let default_y_range = self.default_y_range();
        let y_range = self.y_range.as_ref().unwrap_or(&default_y_range);

        let default_x_label = "".to_string();
        let x_label: String = self.x_label.clone().unwrap_or(default_x_label);

        let default_y_label = "".to_string();
        let y_label: String = self.y_label.clone().unwrap_or(default_y_label);

        let x_axis = axis::Axis::new(x_range.lower, x_range.upper).label(x_label);
        let y_axis = axis::Axis::new(y_range.lower, y_range.upper).label(y_label);

        (x_axis, y_axis)
    }

    /**
    Create an SVG rendering of the view
    */
    pub fn to_svg(&self, face_width: f64, face_height: f64) -> svg::node::element::Group {
        let mut view_group = svg::node::element::Group::new();

        let (x_axis, y_axis) = self.create_axes();

        // Then, based on those ranges, draw each repr as an SVG
        for repr in &self.representations {
            let repr_group = repr.to_svg(&x_axis, &y_axis, face_width, face_height);
            view_group.append(repr_group);
        }

        // Add in the axes
        view_group.append(svg_render::draw_x_axis(&x_axis, face_width));
        view_group.append(svg_render::draw_y_axis(&y_axis, face_height));
        view_group
    }

    /**
    Create a text rendering of the view
    */
    pub fn to_text(&self, face_width: u32, face_height: u32) -> String {
        let (x_axis, y_axis) = self.create_axes();

        let (y_axis_string, longest_y_label_width) =
            text_render::render_y_axis_strings(&y_axis, face_height);

        let (x_axis_string, start_offset) = text_render::render_x_axis_strings(&x_axis, face_width);

        let left_gutter_width = std::cmp::max(
            longest_y_label_width as i32 + 3,
            start_offset.wrapping_neg(),
        ) as u32;

        let view_width = face_width + 1 + left_gutter_width + 1;
        let view_height = face_height + 4;

        let blank: Vec<String> = (0..view_height)
            .map(|_| (0..view_width).map(|_| ' ').collect())
            .collect();
        let mut view_string = blank.join("\n");

        for repr in &self.representations {
            let face_string = repr.to_text(&x_axis, &y_axis, face_width, face_height);
            view_string =
                text_render::overlay(&view_string, &face_string, left_gutter_width as i32 + 1, 0);
        }

        let view_string = text_render::overlay(
            &view_string,
            &y_axis_string,
            left_gutter_width as i32 - 2 - longest_y_label_width,
            0,
        );
        let view_string = text_render::overlay(
            &view_string,
            &x_axis_string,
            left_gutter_width as i32,
            face_height as i32,
        );

        view_string
    }
}