1extern crate tempfile;
9extern crate image;
10extern crate base64;
11
12use std::fs::File;
13use std::fs;
14use std::io::{Write, Read, Seek, SeekFrom};
15use std::process::Command;
16use tempfile::NamedTempFile;
17use std::fmt;
18use image::GenericImage;
19use image::Pixels;
20use image::Pixel;
21use base64::encode;
22
23#[cfg(test)]
24#[macro_use]
25extern crate probability;
26
27pub enum Markers {
28 Point,
29 Pixel,
30 Circle,
31 Triangle_Down,
32 Triangle_Up,
33 Triangle_Left,
34 Triangle_Right,
35 Tri_Down,
36 Tri_Up,
37 Tri_Left,
38 Tri_Right,
39 Square,
40 Pentagon,
41 Star,
42 Hexagon1,
43 Hexagon2,
44 Plus,
45 X ,
46 Diamond,
47 Thin_Diamond,
48 VLine,
49 HLine,
50}
51
52trait AsString {
53 fn as_str(&self) -> &str;
54}
55
56impl Markers {
57 pub fn as_str(&self) -> &str {
58 match self {
59 &Markers::Point => ".", &Markers::Pixel => ",", &Markers::Circle => "o", &Markers::Triangle_Down => "v", &Markers::Triangle_Up => "^", &Markers::Triangle_Left => "<", &Markers::Triangle_Right => ">", &Markers::Tri_Down => "1", &Markers::Tri_Up => "2", &Markers::Tri_Left => "3", &Markers::Tri_Right => "4", &Markers::Square => "s", &Markers::Pentagon => "p", &Markers::Star => "*", &Markers::Hexagon1 => "h", &Markers::Hexagon2 => "H", &Markers::Plus => "+", &Markers::X => "x", &Markers::Diamond => "D", &Markers::Thin_Diamond => "d", &Markers::VLine => "|", &Markers::HLine => "_", }
82 }
83}
84
85pub enum LineStyle {
86 Dot,
87 DashDot,
88 Dash,
89 Fill,
90}
91
92impl LineStyle {
93 pub fn as_str(&self) -> &str {
94 match self {
95 &LineStyle::Dot => ":",
96 &LineStyle::DashDot => "-.",
97 &LineStyle::Dash => "--",
98 &LineStyle::Fill => "-",
99 }
100 }
101}
102
103pub struct Figure {
104 script: String
105}
106
107impl Figure {
108
109 pub fn new() -> Figure {
110 return Figure {
111 script: "import matplotlib\nmatplotlib.use('agg')\nimport matplotlib.pyplot as plt\nfrom matplotlib.lines import Line2D\n\n".to_string()
112 }
113 }
114
115 pub fn add_plot(&mut self, p: String) {
116 self.script += &p;
117 }
118
119 pub fn save(&mut self, output: &str, path: Option<&str>) -> (String, String, String) {
120 self.script += &format!("plt.savefig('{}')\n", output);
121 let mut tmpfile: NamedTempFile = tempfile::NamedTempFile::new().unwrap();
123 tmpfile.write_all(self.script.as_bytes());
124
125 let python_path = match path {
126 Some(s) => s,
127 None => "/usr/local/bin/python3"
128 };
129
130 fs::metadata(python_path).expect("python binary not found at /usr/local/bin/python3");
131 let mut echo_hello = Command::new(python_path);
132 echo_hello.arg(tmpfile.path());
133 echo_hello.output().expect("failed to execute process");
134
135 return (self.script.to_string(), output.to_string(), tmpfile.path().to_str().unwrap().to_string());
136 }
137
138 pub fn as_base64(&mut self, path: Option<&str>) {
139 let mut tmpfile: NamedTempFile = tempfile::NamedTempFile::new().unwrap();
141 let filename = tmpfile.path().to_str().unwrap().to_string() + &".png".to_string();
142 println!("tmpfile = {:?}", filename);
143 self.save(&filename, path);
144 let mut buffer = Vec::new();
146 let mut file = File::open(&filename).unwrap();
147 let _out = file.read_to_end(&mut buffer);
148 println!("EVCXR_BEGIN_CONTENT image/png\n{}\nEVCXR_END_CONTENT", base64::encode(&buffer));
149 }
150
151}
152
153pub struct LinePlotOptions {
154 pub marker: Option<Markers>,
155 pub lineStyle: Option<LineStyle>,
156 pub colour: Option<String>
157}
158
159impl LinePlotOptions {
160 pub fn new() -> LinePlotOptions {
161 return LinePlotOptions {
162 marker: None,
163 lineStyle: None,
164 colour: None
165 }
166 }
167}
168
169pub struct ScatterPlotOptions {
170 pub marker: Option<Markers>,
171 pub alpha: Option<f64>,
172}
173
174impl ScatterPlotOptions {
175 pub fn new() -> ScatterPlotOptions {
176 return ScatterPlotOptions {
177 marker: None,
178 alpha: None,
179 }
180 }
181}
182
183impl fmt::Display for LinePlotOptions {
184 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
185 let mut options : Vec<String> = Vec::new();
186 match self.marker {
187 Some(ref m) => {options.push(format!("marker='{}'", m.as_str()));}
188 None => {}
189 }
190 match self.lineStyle {
191 Some(ref v) => {options.push(format!("linestyle='{}'", v.as_str()));}
192 None => {}
193 }
194 match self.colour {
195 Some(ref v) => {options.push(format!("c='{}'", v.as_str()));}
196 None => {}
197 }
198 fmt.write_str(&options.join(", "));
199 Ok(())
200 }
201}
202
203impl fmt::Display for ScatterPlotOptions {
204 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
205 let mut options : Vec<String> = Vec::new();
206 match self.marker {
207 Some(ref m) => {options.push(format!("marker='{}'", m.as_str()));}
208 None => {}
209 }
210 match self.alpha {
211 Some(ref m) => {options.push(format!("alpha={}", m.to_string()));}
212 None => {}
213 }
214 fmt.write_str(&options.join(", "));
215 Ok(())
216 }
217}
218
219fn convert_list_str<T>(x: Vec<T>) -> String where T: ToString {
220 let mut result:Vec<String> = Vec::new();
221 for _x in x { result.push(_x.to_string()); }
222 return "[".to_string() + &result.join(", ") + &"]".to_string();
223}
224
225pub fn line_plot<U, T>(x: Vec<U>, y: Vec<T>, options: Option<LinePlotOptions>) -> String where U: ToString, T: ToString {
226 let xs = convert_list_str::<U>(x);
227 let ys = convert_list_str::<T>(y);
228 match options {
229 Some(opt) => {
230 return format!("plt.plot({},{},{})\n", xs, ys, opt);
231 },
232 None => {
233 return format!("plt.plot({},{})\n", xs, ys);
234 }
235 }
236}
237
238pub fn scatter_plot<U, T>(x: Vec<U>, y: Vec<T>, options: Option<ScatterPlotOptions>) -> String where U: ToString, T: ToString {
239 let xs = convert_list_str::<U>(x);
240 let ys = convert_list_str::<T>(y);
241 match options {
242 Some(opt) => {
243 return format!("plt.scatter({},{},{})\n", xs, ys, opt);
244 },
245 None => {
246 return format!("plt.scatter({},{})\n", xs, ys);
247 }
248 }
249
250}
251
252pub fn histogram<U>(x: Vec<U>, bins: Option<u32>) -> String where U: ToString {
253 let xs = convert_list_str::<U>(x);
254 return format!("plt.hist({}, bins={})\n", xs, bins.unwrap_or(20));
255}
256
257pub fn horizontal_line<U>(y: U, options: Option<LinePlotOptions>) -> String where U: ToString {
258 match options {
259 Some(opt) => {
260 return format!("plt.axhline(y={},{})\n", y.to_string(), opt);
261 },
262 None => {
263 return format!("plt.axhline(x={})\n", y.to_string());
264 }
265 }
266}
267
268pub fn vertical_line<U>(x: U, options: Option<LinePlotOptions>) -> String where U: ToString {
269 match options {
270 Some(opt) => {
271 return format!("plt.axvline(x={},{})\n", x.to_string(), opt);
272 },
273 None => {
274 return format!("plt.axvline(x={})\n", x.to_string());
275 }
276 }
277}
278
279pub fn line<U, T>(start : (U, T), end : (U, T), options : Option<LinePlotOptions>) -> String where U : ToString, T : ToString {
280 match options {
281 Some(opt) => {
282 return format!("ax=plt.gca()\nax.add_line(Line2D([{},{}],[{},{}],{}))\n", start.0.to_string(), end.0.to_string(), start.1.to_string(), end.1.to_string(), opt);
283 },
284 None => {
285 return format!("ax=plt.gca()\nax.add_line(Line2D([{},{}],[{},{}]))\n", start.0.to_string(), end.0.to_string(), start.1.to_string(), end.1.to_string());
286 }
287 }
288}
289
290#[cfg(test)]
291mod lineplot {
292 use super::*;
293 use probability::distribution::Gaussian;
294 use probability::sampler::Independent;
295 use probability::source;
296 use ::LineStyle::Dash;
297
298 #[test]
299 fn create_lineplot_basic() {
300 let x = vec![1, 2, 3, 4];
301 let y = vec![0.1, 0.2, 0.5, 0.3];
302 let lp = line_plot::<i32, f64>(x, y, None);
303 let mut figure = Figure::new();
304 figure.add_plot(lp);
305 figure.save("./examples/figures/lineplot_basic.png", None);
306 }
307
308 #[test]
309 fn create_lineplot_basic_markers() {
310 let x = vec![1, 2, 3, 4];
311 let y = vec![0.1, 0.2, 0.5, 0.3];
312 let mut options = LinePlotOptions::new();
313 options.marker = Some(Markers::Diamond);
314 let lp = line_plot::<i32, f64>(x, y, Some(options));
315 let mut figure = Figure::new();
316 figure.add_plot(lp);
317 figure.save("./examples/figures/lineplot_basic_markers.png", None);
318 }
319
320 #[test]
321 fn create_lineplot_basic_linestyle() {
322 let x = vec![1, 2, 3, 4];
323 let y = vec![0.1, 0.2, 0.5, 0.3];
324 let mut options = LinePlotOptions::new();
325 options.marker = Some(Markers::Diamond);
326 options.lineStyle = Some(LineStyle::DashDot);
327 let lp = line_plot::<i32, f64>(x, y, Some(options));
328 let mut figure = Figure::new();
329 figure.add_plot(lp);
330 figure.save("./examples/figures/lineplot_basic_linestyle.png", None);
331 }
332
333 #[test]
334 fn create_scatterplot_basic() {
335 let x = vec![1, 2, 3, 4];
336 let y = vec![0.1, 0.2, 0.5, 0.3];
337 let lp = scatter_plot::<i32, f64>(x, y, None);
338 let mut figure = Figure::new();
339 figure.add_plot(lp);
340 figure.save("./examples/figures/scatterplot_basic.png", None);
341 }
342
343 #[test]
344 fn create_scatterplot_marker() {
345 let x = vec![1, 2, 3, 4];
346 let y = vec![0.1, 0.2, 0.5, 0.3];
347 let mut options = ScatterPlotOptions::new();
348 options.marker = Some(Markers::Diamond);
349 let lp = scatter_plot::<i32, f64>(x, y, Some(options));
350 let mut figure = Figure::new();
351 figure.add_plot(lp);
352 figure.save("./examples/figures/scatterplot_marker.png", None);
353 }
354 #[test]
355 fn create_scatterplot_marker_alpha() {
356 let x = vec![1, 2, 3, 4];
357 let y = vec![0.1, 0.2, 0.5, 0.3];
358 let mut options = ScatterPlotOptions::new();
359 options.marker = Some(Markers::Diamond);
360 options.alpha = Some(0.1);
361 let lp = scatter_plot::<i32, f64>(x, y, Some(options));
362 let mut figure = Figure::new();
363 figure.add_plot(lp);
364 figure.save("./examples/figures/scatterplot_marker_alpha.png", None);
365 }
366
367 #[test]
368 fn create_histogram_default_bins() {
369 let mut source = source::default();
370 let gaussian = Gaussian::new(0.0, 2.0);
371 let mut sampler = Independent(&gaussian, &mut source);
372 let x = sampler.take(500).collect::<Vec<_>>();
373 let plot = histogram::<f64>(x, None);
374 let mut figure = Figure::new();
375 figure.add_plot(plot);
376 figure.save("./examples/figures/histogram_default_bins.png", None);
377 }
378
379 #[test]
380 fn create_histogram_custom_bins() {
381 let mut source = source::default();
382 let gaussian = Gaussian::new(0.0, 2.0);
383 let mut sampler = Independent(&gaussian, &mut source);
384 let x = sampler.take(500).collect::<Vec<_>>();
385 let plot = histogram::<f64>(x, Some(100));
386 let mut figure = Figure::new();
387 figure.add_plot(plot);
388 figure.save("./examples/figures/histogram_custom_bins.png", None);
389 }
390
391 #[test]
392 fn create_histogram_to_b64() {
393 let mut source = source::default();
394 let gaussian = Gaussian::new(0.0, 2.0);
395 let mut sampler = Independent(&gaussian, &mut source);
396 let x = sampler.take(500).collect::<Vec<_>>();
397 let plot = histogram::<f64>(x, Some(100));
398 let mut figure = Figure::new();
399 figure.add_plot(plot);
400 figure.as_base64(None);
401 }
402
403 fn mean(numbers: &Vec<f64>) -> f64 {
404
405 let sum: f64 = numbers.iter().sum();
406
407 sum / numbers.len() as f64
408
409 }
410
411 #[test]
412 fn create_histogram_vertical_mean() {
413 let mut source = source::default();
414 let gaussian = Gaussian::new(0.0, 2.0);
415 let mut sampler = Independent(&gaussian, &mut source);
416 let x = sampler.take(500).collect::<Vec<_>>();
417 let plot = histogram::<f64>(x.clone(), Some(100));
418 let mut figure = Figure::new();
419 figure.add_plot(plot);
420 let mean = mean(&(x.clone()));
421 let mut mean_line_opts = LinePlotOptions::new();
422 mean_line_opts.lineStyle = Some(Dash);
423 mean_line_opts.colour = Some("black".to_string());
424 let mean_line = vertical_line(mean, Some(mean_line_opts));
425 figure.add_plot(mean_line);
426 figure.save("./examples/figures/histogram_mean_line.png", None);
427 }
428
429 #[test]
430 fn create_horizontal_line() {
431 let mut source = source::default();
432 let gaussian = Gaussian::new(0.0, 2.0);
433 let mut sampler = Independent(&gaussian, &mut source);
434 let x = (0..500).collect();
435 let y = sampler.take(500).collect::<Vec<_>>();
436 let plot = scatter_plot::<i32, f64>(x, y.clone(), None);
437 let mut figure = Figure::new();
438 figure.add_plot(plot);
439 let mean = mean(&(y.clone()));
440 let mut mean_line_opts = LinePlotOptions::new();
441 mean_line_opts.lineStyle = Some(Dash);
442 mean_line_opts.colour = Some("black".to_string());
443 let mean_line = horizontal_line(mean, Some(mean_line_opts));
444 figure.add_plot(mean_line);
445 print!("{:?}", figure.save("./examples/figures/horizontal_line.png", None));
446 }
447
448 #[test]
449 fn line_segment() {
450 let x = vec![1, 2, 3, 4];
451 let y = vec![0.1, 0.2, 0.5, 0.3];
452 let mut options = ScatterPlotOptions::new();
453 options.marker = Some(Markers::Diamond);
454 let lp = scatter_plot::<i32, f64>(x, y, Some(options));
455 let mut figure = Figure::new();
456 figure.add_plot(lp);
457 let mut line_opt = LinePlotOptions::new();
458 line_opt.colour = Some("red".to_string());
459 line_opt.lineStyle = Some(LineStyle::Dash);
460 figure.add_plot(line::<i32, f64>((1, 0.1), (4, 0.3), Some(line_opt)));
461 figure.save("./examples/figures/line_segment.png", None);
462 print!("{:?}", figure.script);
463
464 }
465
466}