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