plotter/plotter/
mod.rs

1///A Plotter Object that can plot the dataset
2#[derive(Debug)]
3pub struct Plotter {
4    dataset: Dataset,
5}
6
7use std::net::{TcpListener, TcpStream};
8use std::io::{Read, Write};
9
10impl Plotter {
11    /// Returns a new Plotter with the given dataset
12    /// 
13    /// # Example
14    /// ```
15    /// let p = Plotter::new(dataset)
16    /// ```
17    pub fn new(dataset: Dataset) -> Plotter {
18        Plotter { dataset }
19    }
20
21    /// Plots the dataset on a HTML Canvas and starts a server
22    /// 
23    /// # Arguments
24    /// * `size` - a tuple of u32 containing (width, height)
25    /// * `name` - title of the HTML Page (unimplemented)
26    /// * `port` - the port (on localhost) that the page opens to
27    /// * `options` - the options that the Plotter will use to plot
28    /// 
29    /// # Example
30    /// ```
31    /// //Plotter from above
32    /// p.plot((640,480),"hello world", String::from("8080"), PlotOptions::new());
33    /// ```
34    pub fn plot(&self, size: (u32, u32), name: &'static str, port: String, options: PlotOptions) {
35        let mut addr = String::from("127.0.0.1:");
36        addr.push_str(&port);
37        let listener = TcpListener::bind(&addr).expect(&format!("Error binding to {}", addr));
38
39        let mut content = format!("var canvas = document.getElementById('c');\ncanvas.height = {};\ncanvas.width = {};\nvar ctx = canvas.getContext('2d');", size.1 + 10, size.0);
40        content.push_str(&format!("ctx.fillStyle = '{}';\n", options.fg_colour.unwrap_or(String::from("black"))));
41
42        let data = self.dataset.data.clone();
43        
44        let max_x = data.clone().into_iter().fold(0.0, |x, acc| if x > acc.x {x} else {acc.x});
45        let min_x = data.clone().into_iter().fold(0.0, |x, acc| if x < acc.x {x} else {acc.x});
46        let max_y = data.clone().into_iter().fold(0.0, |y, acc| if y > acc.x {y} else {acc.y});
47        let min_y = data.clone().into_iter().fold(0.0, |y, acc| if y < acc.x {y} else {acc.y});
48
49        let x_range = max_x - min_x;
50        let y_range = max_y - min_y;
51
52        for point in data {
53            content.push_str(&format!("ctx.fillRect({},{},{});\n",format!("{},{}",
54                ((point.x - min_x)/x_range) * size.0 as f64, 
55                (size.1 as f64 - ((point.y - min_y)/y_range) * size.1 as f64)),
56                options.shape.unwrap_or((10.0,10.0)).0,options.shape.unwrap_or((10.0,10.0)).1
57                ));
58        }
59
60        for stream in listener.incoming() {
61            let stream = stream.unwrap();
62
63            self.handle_connection(stream, &content);
64        }
65    }
66
67    fn handle_connection(&self, mut stream: TcpStream, content: &String) {
68        let mut buffer = [0; 512];
69
70        stream.read(&mut buffer).expect("Error reading request");
71
72        let contents = format!(
73            "<!DOCTYPE html><html><head></head><body><canvas id='c'></canvas><script>{}</script></body></html>",content
74        );
75
76        let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents);
77
78        stream
79            .write(response.as_bytes())
80            .expect("Error Formatting response");
81        stream.flush().unwrap();
82    }
83}
84
85/// A dataset type that can be plotted
86#[derive(Clone,Debug)]
87pub struct Dataset {
88    data: Vec<Point>,
89}
90
91impl Dataset {
92    /// Returns a blank dataset
93    pub fn new() -> Dataset {
94        Dataset { data: Vec::new() }
95    }
96
97    /// Generates a dataset from a function
98    /// 
99    /// # Arguments
100    /// * `f` - a function that is `f(f64) -> f64`
101    /// * `range` - the start and end of the inputs
102    /// * `interval` - the interval in x value between the points
103    /// * `cap` - optional cap for min/max y value
104    /// 
105    /// # Example
106    /// ```
107    /// fn f(x:f64) -> f64 {
108    ///     x * 2
109    /// }
110    /// 
111    /// fn main()  {
112    ///     let data = Dataset::from_fn(f, (-10,10), 0.5, None);
113    /// }
114    pub fn from_fn(
115        f: &Fn(f64) -> f64,
116        range: (f64, f64),
117        interval: f64,
118        cap: Option<(f64, f64)>,
119    ) -> Dataset {
120        let mut data = Vec::new();
121        let mut c = range.0;
122        while c <= range.1 {
123            let prev_c = c;
124            let result = f(c);
125            c += interval;
126            if cap.is_some(){
127                if (cap.unwrap().0 > result) | (result > cap.unwrap().1) {
128                continue;
129                }
130            }
131            data.push(Point {
132                x: prev_c,
133                y: result,
134            });
135        }
136        Dataset { data }
137    }
138
139    /// Adds a point to the dataset
140    /// 
141    /// # Example
142    /// ```
143    /// // Dataset from above
144    /// data.add_point(p);
145    /// ```
146    pub fn add_point(&mut self, p: Point) {
147        self.data.push(p);
148    }
149    
150    /// Joins two datasets
151    ///
152    /// # Example
153    /// ```
154    /// // Dataset from above
155    /// fn o(x: f64) -> f64 {
156    ///     x
157    /// }
158    /// fn main() {
159    ///     // main from above
160    ///     let mut other = Dataset::from_fn(o, (-10,10), 0.5, None);
161    ///     o.join(data);
162    /// }
163    pub fn join(&mut self, mut other: Dataset) {
164        self.data.append(&mut other.data);
165    }
166}
167
168/// A point object that can be plotted
169#[derive(Copy,Clone,Debug)]
170pub struct Point {
171    x: f64,
172    y: f64,
173}
174
175impl Point {
176    /// Returns a new point at the x and y coordinates
177    pub fn new(x: f64, y: f64) -> Point {
178        Point { x, y }
179    }
180}
181
182/// A PlotOptions object used to specify the options for plotting
183/// 
184/// # Example
185/// ```
186/// let opt = PlotOptions::new()
187///     .with_fg(String::from("black"))
188///     .with_shape((5,5));
189/// ```
190pub struct PlotOptions {
191    bg_colour: Option<String>,
192    fg_colour: Option<String>,
193    shape: Option<(f64,f64)>,
194}
195
196impl PlotOptions {
197    /// Returns a blank `PlotOptions`
198    pub fn new() -> PlotOptions {
199        PlotOptions {
200            bg_colour: None,
201            fg_colour: None,
202            shape: None,
203        }
204    }
205
206    /// Sets the foreground colour
207    pub fn with_fg(&self, c:String) -> PlotOptions {
208        PlotOptions {
209            bg_colour: self.bg_colour.clone(),
210            fg_colour: Some(c),
211            shape: self.shape.clone(),
212        }
213    }
214
215    /// Sets the size of the point plotted
216    pub fn with_shape(&self, s:(f64,f64)) -> PlotOptions {
217        PlotOptions {
218            bg_colour: self.bg_colour.clone(),
219            fg_colour: self.fg_colour.clone(),
220            shape: Some(s)
221        }
222    }
223}