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
// Traits
pub use crate::traits::{Configurable, Plotable, Saveable};
use core::fmt::Display;

/// 3-dimensional surface by plotting constant z slices, called contours, on a 2-dimensional format.
///
/// Check out [`Contour` documentation] of gnuplot for more options.
///
///
/// # Examples
///
/// Quick plot.
/// ```no_run
/// use itertools::iproduct;
/// use preexplorer::prelude::*;
/// let values = iproduct!(0..10, 0..5).map(|(x, y)| x + y);
/// pre::Contour::new(0..10, 0..5, values).plot("my_identifier").unwrap();
/// ```
///
/// [`Contour` documentation]: http://gnuplot.info/docs_5.5/loc10902.html
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "use-serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Contour<T, S, U>
where
    T: Display + Clone,
    S: Display + Clone,
    U: Display + Clone,
{
    xs: Vec<T>,
    ys: Vec<S>,
    values: Vec<U>,
    config: crate::configuration::Configuration,
}

impl<T, S, U> Contour<T, S, U>
where
    T: Display + Clone,
    S: Display + Clone,
    U: Display + Clone,
{
    /// Constructs a new ``Contour<T, S, U>``.
    ///
    /// # Panics
    ///
    /// The number of values must be equal to the dimension of the grid
    /// given by the cartesian product of ``xs`` and ``ys``.
    ///
    /// # Examples
    ///
    /// From a complicated computation.
    /// ```
    /// # use itertools::iproduct;
    /// use preexplorer::prelude::*;
    /// let values = iproduct!(0..10, 0..5).map(|(x, y)| x + y);
    /// let heatmap = pre::Contour::new(0..10, 0..5, values);
    /// ```
    pub fn new<I, J, K>(xs: I, ys: J, values: K) -> Contour<T, S, U>
    where
        I: IntoIterator<Item = T>,
        J: IntoIterator<Item = S>,
        K: IntoIterator<Item = U>,
    {
        let xs: Vec<T> = xs.into_iter().collect();
        let ys: Vec<S> = ys.into_iter().collect();
        let values: Vec<U> = values.into_iter().collect();

        debug_assert!(
            xs.len() * ys.len() == values.len(),
            "The numbers of values ({}) does not match the grid ({}x{})",
            values.len(),
            xs.len(),
            ys.len()
        );

        let mut config = crate::configuration::Configuration::default();
        config.set_style("lines").unwrap();

        Contour {
            xs,
            ys,
            values,
            config,
        }
    }
}


impl<T, S, U> Configurable for Contour<T, S, U>
where
    T: Display + Clone,
    S: Display + Clone,
    U: Display + Clone,
{
    fn configuration_mut(&mut self) -> &mut crate::configuration::Configuration {
        &mut self.config
    }
    fn configuration(&self) -> &crate::configuration::Configuration {
        &self.config
    }
}

impl<T, S, U> Saveable for Contour<T, S, U>
where
    T: Display + Clone,
    S: Display + Clone,
    U: Display + Clone,
{
    fn plotable_data(&self) -> String {
        // Initial warning
        if self.xs.is_empty() {
            eprintln!("Warning: There is no data.");
        }

        let mut plotable_data = String::new();
        for i in 0..self.xs.len() {
            for j in 0..self.ys.len() {
                plotable_data.push_str(&format!(
                    "{}\t{}\t{}\n",
                    self.xs[i],
                    self.ys[j],
                    self.values[i * self.ys.len() + j]
                ));
            }
            plotable_data.push_str("\n");
        }
        plotable_data
    }
}

impl<T, S, U> Plotable for Contour<T, S, U>
where
    T: Display + Clone,
    S: Display + Clone,
    U: Display + Clone,
{
    fn plot_script(&self) -> String {
        let mut gnuplot_script = self.opening_plot_script();

        gnuplot_script += "set surface # unset to plot only isolines\n";
        gnuplot_script += "set contour\n";
        gnuplot_script += &format!("splot {:?} using 1:2:3 with {}\n", self.data_path(), self.style());
        gnuplot_script += &self.ending_plot_script();

        gnuplot_script
    }
}

impl<T> From<ndarray::Array2<T>> for Contour<usize, usize, T>
where
    T: Display + Clone,
{
    fn from(array: ndarray::Array2<T>) -> Self {
        let shape = array.shape();

        let xs: Vec<usize> = (0..shape[0]).collect();
        let ys: Vec<usize> = (0..shape[1]).rev().collect();
        let values: Vec<T> = array.t().iter().cloned().collect();

        Contour::new(xs, ys, values)
    }
}