1extern crate pyo3;
80use self::pyo3::types::{IntoPyDict, PyDictMethods};
81use self::pyo3::{PyResult, Python};
82pub use self::Grid::{Off, On};
83use self::PlotOptions::{Domain, Images, Pairs, Path};
84use std::collections::HashMap;
85use std::fmt::Display;
86use std::borrow::BorrowMut;
87use std::ffi::CString;
88
89type Vector = Vec<f64>;
90
91#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
92pub enum PlotOptions {
93 Domain,
94 Images,
95 Pairs,
96 Legends,
97 Path,
98}
99
100#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
101pub enum Markers {
102 Point,
103 Circle,
104 Pixel,
105 TriangleDown,
106 TriangleUp,
107 TriangleLeft,
108 TriangleRight,
109 Square,
110 Pentagon,
111 Star,
112 Hexagon1,
113 Hexagon2,
114 Plus,
115 X,
116 Diamond,
117 ThinDiamond,
118 VLine,
119 HLine,
120}
121
122impl Display for Markers {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 let str = match self {
125 Markers::Point => ".".to_string(),
126 Markers::Circle => "o".to_string(),
127 Markers::Pixel => ",".to_string(),
128 Markers::TriangleDown => "v".to_string(),
129 Markers::TriangleUp => "^".to_string(),
130 Markers::TriangleLeft => "<".to_string(),
131 Markers::TriangleRight => ">".to_string(),
132 Markers::Square => "s".to_string(),
133 Markers::Pentagon => "p".to_string(),
134 Markers::Star => "*".to_string(),
135 Markers::Hexagon1 => "h".to_string(),
136 Markers::Hexagon2 => "H".to_string(),
137 Markers::Plus => "+".to_string(),
138 Markers::X => "x".to_string(),
139 Markers::Diamond => "D".to_string(),
140 Markers::ThinDiamond => "d".to_string(),
141 Markers::VLine => "|".to_string(),
142 Markers::HLine => "_".to_string(),
143 };
144 write!(f, "{}", str)
145 }
146}
147
148#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
149pub enum LineStyle {
150 Solid,
151 Dashed,
152 Dotted,
153 DashDot,
154}
155
156impl Display for LineStyle {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 let str = match self {
159 LineStyle::Solid => "solid".to_string(),
160 LineStyle::Dashed => "dashed".to_string(),
161 LineStyle::Dotted => "dotted".to_string(),
162 LineStyle::DashDot => "dashdot".to_string(),
163 };
164 write!(f, "{}", str)
165 }
166}
167
168#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
169pub enum Grid {
170 On,
171 Off,
172}
173
174#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
181pub enum PlotStyle {
182 Nature,
183 IEEE,
184 Default,
185 Science,
186}
187
188#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
189pub enum PlotScale {
190 Linear,
191 Log,
192}
193
194#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
195pub enum PlotType {
196 Scatter,
197 Line,
198 Bar,
199}
200
201impl Display for PlotType {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 let str = match self {
204 PlotType::Scatter => "scatter".to_string(),
205 PlotType::Line => "line".to_string(),
206 PlotType::Bar => "bar".to_string(),
207 };
208 write!(f, "{}", str)
209 }
210}
211
212pub trait Plot {
213 fn set_domain(&mut self, x: Vec<f64>) -> &mut Self;
214 fn insert_image(&mut self, y: Vec<f64>) -> &mut Self;
215 fn insert_pair(&mut self, xy: (Vec<f64>, Vec<f64>)) -> &mut Self;
216 fn set_title(&mut self, title: &str) -> &mut Self;
217 fn set_xlabel(&mut self, xlabel: &str) -> &mut Self;
218 fn set_ylabel(&mut self, ylabel: &str) -> &mut Self;
219 fn set_zlabel(&mut self, zlabel: &str) -> &mut Self;
220 fn set_xscale(&mut self, xscale: PlotScale) -> &mut Self;
221 fn set_yscale(&mut self, yscale: PlotScale) -> &mut Self;
222 fn set_xlim(&mut self, xlim: (f64, f64)) -> &mut Self;
223 fn set_ylim(&mut self, ylim: (f64, f64)) -> &mut Self;
224 fn set_legend(&mut self, legends: Vec<&str>) -> &mut Self;
225 fn set_path(&mut self, path: &str) -> &mut Self;
226 fn set_fig_size(&mut self, fig_size: (usize, usize)) -> &mut Self;
227 fn set_dpi(&mut self, dpi: usize) -> &mut Self;
228 fn grid(&mut self, grid: Grid) -> &mut Self;
229 fn set_marker(&mut self, styles: Vec<(usize, Markers)>) -> &mut Self;
230 fn set_style(&mut self, style: PlotStyle) -> &mut Self;
231 fn tight_layout(&mut self) -> &mut Self;
232 fn set_line_style(&mut self, style: Vec<(usize, LineStyle)>) -> &mut Self;
233 fn set_color(&mut self, color: Vec<(usize, &str)>) -> &mut Self;
234 fn set_alpha(&mut self, alpha: Vec<(usize, f64)>) -> &mut Self;
235 fn set_plot_type(&mut self, plot_type: Vec<(usize, PlotType)>) -> &mut Self;
236 fn savefig(&self) -> PyResult<()>;
237}
238
239#[derive(Debug)]
240pub struct Plot2D {
241 domain: Vector,
242 images: Vec<Vector>,
243 pairs: Vec<(Vector, Vector)>,
244 title: Option<String>,
245 xlabel: Option<String>,
246 ylabel: Option<String>,
247 xscale: PlotScale,
248 yscale: PlotScale,
249 xlim: Option<(f64, f64)>,
250 ylim: Option<(f64, f64)>,
251 legends: Vec<String>,
252 markers: Vec<(usize, Markers)>,
253 line_style: Vec<(usize, LineStyle)>,
254 color: Vec<(usize, String)>,
255 alpha: Vec<(usize, f64)>,
256 path: String,
257 fig_size: Option<(usize, usize)>,
258 dpi: usize,
259 grid: Grid,
260 style: PlotStyle,
261 tight: bool,
262 plot_type: Vec<(usize, PlotType)>,
263 options: HashMap<PlotOptions, bool>,
264}
265
266impl Plot2D {
267 pub fn new() -> Self {
268 let mut default_options: HashMap<PlotOptions, bool> = HashMap::new();
269 default_options.insert(Domain, false);
270 default_options.insert(Images, false);
271 default_options.insert(Pairs, false);
272 default_options.insert(Path, false);
273
274 Plot2D {
275 domain: vec![],
276 images: vec![],
277 pairs: vec![],
278 title: None,
279 xlabel: None,
280 ylabel: None,
281 xscale: PlotScale::Linear,
282 yscale: PlotScale::Linear,
283 xlim: None,
284 ylim: None,
285 legends: vec![],
286 markers: vec![],
287 line_style: vec![],
288 color: vec![],
289 alpha: vec![],
290 path: "".to_string(),
291 fig_size: None,
292 dpi: 300,
293 grid: On,
294 style: PlotStyle::Default,
295 tight: false,
296 plot_type: vec![],
297 options: default_options,
298 }
299 }
300}
301
302impl Plot for Plot2D {
303 fn set_domain(&mut self, x: Vec<f64>) -> &mut Self {
304 if let Some(x) = self.options.get_mut(&Domain) {
305 *x = true
306 }
307 self.domain = x;
308 self
309 }
310
311 fn insert_image(&mut self, y: Vec<f64>) -> &mut Self {
312 if let Some(x) = self.options.get_mut(&Images) {
313 *x = true
314 }
315 self.images.push(y);
316 self
317 }
318
319 fn insert_pair(&mut self, xy: (Vec<f64>, Vec<f64>)) -> &mut Self {
320 if let Some(t) = self.options.get_mut(&Pairs) {
321 *t = true
322 }
323 self.pairs.push(xy);
324 self
325 }
326
327 fn set_title(&mut self, title: &str) -> &mut Self {
328 self.title = Some(title.to_owned());
329 self
330 }
331
332 fn set_xlabel(&mut self, xlabel: &str) -> &mut Self {
333 self.xlabel = Some(xlabel.to_owned());
334 self
335 }
336
337 fn set_ylabel(&mut self, ylabel: &str) -> &mut Self {
338 self.ylabel = Some(ylabel.to_owned());
339 self
340 }
341
342 fn set_zlabel(&mut self, _zlabel: &str) -> &mut Self {
343 unimplemented!()
344 }
345
346 fn set_xscale(&mut self, xscale: PlotScale) -> &mut Self {
347 self.xscale = xscale;
348 self
349 }
350
351 fn set_yscale(&mut self, yscale: PlotScale) -> &mut Self {
352 self.yscale = yscale;
353 self
354 }
355
356 fn set_xlim(&mut self, xlim: (f64, f64)) -> &mut Self {
357 self.xlim = Some(xlim);
358 self
359 }
360
361 fn set_ylim(&mut self, ylim: (f64, f64)) -> &mut Self {
362 self.ylim = Some(ylim);
363 self
364 }
365
366 fn set_legend(&mut self, legends: Vec<&str>) -> &mut Self {
367 self.legends = legends
368 .into_iter()
369 .map(|x| x.to_owned())
370 .collect::<Vec<String>>();
371 self
372 }
373
374 fn set_path(&mut self, path: &str) -> &mut Self {
375 if let Some(x) = self.options.get_mut(&Path) {
376 *x = true
377 }
378 self.path = path.to_owned();
379 self
380 }
381
382 fn set_fig_size(&mut self, fig_size: (usize, usize)) -> &mut Self {
383 self.fig_size = Some(fig_size);
384 self
385 }
386
387 fn set_dpi(&mut self, dpi: usize) -> &mut Self {
388 self.dpi = dpi;
389 self
390 }
391
392 fn grid(&mut self, grid: Grid) -> &mut Self {
393 self.grid = grid;
394 self
395 }
396
397 fn set_marker(&mut self, styles: Vec<(usize, Markers)>) -> &mut Self {
398 self.markers = styles;
399 self
400 }
401
402 fn set_style(&mut self, style: PlotStyle) -> &mut Self {
403 self.style = style;
404 self
405 }
406
407 fn tight_layout(&mut self) -> &mut Self {
408 self.tight = true;
409 self
410 }
411
412 fn set_line_style(&mut self, style: Vec<(usize, LineStyle)>) -> &mut Self {
413 self.line_style = style;
414 self
415 }
416
417 fn set_color(&mut self, color: Vec<(usize, &str)>) -> &mut Self {
418 self.color = color.into_iter().map(|(i, x)| (i, x.to_owned())).collect();
419 self
420 }
421
422 fn set_alpha(&mut self, alpha: Vec<(usize, f64)>) -> &mut Self {
423 self.alpha = alpha;
424 self
425 }
426
427 fn set_plot_type(&mut self, plot_type: Vec<(usize, PlotType)>) -> &mut Self {
428 self.plot_type = plot_type;
429 self
430 }
431
432 fn savefig(&self) -> PyResult<()> {
433 match self.options.get(&Domain) {
435 Some(x) if !*x => match self.options.get(&Pairs) {
436 Some(xy) if !*xy => {
437 panic!("There are no data to plot");
438 }
439 None => {
440 panic!("There are some serious problems in plot system");
441 }
442 _ => (),
443 },
444 None => {
445 panic!("There are some serious problems in plot system");
446 }
447 _ => (),
448 }
449
450 match self.options.get(&Images) {
452 Some(x) if !*x => match self.options.get(&Pairs) {
453 Some(xy) if !*xy => {
454 panic!("there are no data to plot");
455 }
456 None => {
457 panic!("There are some serious problems in plot system");
458 }
459 _ => (),
460 },
461 None => {
462 panic!("There are some serious problems in plot system");
463 }
464 _ => (),
465 }
466
467 Python::attach(|py| {
469 let x = self.domain.clone();
471 let ys = self.images.clone();
472 let pairs = self.pairs.clone();
473 let y_length = ys.len();
474 let pair_length = pairs.len();
475 let title = self.title.clone();
476 let fig_size = self.fig_size;
477 let dpi = self.dpi;
478 let grid = match self.grid {
479 On => true,
480 Off => false,
481 };
482 let style = match self.style {
483 PlotStyle::Nature => "nature",
484 PlotStyle::IEEE => "ieee",
485 PlotStyle::Default => "default",
486 PlotStyle::Science => "science",
487 };
488 let xlabel = self.xlabel.clone();
489 let ylabel = self.ylabel.clone();
490 let legends = self.legends.clone();
491 let path = self.path.clone();
492 let markers = self
493 .markers
494 .iter()
495 .map(|(i, x)| (i, format!("{}", x)))
496 .collect::<Vec<_>>();
497 let line_style = self
498 .line_style
499 .iter()
500 .map(|(i, x)| (i, format!("{}", x)))
501 .collect::<Vec<_>>();
502 let color = self.color.clone();
503 let alpha = self.alpha.clone();
504 let plot_type = self.plot_type.clone();
505
506 let mut globals =
508 vec![("plt", py.import("matplotlib.pyplot")?)].into_py_dict(py)?;
509 globals.borrow_mut().set_item("x", x)?;
510 globals.borrow_mut().set_item("y", ys)?;
511 globals.borrow_mut().set_item("pair", pairs)?;
512 globals.borrow_mut().set_item("n", y_length)?;
513 globals.borrow_mut().set_item("p", pair_length)?;
514 if let Some(fs) = fig_size {
515 globals.borrow_mut().set_item("fs", fs)?;
516 }
517 globals.borrow_mut().set_item("dp", dpi)?;
518 globals.borrow_mut().set_item("gr", grid)?;
519 globals.borrow_mut().set_item("pa", path)?;
520 if let Some(xl) = self.xlim {
521 globals.borrow_mut().set_item("xl", xl)?;
522 }
523 if let Some(yl) = self.ylim {
524 globals.borrow_mut().set_item("yl", yl)?;
525 }
526
527 let mut plot_string = match self.style {
529 PlotStyle::Default => "\
530 plt.rc(\"text\", usetex=True)\n\
531 plt.rc(\"font\", family=\"serif\")\n"
532 .to_string(),
533 PlotStyle::Science => "\
534 import scienceplots\n\
535 plt.style.use(\"science\")\n"
536 .to_string(),
537 _ => format!(
538 "\
539 import scienceplots\n\
540 plt.style.use([\"science\", \"{}\"])\n",
541 style
542 ),
543 };
544 if fig_size.is_some() {
545 plot_string.push_str(&"plt.figure(figsize=fs, dpi=dp)\n".to_string()[..]);
546 } else {
547 plot_string.push_str(&"plt.figure()\n".to_string()[..]);
548 }
549 if self.tight {
550 plot_string.push_str(&"plt.autoscale(tight=True)\n".to_string()[..]);
551 }
552 if let Some(t) = title {
553 plot_string.push_str(&format!("plt.title(r\"{}\")\n", t)[..]);
554 }
555 if let Some(x) = xlabel {
556 plot_string.push_str(&format!("plt.xlabel(r\"{}\")\n", x)[..]);
557 }
558 if let Some(y) = ylabel {
559 plot_string.push_str(&format!("plt.ylabel(r\"{}\")\n", y)[..]);
560 }
561 match self.xscale {
562 PlotScale::Linear => {
563 plot_string.push_str(&"plt.xscale(\"linear\")\n".to_string()[..])
564 }
565 PlotScale::Log => plot_string.push_str(&"plt.xscale(\"log\")\n".to_string()[..]),
566 }
567 match self.yscale {
568 PlotScale::Linear => {
569 plot_string.push_str(&"plt.yscale(\"linear\")\n".to_string()[..])
570 }
571 PlotScale::Log => plot_string.push_str(&"plt.yscale(\"log\")\n".to_string()[..]),
572 }
573 if self.xlim.is_some() {
574 plot_string.push_str(&"plt.xlim(xl)\n".to_string()[..]);
575 }
576 if self.ylim.is_some() {
577 plot_string.push_str(&"plt.ylim(yl)\n".to_string()[..]);
578 }
579
580 for i in 0..y_length {
581 let mut inner_string = format!("x,y[{}]", i);
582 let is_corresponding_marker =
583 !markers.is_empty() && (markers.iter().any(|(&j, _)| j == i));
584 if is_corresponding_marker {
585 let marker = markers.iter().find(|(&j, _)| j == i).unwrap().1.as_str();
586 inner_string.push_str(&format!(",marker=\"{}\"", marker)[..]);
587 }
588 let is_corresponding_line_style =
589 !line_style.is_empty() && (line_style.iter().any(|(&j, _)| j == i));
590 if is_corresponding_line_style {
591 let style = line_style.iter().find(|(&j, _)| j == i).unwrap().1.as_str();
592 inner_string.push_str(&format!(",linestyle=\"{}\"", style)[..]);
593 }
594 let is_corresponding_color =
595 !color.is_empty() && (color.iter().any(|(j, _)| j == &i));
596 if is_corresponding_color {
597 let color = color.iter().find(|(j, _)| j == &i).unwrap().1.as_str();
598 inner_string.push_str(&format!(",color=\"{}\"", color)[..]);
599 }
600 if !legends.is_empty() {
601 inner_string.push_str(&format!(",label=r\"{}\"", legends[i])[..]);
602 }
603 let is_corresponding_alpha =
604 !alpha.is_empty() && (alpha.iter().any(|(j, _)| j == &i));
605 if is_corresponding_alpha {
606 let alpha = alpha.iter().find(|(j, _)| j == &i).unwrap().1;
607 inner_string.push_str(&format!(",alpha={}", alpha)[..]);
608 }
609 let is_corresponding_plot_type =
610 !plot_type.is_empty() && (plot_type.iter().any(|(j, _)| j == &i));
611 if is_corresponding_plot_type {
612 let plot_type = plot_type.iter().find(|(j, _)| j == &i).unwrap().1;
613 match plot_type {
614 PlotType::Scatter => {
615 plot_string.push_str(&format!("plt.scatter({})\n", inner_string)[..]);
616 }
617 PlotType::Line => {
618 plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
619 }
620 PlotType::Bar => {
621 plot_string.push_str(&format!("plt.bar({})\n", inner_string)[..]);
622 }
623 }
624 } else {
625 plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
626 }
627 }
628 for i in 0..pair_length {
629 let mut inner_string = format!("pair[{}][0],pair[{}][1]", i, i);
630 let is_corresponding_marker =
631 !markers.is_empty() && (markers.iter().any(|(&j, _)| j == (i + y_length)));
632 if is_corresponding_marker {
633 let marker = markers
634 .iter()
635 .find(|(&j, _)| j == (i + y_length))
636 .unwrap()
637 .1
638 .as_str();
639 inner_string.push_str(&format!(",marker=\"{}\"", marker)[..]);
640 }
641 let is_corresponding_line_style = !line_style.is_empty()
642 && (line_style.iter().any(|(&j, _)| j == (i + y_length)));
643 if is_corresponding_line_style {
644 let style = line_style
645 .iter()
646 .find(|(&j, _)| j == (i + y_length))
647 .unwrap()
648 .1
649 .as_str();
650 inner_string.push_str(&format!(",linestyle=\"{}\"", style)[..]);
651 }
652 let is_corresponding_color =
653 !color.is_empty() && (color.iter().any(|(j, _)| j == &(i + y_length)));
654 if is_corresponding_color {
655 let color = color
656 .iter()
657 .find(|(j, _)| j == &(i + y_length))
658 .unwrap()
659 .1
660 .as_str();
661 inner_string.push_str(&format!(",color=\"{}\"", color)[..]);
662 }
663 if !legends.is_empty() {
664 inner_string.push_str(&format!(",label=r\"{}\"", legends[i + y_length])[..]);
665 }
666 let is_corresponding_alpha =
667 !alpha.is_empty() && (alpha.iter().any(|(j, _)| j == &(i + y_length)));
668 if is_corresponding_alpha {
669 let alpha = alpha.iter().find(|(j, _)| j == &(i + y_length)).unwrap().1;
670 inner_string.push_str(&format!(",alpha={}", alpha)[..]);
671 }
672 let is_corresponding_plot_type =
673 !plot_type.is_empty() && (plot_type.iter().any(|(j, _)| j == &(i + y_length)));
674 if is_corresponding_plot_type {
675 let plot_type = plot_type
676 .iter()
677 .find(|(j, _)| j == &(i + y_length))
678 .unwrap()
679 .1;
680 match plot_type {
681 PlotType::Scatter => {
682 plot_string.push_str(&format!("plt.scatter({})\n", inner_string)[..]);
683 }
684 PlotType::Line => {
685 plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
686 }
687 PlotType::Bar => {
688 plot_string.push_str(&format!("plt.bar({})\n", inner_string)[..]);
689 }
690 }
691 } else {
692 plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
693 }
694 }
695
696 if !legends.is_empty() {
697 plot_string.push_str("plt.legend()\n");
698 }
699
700 if self.tight {
701 plot_string
702 .push_str(&format!("plt.savefig(pa, dpi={}, bbox_inches='tight')", dpi)[..]);
703 } else {
704 plot_string.push_str(&format!("plt.savefig(pa, dpi={})", dpi)[..]);
705 }
706
707 py.run(&CString::new(plot_string)?, Some(&globals), None)?;
708 Ok(())
709 })
710 }
711}