1use super::{call_python3, generate_list_quoted, vector_to_array, AsVector, Legend, StrError, SuperTitleParams};
2use num_traits::Num;
3use std::ffi::OsStr;
4use std::fmt::Write;
5use std::fs::{self, File};
6use std::io::Write as IoWrite;
7use std::path::Path;
8
9const DEFAULT_PYTHON_EXE: &str = "python3";
10
11pub trait GraphMaker {
13 fn get_buffer<'a>(&'a self) -> &'a String;
15
16 fn clear_buffer(&mut self);
18}
19
20pub struct Plot {
159 show_errors: bool, buffer: String, save_tight: bool, save_pad_inches: Option<f64>, save_transparent: Option<bool>, python_exe: String, }
166
167impl Plot {
168 pub fn new() -> Self {
170 Plot {
171 show_errors: false,
172 buffer: String::new(),
173 save_tight: true,
174 save_pad_inches: None,
175 save_transparent: None,
176 python_exe: DEFAULT_PYTHON_EXE.to_string(),
177 }
178 }
179
180 pub fn add(&mut self, graph: &dyn GraphMaker) -> &mut Self {
182 self.buffer.push_str(graph.get_buffer());
183 self
184 }
185
186 pub fn set_save_tight(&mut self, tight: bool) -> &mut Self {
188 self.save_tight = tight;
189 self
190 }
191
192 pub fn set_save_pad_inches(&mut self, pad_inches: f64) -> &mut Self {
197 self.save_pad_inches = Some(pad_inches);
198 self
199 }
200
201 pub fn set_save_transparent(&mut self, transparent: bool) -> &mut Self {
203 self.save_transparent = Some(transparent);
204 self
205 }
206
207 pub fn save<S>(&self, figure_path: &S) -> Result<(), StrError>
218 where
219 S: AsRef<OsStr> + ?Sized,
220 {
221 self.run(figure_path, false)
222 }
223
224 pub fn show<S>(&self, figure_path: &S) -> Result<(), StrError>
236 where
237 S: AsRef<OsStr> + ?Sized,
238 {
239 self.run(figure_path, true)
240 }
241
242 pub fn show_in_jupyter<S>(&self, figure_path: &S) -> Result<(), StrError>
257 where
258 S: AsRef<OsStr> + ?Sized,
259 {
260 self.run(figure_path, false)?;
261 let fig_path = Path::new(figure_path);
262 match fs::read_to_string(fig_path) {
263 Ok(figure) => println!("EVCXR_BEGIN_CONTENT text/html\n{}\nEVCXR_END_CONTENT", figure),
264 Err(_) => return Err("Failed to read the SVG figure, please check it."),
265 }
266 Ok(())
267 }
268
269 pub fn clear_current_axes(&mut self) -> &mut Self {
271 self.buffer.push_str("plt.gca().cla()\n");
272 self
273 }
274
275 pub fn clear_current_figure(&mut self) -> &mut Self {
277 self.buffer.push_str("plt.clf()\n");
278 self
279 }
280
281 pub fn legend(&mut self) -> &mut Self {
283 let mut legend = Legend::new();
284 legend.draw();
285 self.add(&legend)
286 }
287
288 pub fn grid_and_labels(&mut self, xlabel: &str, ylabel: &str) -> &mut Self {
290 write!(
291 &mut self.buffer,
292 "plt.gca().set_axisbelow(True)\n\
293 plt.grid(linestyle='--',color='grey',zorder=-1000)\n\
294 plt.gca().set_xlabel(r'{}')\n\
295 plt.gca().set_ylabel(r'{}')\n",
296 xlabel, ylabel
297 )
298 .unwrap();
299 self
300 }
301
302 pub fn grid_labels_legend(&mut self, xlabel: &str, ylabel: &str) -> &mut Self {
304 write!(
305 &mut self.buffer,
306 "plt.gca().set_axisbelow(True)\n\
307 plt.grid(linestyle='--',color='grey',zorder=-1000)\n\
308 plt.gca().set_xlabel(r'{}')\n\
309 plt.gca().set_ylabel(r'{}')\n",
310 xlabel, ylabel
311 )
312 .unwrap();
313 self.legend()
314 }
315
316 pub fn set_show_errors(&mut self, option: bool) -> &mut Self {
318 self.show_errors = option;
319 self
320 }
321
322 pub fn set_subplot_3d(&mut self, row: usize, col: usize, index: usize) -> &mut Self {
330 write!(&mut self.buffer, "\nsubplot_3d({},{},{})\n", row, col, index).unwrap();
331 self
332 }
333
334 pub fn set_subplot(&mut self, row: usize, col: usize, index: usize) -> &mut Self {
342 write!(&mut self.buffer, "\nplt.subplot({},{},{})\n", row, col, index).unwrap();
343 self
344 }
345
346 pub fn set_gridspec(&mut self, grid_handle: &str, row: usize, col: usize, options: &str) -> &mut Self {
356 write!(
357 &mut self.buffer,
358 "grid_{}=plt.GridSpec({},{},{})\n",
359 grid_handle, row, col, options
360 )
361 .unwrap();
362 self
363 }
364
365 pub fn set_subplot_grid(&mut self, grid_handle: &str, i_range: &str, j_range: &str) -> &mut Self {
375 write!(
376 &mut self.buffer,
377 "\nplt.subplot(grid_{}[{},{}])\n",
378 grid_handle, i_range, j_range
379 )
380 .unwrap();
381 self
382 }
383
384 pub fn set_rotation_ticks_x(&mut self, rotation: f64) -> &mut Self {
386 write!(
387 &mut self.buffer,
388 "plt.gca().tick_params(axis='x',rotation={})\n",
389 rotation
390 )
391 .unwrap();
392 self
393 }
394
395 pub fn set_rotation_ticks_y(&mut self, rotation: f64) -> &mut Self {
397 write!(
398 &mut self.buffer,
399 "plt.gca().tick_params(axis='y',rotation={})\n",
400 rotation
401 )
402 .unwrap();
403 self
404 }
405
406 pub fn set_align_labels(&mut self) -> &mut Self {
408 write!(&mut self.buffer, "plt.gcf().align_labels()\n").unwrap();
409 self
410 }
411
412 pub fn set_title(&mut self, title: &str) -> &mut Self {
420 let t = title.replace("'", "’");
421 write!(&mut self.buffer, "plt.title(r'{}')\n", t).unwrap();
422 self
423 }
424
425 pub fn set_super_title(&mut self, title: &str, params: Option<&SuperTitleParams>) -> &mut Self {
436 let t = title.replace("'", "’").replace("\\n", "'+'\\n'+r'");
437 match params {
438 Some(p) => write!(&mut self.buffer, "st=plt.suptitle(r'{}'{})\n", t, p.options()).unwrap(),
439 None => write!(&mut self.buffer, "st=plt.suptitle(r'{}')\n", t).unwrap(),
440 }
441 write!(&mut self.buffer, "add_to_ea(st)\n").unwrap();
442 self
443 }
444
445 pub fn set_horizontal_gap(&mut self, value: f64) -> &mut Self {
447 write!(&mut self.buffer, "plt.subplots_adjust(wspace={})\n", value).unwrap();
448 self
449 }
450
451 pub fn set_vertical_gap(&mut self, value: f64) -> &mut Self {
453 write!(&mut self.buffer, "plt.subplots_adjust(hspace={})\n", value).unwrap();
454 self
455 }
456
457 pub fn set_gaps(&mut self, horizontal: f64, vertical: f64) -> &mut Self {
459 write!(
460 &mut self.buffer,
461 "plt.subplots_adjust(wspace={},hspace={})\n",
462 horizontal, vertical
463 )
464 .unwrap();
465 self
466 }
467
468 pub fn set_equal_axes(&mut self, equal: bool) -> &mut Self {
470 if equal {
471 self.buffer.push_str("set_equal_axes()\n");
472 } else {
473 self.buffer.push_str("plt.gca().axes.set_aspect('auto')\n");
474 }
475 self
476 }
477
478 pub fn set_figure_size_inches(&mut self, width: f64, height: f64) -> &mut Self {
480 write!(&mut self.buffer, "plt.gcf().set_size_inches({},{})\n", width, height).unwrap();
481 self
482 }
483
484 #[rustfmt::skip]
486 pub fn set_figure_size_points(&mut self, width: f64, height: f64) -> &mut Self {
487 const FACTOR: f64 = 72.27;
488 write!(&mut self.buffer, "plt.gcf().set_size_inches({},{})\n", width / FACTOR, height / FACTOR).unwrap();
489 self
490 }
491
492 pub fn set_hide_xticks(&mut self) -> &mut Self {
494 write!(&mut self.buffer, "plt.gca().set_xticklabels([])\n").unwrap();
495 self
496 }
497
498 pub fn set_hide_yticks(&mut self) -> &mut Self {
500 write!(&mut self.buffer, "plt.gca().set_yticklabels([])\n").unwrap();
501 self
502 }
503
504 pub fn set_hide_zticks(&mut self) -> &mut Self {
506 write!(&mut self.buffer, "plt.gca().set_zticklabels([])\n").unwrap();
507 self
508 }
509
510 pub fn set_hide_axes(&mut self, hide: bool) -> &mut Self {
512 let option = if hide { "off" } else { "on" };
513 write!(&mut self.buffer, "plt.axis('{}')\n", option).unwrap();
514 self
515 }
516
517 pub fn set_hide_3d_grid(&mut self, hide: bool) -> &mut Self {
521 if hide {
522 self.buffer.push_str(
523 "plt.gca().xaxis.pane.set_color((1.0, 1.0, 1.0, 0.0))\n\
524 plt.gca().yaxis.pane.set_color((1.0, 1.0, 1.0, 0.0))\n\
525 plt.gca().zaxis.pane.set_color((1.0, 1.0, 1.0, 0.0))\n\
526 plt.gca().grid(False)\n",
527 );
528 } else {
529 self.buffer.push_str("plt.gca().grid(True)\n");
530 }
531 self
532 }
533
534 pub fn set_range_3d(&mut self, xmin: f64, xmax: f64, ymin: f64, ymax: f64, zmin: f64, zmax: f64) -> &mut Self {
536 write!(
537 &mut self.buffer,
538 "plt.gca().set_xlim({},{})\n\
539 plt.gca().set_ylim({},{})\n\
540 plt.gca().set_zlim({},{})\n",
541 xmin, xmax, ymin, ymax, zmin, zmax,
542 )
543 .unwrap();
544 self
545 }
546
547 pub fn set_range(&mut self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> &mut Self {
549 write!(&mut self.buffer, "plt.axis([{},{},{},{}])\n", xmin, xmax, ymin, ymax).unwrap();
550 self
551 }
552
553 pub fn set_range_from_vec(&mut self, limits: &[f64]) -> &mut Self {
555 write!(
556 &mut self.buffer,
557 "plt.axis([{},{},{},{}])\n",
558 limits[0], limits[1], limits[2], limits[3]
559 )
560 .unwrap();
561 self
562 }
563
564 pub fn set_xmin(&mut self, xmin: f64) -> &mut Self {
566 write!(&mut self.buffer, "plt.gca().set_xlim([{},None])\n", xmin).unwrap();
567 self
568 }
569
570 pub fn set_xmax(&mut self, xmax: f64) -> &mut Self {
572 write!(&mut self.buffer, "plt.gca().set_xlim([None,{}])\n", xmax).unwrap();
573 self
574 }
575
576 pub fn set_ymin(&mut self, ymin: f64) -> &mut Self {
578 write!(&mut self.buffer, "plt.gca().set_ylim([{},None])\n", ymin).unwrap();
579 self
580 }
581
582 pub fn set_ymax(&mut self, ymax: f64) -> &mut Self {
584 write!(&mut self.buffer, "plt.gca().set_ylim([None,{}])\n", ymax).unwrap();
585 self
586 }
587
588 pub fn set_zmin(&mut self, zmin: f64) -> &mut Self {
590 write!(&mut self.buffer, "plt.gca().set_zlim([{},None])\n", zmin).unwrap();
591 self
592 }
593
594 pub fn set_zmax(&mut self, zmax: f64) -> &mut Self {
596 write!(&mut self.buffer, "plt.gca().set_zlim([None,{}])\n", zmax).unwrap();
597 self
598 }
599
600 pub fn set_xrange(&mut self, xmin: f64, xmax: f64) -> &mut Self {
602 write!(&mut self.buffer, "plt.gca().set_xlim([{},{}])\n", xmin, xmax).unwrap();
603 self
604 }
605
606 pub fn set_yrange(&mut self, ymin: f64, ymax: f64) -> &mut Self {
608 write!(&mut self.buffer, "plt.gca().set_ylim([{},{}])\n", ymin, ymax).unwrap();
609 self
610 }
611
612 pub fn set_zrange(&mut self, zmin: f64, zmax: f64) -> &mut Self {
614 write!(&mut self.buffer, "plt.gca().set_zlim([{},{}])\n", zmin, zmax).unwrap();
615 self
616 }
617
618 pub fn set_num_ticks_x(&mut self, num: usize) -> &mut Self {
620 if num == 0 {
621 self.buffer.push_str("plt.gca().get_xaxis().set_ticks([])\n");
622 } else {
623 write!(
624 &mut self.buffer,
625 "plt.gca().get_xaxis().set_major_locator(tck.MaxNLocator({}))\n",
626 num
627 )
628 .unwrap();
629 }
630 self
631 }
632
633 pub fn set_num_ticks_y(&mut self, num: usize) -> &mut Self {
635 if num == 0 {
636 self.buffer.push_str("plt.gca().get_yaxis().set_ticks([])\n");
637 } else {
638 write!(
639 &mut self.buffer,
640 "plt.gca().get_yaxis().set_major_locator(tck.MaxNLocator({}))\n",
641 num
642 )
643 .unwrap();
644 }
645 self
646 }
647
648 pub fn set_num_ticks_z(&mut self, num: usize) -> &mut Self {
650 if num == 0 {
651 self.buffer.push_str("plt.gca().get_zaxis().set_ticks([])\n");
652 } else {
653 write!(
654 &mut self.buffer,
655 "plt.gca().get_zaxis().set_major_locator(tck.MaxNLocator({}))\n",
656 num
657 )
658 .unwrap();
659 }
660 self
661 }
662
663 #[rustfmt::skip]
672 pub fn set_ticks_x(&mut self, major_every: f64, minor_every: f64, major_number_format: &str) -> &mut Self {
673 if major_every > 0.0 {
674 write!(&mut self.buffer, "major_locator = tck.MultipleLocator({})\n", major_every).unwrap();
675 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / {}\n", major_every).unwrap();
676 write!(&mut self.buffer, "if n_ticks < major_locator.MAXTICKS * 0.9:\n").unwrap();
677 write!(&mut self.buffer, " plt.gca().xaxis.set_major_locator(major_locator)\n").unwrap();
678 }
679 if minor_every > 0.0 {
680 write!(&mut self.buffer, "minor_locator = tck.MultipleLocator({})\n", minor_every).unwrap();
681 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / {}\n", minor_every).unwrap();
682 write!(&mut self.buffer, "if n_ticks < minor_locator.MAXTICKS * 0.9:\n").unwrap();
683 write!(&mut self.buffer, " plt.gca().xaxis.set_minor_locator(minor_locator)\n").unwrap();
684 }
685 if major_number_format != "" {
686 write!(&mut self.buffer, "major_formatter = tck.FormatStrFormatter(r'{}')\n", major_number_format).unwrap();
687 write!(&mut self.buffer, "plt.gca().xaxis.set_major_formatter(major_formatter)\n").unwrap();
688 }
689 self
690 }
691
692 #[rustfmt::skip]
701 pub fn set_ticks_y(&mut self, major_every: f64, minor_every: f64, major_number_format: &str) -> &mut Self {
702 if major_every > 0.0 {
703 write!(&mut self.buffer, "major_locator = tck.MultipleLocator({})\n", major_every).unwrap();
704 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / {}\n", major_every).unwrap();
705 write!(&mut self.buffer, "if n_ticks < major_locator.MAXTICKS * 0.9:\n").unwrap();
706 write!(&mut self.buffer, " plt.gca().yaxis.set_major_locator(major_locator)\n").unwrap();
707 }
708 if minor_every > 0.0 {
709 write!(&mut self.buffer, "minor_locator = tck.MultipleLocator({})\n", minor_every).unwrap();
710 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / {}\n", minor_every).unwrap();
711 write!(&mut self.buffer, "if n_ticks < minor_locator.MAXTICKS * 0.9:\n").unwrap();
712 write!(&mut self.buffer, " plt.gca().yaxis.set_minor_locator(minor_locator)\n").unwrap();
713 }
714 if major_number_format != "" {
715 write!(&mut self.buffer, "major_formatter = tck.FormatStrFormatter(r'{}')\n", major_number_format).unwrap();
716 write!(&mut self.buffer, "plt.gca().yaxis.set_major_formatter(major_formatter)\n").unwrap();
717 }
718 self
719 }
720
721 pub fn set_ticks_x_labels<'a, S, T, U>(&mut self, ticks: &'a T, labels: &[S]) -> &mut Self
723 where
724 S: std::fmt::Display,
725 T: AsVector<'a, U>,
726 U: 'a + std::fmt::Display + Num,
727 {
728 assert_eq!(ticks.vec_size(), labels.len());
729 vector_to_array(&mut self.buffer, "tx", ticks);
730 generate_list_quoted(&mut self.buffer, "lx", labels);
731 write!(
732 &mut self.buffer,
733 "plt.gca().set_xticks(tx)\nplt.gca().set_xticklabels(lx)\n"
734 )
735 .unwrap();
736 self
737 }
738
739 pub fn set_ticks_y_labels<'a, S, T, U>(&mut self, ticks: &'a T, labels: &[S]) -> &mut Self
741 where
742 S: std::fmt::Display,
743 T: AsVector<'a, U>,
744 U: 'a + std::fmt::Display + Num,
745 {
746 assert_eq!(ticks.vec_size(), labels.len());
747 vector_to_array(&mut self.buffer, "ty", ticks);
748 generate_list_quoted(&mut self.buffer, "ly", labels);
749 write!(
750 &mut self.buffer,
751 "plt.gca().set_yticks(ty)\nplt.gca().set_yticklabels(ly)\n"
752 )
753 .unwrap();
754 self
755 }
756
757 pub fn set_ticks_x_fontsize(&mut self, fontsize: f64) -> &mut Self {
759 write!(
760 &mut self.buffer,
761 "plt.gca().tick_params(axis='x',labelsize={})\n",
762 fontsize,
763 )
764 .unwrap();
765 self
766 }
767
768 pub fn set_ticks_y_fontsize(&mut self, fontsize: f64) -> &mut Self {
770 write!(
771 &mut self.buffer,
772 "plt.gca().tick_params(axis='y',labelsize={})\n",
773 fontsize,
774 )
775 .unwrap();
776 self
777 }
778
779 pub fn set_ticks_z_fontsize(&mut self, fontsize: f64) -> &mut Self {
781 write!(
782 &mut self.buffer,
783 "plt.gca().tick_params(axis='z',labelsize={})\n",
784 fontsize,
785 )
786 .unwrap();
787 self
788 }
789
790 #[inline]
792 fn write_multiple_of_pi_formatter(&mut self) {
793 write!(
794 &mut self.buffer,
795 "def multiple_of_pi_formatter(x, pos):\n\
796 \x20\x20\x20\x20den = 2\n\
797 \x20\x20\x20\x20num = int(np.rint(den*x/np.pi))\n\
798 \x20\x20\x20\x20com = np.gcd(num,den)\n\
799 \x20\x20\x20\x20(num,den) = (int(num/com),int(den/com))\n\
800 \x20\x20\x20\x20if den==1:\n\
801 \x20\x20\x20\x20\x20\x20\x20\x20if num==0: return r'$0$'\n\
802 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\pi$'\n\
803 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$-\\pi$'\n\
804 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$%s\\pi$'%num\n\
805 \x20\x20\x20\x20else:\n\
806 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\frac{{\\pi}}{{%s}}$'%den\n\
807 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$\\frac{{-\\pi}}{{%s}}$'%den\n\
808 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$\\frac{{%s\\pi}}{{%s}}$'%(num,den)\n"
809 )
810 .unwrap();
811 }
812
813 #[rustfmt::skip]
821 pub fn set_ticks_x_multiple_of_pi(&mut self, minor_every: f64) -> &mut Self {
822 write!(&mut self.buffer, "major_locator = tck.MultipleLocator(np.pi/2.0)\n").unwrap();
823 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / (np.pi/2.0)\n").unwrap();
824 write!(&mut self.buffer, "if n_ticks < major_locator.MAXTICKS * 0.9:\n").unwrap();
825 write!(&mut self.buffer, " plt.gca().xaxis.set_major_locator(major_locator)\n").unwrap();
826 if minor_every > 0.0 {
827 write!(&mut self.buffer, "minor_locator = tck.MultipleLocator({})\n", minor_every).unwrap();
828 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / {}\n", minor_every).unwrap();
829 write!(&mut self.buffer, "if n_ticks < minor_locator.MAXTICKS * 0.9:\n").unwrap();
830 write!(&mut self.buffer, " plt.gca().xaxis.set_minor_locator(minor_locator)\n").unwrap();
831 }
832 self.write_multiple_of_pi_formatter();
833 write!(&mut self.buffer, "major_formatter = tck.FuncFormatter(multiple_of_pi_formatter)\n").unwrap();
834 write!(&mut self.buffer, "plt.gca().xaxis.set_major_formatter(major_formatter)\n").unwrap();
835 self
836 }
837
838 #[rustfmt::skip]
846 pub fn set_ticks_y_multiple_of_pi(&mut self, minor_every: f64) -> &mut Self {
847 write!(&mut self.buffer, "major_locator = tck.MultipleLocator(np.pi/2.0)\n").unwrap();
848 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / (np.pi/2.0)\n").unwrap();
849 write!(&mut self.buffer, "if n_ticks < major_locator.MAXTICKS * 0.9:\n").unwrap();
850 write!(&mut self.buffer, " plt.gca().yaxis.set_major_locator(major_locator)\n").unwrap();
851 if minor_every > 0.0 {
852 write!(&mut self.buffer, "minor_locator = tck.MultipleLocator({})\n", minor_every).unwrap();
853 write!(&mut self.buffer, "n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / {}\n", minor_every).unwrap();
854 write!(&mut self.buffer, "if n_ticks < minor_locator.MAXTICKS * 0.9:\n").unwrap();
855 write!(&mut self.buffer, " plt.gca().yaxis.set_minor_locator(minor_locator)\n").unwrap();
856 }
857 self.write_multiple_of_pi_formatter();
858 write!(&mut self.buffer, "major_formatter = tck.FuncFormatter(multiple_of_pi_formatter)\n").unwrap();
859 write!(&mut self.buffer, "plt.gca().yaxis.set_major_formatter(major_formatter)\n").unwrap();
860 self
861 }
862
863 pub fn set_log_x(&mut self, log: bool) -> &mut Self {
869 if log {
870 self.buffer.push_str("plt.gca().set_xscale('log')\n");
871 } else {
872 self.buffer.push_str("plt.gca().set_xscale('linear')\n");
873 }
874 self
875 }
876
877 pub fn set_log_y(&mut self, log: bool) -> &mut Self {
883 if log {
884 self.buffer.push_str("plt.gca().set_yscale('log')\n");
885 } else {
886 self.buffer.push_str("plt.gca().set_yscale('linear')\n");
887 }
888 self
889 }
890
891 pub fn set_label_x(&mut self, label: &str) -> &mut Self {
893 write!(&mut self.buffer, "plt.gca().set_xlabel(r'{}')\n", label).unwrap();
894 self
895 }
896
897 pub fn set_label_y(&mut self, label: &str) -> &mut Self {
899 write!(&mut self.buffer, "plt.gca().set_ylabel(r'{}')\n", label).unwrap();
900 self
901 }
902
903 pub fn set_label_z(&mut self, label: &str) -> &mut Self {
905 write!(&mut self.buffer, "plt.gca().set_zlabel(r'{}')\n", label).unwrap();
906 self
907 }
908
909 pub fn set_label_x_fontsize(&mut self, fontsize: f64) -> &mut Self {
911 write!(&mut self.buffer, "plt.gca().xaxis.label.set_fontsize({})\n", fontsize,).unwrap();
912 self
913 }
914
915 pub fn set_label_y_fontsize(&mut self, fontsize: f64) -> &mut Self {
917 write!(&mut self.buffer, "plt.gca().yaxis.label.set_fontsize({})\n", fontsize,).unwrap();
918 self
919 }
920
921 pub fn set_label_z_fontsize(&mut self, fontsize: f64) -> &mut Self {
923 write!(&mut self.buffer, "plt.gca().zaxis.label.set_fontsize({})\n", fontsize,).unwrap();
924 self
925 }
926
927 pub fn set_label_x_color(&mut self, color: &str) -> &mut Self {
929 write!(
930 &mut self.buffer,
931 "plt.gca().xaxis.label.set_color('{}')\n\
932 plt.gca().tick_params(axis='x',labelcolor='{}')\n",
933 color, color
934 )
935 .unwrap();
936 self
937 }
938
939 pub fn set_label_y_color(&mut self, color: &str) -> &mut Self {
941 write!(
942 &mut self.buffer,
943 "plt.gca().yaxis.label.set_color('{}')\n\
944 plt.gca().tick_params(axis='y',labelcolor='{}')\n",
945 color, color
946 )
947 .unwrap();
948 self
949 }
950
951 pub fn set_label_z_color(&mut self, color: &str) -> &mut Self {
953 write!(
954 &mut self.buffer,
955 "plt.gca().zaxis.label.set_color('{}')\n\
956 plt.gca().tick_params(axis='z',labelcolor='{}')\n",
957 color, color
958 )
959 .unwrap();
960 self
961 }
962
963 pub fn set_label_y_twinx(&mut self, label: &str) -> &mut Self {
967 write!(
968 &mut self.buffer,
969 "if 'ax_twinx' in locals():\n\
970 \x20\x20\x20\x20ax_twinx.set_ylabel(r'{}')\n",
971 label,
972 )
973 .unwrap();
974 self
975 }
976
977 pub fn set_label_y_twinx_color(&mut self, color: &str) -> &mut Self {
981 write!(
982 &mut self.buffer,
983 "if 'ax_twinx' in locals():\n\
984 \x20\x20\x20\x20ax_twinx.yaxis.label.set_color('{}')\n\
985 \x20\x20\x20\x20ax_twinx.tick_params(axis='y',labelcolor='{}')\n",
986 color, color
987 )
988 .unwrap();
989 self
990 }
991
992 pub fn set_label_x_and_pad(&mut self, label: &str, pad: f64) -> &mut Self {
994 write!(
995 &mut self.buffer,
996 "plt.gca().set_xlabel(r'{}',labelpad={})\n",
997 label, pad
998 )
999 .unwrap();
1000 self
1001 }
1002
1003 pub fn set_label_y_and_pad(&mut self, label: &str, pad: f64) -> &mut Self {
1005 write!(
1006 &mut self.buffer,
1007 "plt.gca().set_ylabel(r'{}',labelpad={})\n",
1008 label, pad
1009 )
1010 .unwrap();
1011 self
1012 }
1013
1014 pub fn set_label_z_and_pad(&mut self, label: &str, pad: f64) -> &mut Self {
1016 write!(
1017 &mut self.buffer,
1018 "plt.gca().set_zlabel(r'{}',labelpad={})\n",
1019 label, pad
1020 )
1021 .unwrap();
1022 self
1023 }
1024
1025 pub fn set_labels(&mut self, xlabel: &str, ylabel: &str) -> &mut Self {
1027 write!(
1028 &mut self.buffer,
1029 "plt.gca().set_xlabel(r'{}')\nplt.gca().set_ylabel(r'{}')\n",
1030 xlabel, ylabel
1031 )
1032 .unwrap();
1033 self
1034 }
1035
1036 pub fn set_labels_3d(&mut self, xlabel: &str, ylabel: &str, zlabel: &str) -> &mut Self {
1038 write!(
1039 &mut self.buffer,
1040 "plt.gca().set_xlabel(r'{}')\nplt.gca().set_ylabel(r'{}')\nplt.gca().set_zlabel(r'{}')\n",
1041 xlabel, ylabel, zlabel
1042 )
1043 .unwrap();
1044 self
1045 }
1046
1047 pub fn set_inv_x(&mut self) -> &mut Self {
1049 write!(&mut self.buffer, "plt.gca().invert_xaxis()\n").unwrap();
1050 self
1051 }
1052
1053 pub fn set_inv_y(&mut self) -> &mut Self {
1055 write!(&mut self.buffer, "plt.gca().invert_yaxis()\n").unwrap();
1056 self
1057 }
1058
1059 pub fn set_camera(&mut self, elevation: f64, azimuth: f64) -> &mut Self {
1077 write!(
1078 &mut self.buffer,
1079 "plt.gca().view_init(elev={},azim={})\n",
1080 elevation, azimuth
1081 )
1082 .unwrap();
1083 self
1084 }
1085
1086 pub fn set_frame_border(&mut self, left: bool, right: bool, bottom: bool, top: bool) -> &mut Self {
1088 if left {
1089 self.buffer.push_str("plt.gca().spines['left'].set_visible(True)\n");
1090 } else {
1091 self.buffer.push_str("plt.gca().spines['left'].set_visible(False)\n");
1092 }
1093 if right {
1094 self.buffer.push_str("plt.gca().spines['right'].set_visible(True)\n");
1095 } else {
1096 self.buffer.push_str("plt.gca().spines['right'].set_visible(False)\n");
1097 }
1098 if bottom {
1099 self.buffer.push_str("plt.gca().spines['bottom'].set_visible(True)\n");
1100 } else {
1101 self.buffer.push_str("plt.gca().spines['bottom'].set_visible(False)\n");
1102 }
1103 if top {
1104 self.buffer.push_str("plt.gca().spines['top'].set_visible(True)\n");
1105 } else {
1106 self.buffer.push_str("plt.gca().spines['top'].set_visible(False)\n");
1107 }
1108 self
1109 }
1110
1111 pub fn set_frame_borders(&mut self, show_all: bool) -> &mut Self {
1113 self.set_frame_border(show_all, show_all, show_all, show_all)
1114 }
1115
1116 pub fn set_horiz_line(&mut self, y: f64, color: &str, line_style: &str, line_width: f64) -> &mut Self {
1118 let opt = format!(",color='{}',linestyle='{}',linewidth={}", color, line_style, line_width);
1119 self.buffer.push_str(&format!("plt.axhline({}{})\n", y, &opt));
1120 self
1121 }
1122
1123 pub fn set_vert_line(&mut self, x: f64, color: &str, line_style: &str, line_width: f64) -> &mut Self {
1125 let opt = format!(",color='{}',linestyle='{}',linewidth={}", color, line_style, line_width);
1126 self.buffer.push_str(&format!("plt.axvline({}{})\n", x, &opt));
1127 self
1128 }
1129
1130 pub fn set_cross(&mut self, x: f64, y: f64, color: &str, line_style: &str, line_width: f64) -> &mut Self {
1132 let opt = format!(",color='{}',linestyle='{}',linewidth={}", color, line_style, line_width);
1133 self.buffer
1134 .push_str(&format!("plt.axhline({}{})\nplt.axvline({}{})\n", y, &opt, x, &opt));
1135 self
1136 }
1137
1138 pub fn extra(&mut self, commands: &str) -> &mut Self {
1140 self.buffer.write_str(commands).unwrap();
1141 self
1142 }
1143
1144 pub fn set_python_exe(&mut self, python_exe: &str) -> &mut Self {
1148 self.python_exe = python_exe.to_string();
1149 self
1150 }
1151
1152 fn run<S>(&self, figure_path: &S, show: bool) -> Result<(), StrError>
1154 where
1155 S: AsRef<OsStr> + ?Sized,
1156 {
1157 let fig_path = Path::new(figure_path);
1159 let mut txt = "plt.savefig(fn".to_string();
1160 if self.save_tight {
1161 txt.push_str(",bbox_inches='tight',bbox_extra_artists=EXTRA_ARTISTS");
1162 }
1163 if let Some(pad) = self.save_pad_inches {
1164 txt.push_str(format!(",pad_inches={}", pad).as_str());
1165 }
1166 if let Some(transparent) = self.save_transparent {
1167 if transparent {
1168 txt.push_str(",transparent=True");
1169 }
1170 }
1171 txt.push_str(")\n");
1172 if show {
1173 txt.push_str("\nplt.show()\n");
1174 };
1175 let commands = format!("{}\nfn=r'{}'\n{}", self.buffer, fig_path.to_string_lossy(), txt);
1176
1177 let mut path = Path::new(figure_path).to_path_buf();
1179 path.set_extension("py");
1180 let output = call_python3(&self.python_exe, &commands, &path)?;
1181
1182 if output != "" {
1184 let mut log_path = Path::new(figure_path).to_path_buf();
1185 log_path.set_extension("log");
1186 let mut log_file = File::create(log_path).map_err(|_| "cannot create log file")?;
1187 log_file
1188 .write_all(output.as_bytes())
1189 .map_err(|_| "cannot write to log file")?;
1190 if self.show_errors {
1191 println!("{}", output);
1192 }
1193 return Err("python3 failed; please see the log file");
1194 }
1195 Ok(())
1196 }
1197}
1198
1199#[cfg(test)]
1202mod tests {
1203 use crate::SuperTitleParams;
1204
1205 use super::Plot;
1206 use std::fs::File;
1207 use std::io::{BufRead, BufReader};
1208 use std::path::Path;
1209
1210 const OUT_DIR: &str = "/tmp/plotpy/unit_tests";
1211
1212 #[test]
1213 fn new_plot_works() {
1214 let plot = Plot::new();
1215 assert_eq!(plot.buffer.len(), 0);
1216 }
1217
1218 #[test]
1219 fn save_works() {
1220 let plot = Plot::new();
1221 assert_eq!(plot.buffer.len(), 0);
1222 let path = Path::new(OUT_DIR).join("save_works.svg");
1223 plot.save(&path).unwrap();
1224 let file = File::open(&path).map_err(|_| "cannot open file").unwrap();
1225 let buffered = BufReader::new(file);
1226 let lines_iter = buffered.lines();
1227 assert!(lines_iter.count() > 20);
1228 }
1229
1230 #[test]
1231 fn show_in_jupyter_works() {
1232 let plot = Plot::new();
1233 let path = Path::new(OUT_DIR).join("show_works.svg");
1234 plot.save(&path).unwrap();
1235 let result = plot.show_in_jupyter(&path).unwrap();
1236 assert_eq!(result, ());
1237 }
1238
1239 #[test]
1240 fn save_str_works() {
1241 let plot = Plot::new();
1242 assert_eq!(plot.buffer.len(), 0);
1243 let path = "/tmp/plotpy/unit_tests/save_str_works.svg";
1244 plot.save(&path).unwrap();
1245 let file = File::open(&path).map_err(|_| "cannot open file").unwrap();
1246 let buffered = BufReader::new(file);
1247 let lines_iter = buffered.lines();
1248 assert!(lines_iter.count() > 20);
1249 }
1250
1251 #[test]
1252 fn show_errors_works() {
1253 const WRONG: usize = 0;
1254 let mut plot = Plot::new();
1255 plot.set_show_errors(true);
1256 plot.set_subplot(1, 1, WRONG);
1257 let path = Path::new(OUT_DIR).join("show_errors_works.svg");
1258 assert_eq!(plot.save(&path).err(), Some("python3 failed; please see the log file"));
1259 }
1260
1261 #[test]
1262 fn subplot_3d_works() {
1263 let mut plot = Plot::new();
1264 plot.set_subplot_3d(3, 2, 1);
1265 let b: &str = "\nsubplot_3d(3,2,1)\n";
1266 assert_eq!(plot.buffer, b);
1267 }
1268
1269 #[test]
1270 fn subplot_functions_work() {
1271 let mut plot = Plot::new();
1272 plot.set_super_title("all subplots", None)
1273 .set_subplot(2, 2, 1)
1274 .set_horizontal_gap(0.1)
1275 .set_vertical_gap(0.2)
1276 .set_gaps(0.3, 0.4);
1277 let b: &str = "st=plt.suptitle(r'all subplots')\n\
1278 add_to_ea(st)\n\
1279 \nplt.subplot(2,2,1)\n\
1280 plt.subplots_adjust(wspace=0.1)\n\
1281 plt.subplots_adjust(hspace=0.2)\n\
1282 plt.subplots_adjust(wspace=0.3,hspace=0.4)\n";
1283 assert_eq!(plot.buffer, b);
1284 }
1285
1286 #[test]
1287 fn super_title_works() {
1288 let mut params = SuperTitleParams::new();
1289 params
1290 .set_x(123.3)
1291 .set_y(456.7)
1292 .set_align_horizontal("left")
1293 .set_align_vertical("bottom")
1294 .set_fontsize(12.0)
1295 .set_fontweight(10.0);
1296 let mut plot = Plot::new();
1297 plot.set_super_title("all subplots", Some(¶ms));
1298 let b: &str =
1299 "st=plt.suptitle(r'all subplots',x=123.3,y=456.7,ha='left',va='bottom',fontsize=12,fontweight=10)\n\
1300 add_to_ea(st)\n";
1301 assert_eq!(plot.buffer, b);
1302 }
1303
1304 #[test]
1305 fn grid_functions_work() {
1306 let mut plot = Plot::new();
1307 plot.grid_and_labels("xx", "yy").grid_labels_legend("xx", "yy").legend();
1308 let b: &str = "plt.gca().set_axisbelow(True)\n\
1309 plt.grid(linestyle='--',color='grey',zorder=-1000)\n\
1310 plt.gca().set_xlabel(r'xx')\n\
1311 plt.gca().set_ylabel(r'yy')\n\
1312 plt.gca().set_axisbelow(True)\n\
1313 plt.grid(linestyle='--',color='grey',zorder=-1000)\n\
1314 plt.gca().set_xlabel(r'xx')\n\
1315 plt.gca().set_ylabel(r'yy')\n\
1316 h,l=plt.gca().get_legend_handles_labels()\n\
1317 if len(h)>0 and len(l)>0:\n\
1318 \x20\x20\x20\x20leg=plt.legend(handlelength=3,ncol=1,loc='best')\n\
1319 \x20\x20\x20\x20add_to_ea(leg)\n\
1320 h,l=plt.gca().get_legend_handles_labels()\n\
1321 if len(h)>0 and len(l)>0:\n\
1322 \x20\x20\x20\x20leg=plt.legend(handlelength=3,ncol=1,loc='best')\n\
1323 \x20\x20\x20\x20add_to_ea(leg)\n";
1324 assert_eq!(plot.buffer, b);
1325 }
1326
1327 #[test]
1328 fn set_title_and_super_title_handle_quotes() {
1329 let mut p1 = Plot::new();
1330 p1.set_title("Without Quotes").set_super_title("Hi", None);
1331 let b: &str = "plt.title(r'Without Quotes')\n\
1332 st=plt.suptitle(r'Hi')\n\
1333 add_to_ea(st)\n";
1334 assert_eq!(p1.buffer, b);
1335
1336 let mut p2 = Plot::new();
1337 p2.set_title("Developer's Plot").set_super_title("Dev's", None);
1338 let b: &str = "plt.title(r'Developer’s Plot')\n\
1339 st=plt.suptitle(r'Dev’s')\n\
1340 add_to_ea(st)\n";
1341 assert_eq!(p2.buffer, b);
1342
1343 let mut p3 = Plot::new();
1344 p3.set_title("\"Look at This\"").set_super_title("\"Dev's\"", None);
1345 let b: &str = "plt.title(r'\"Look at This\"')\n\
1346 st=plt.suptitle(r'\"Dev’s\"')\n\
1347 add_to_ea(st)\n";
1348 assert_eq!(p3.buffer, b);
1349 }
1350
1351 #[test]
1352 fn set_super_title_handles_math_and_newline() {
1353 let mut plot = Plot::new();
1354 plot.set_super_title("$\\alpha$\\n$\\beta$", None);
1355 let b: &str = "st=plt.suptitle(r'$\\alpha$'+'\\n'+r'$\\beta$')\n\
1356 add_to_ea(st)\n";
1357 assert_eq!(plot.buffer, b);
1358 }
1359
1360 #[test]
1361 fn set_functions_work() {
1362 let mut plot = Plot::new();
1363 plot.set_show_errors(true)
1364 .set_equal_axes(true)
1365 .set_equal_axes(false)
1366 .set_hide_axes(true)
1367 .set_range_3d(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0)
1368 .set_range(-1.0, 1.0, -1.0, 1.0)
1369 .set_range_from_vec(&[0.0, 1.0, 0.0, 1.0])
1370 .set_xrange(-80.0, 800.0)
1371 .set_yrange(13.0, 130.0)
1372 .set_zrange(44.0, 444.0)
1373 .set_xmin(-3.0)
1374 .set_xmax(8.0)
1375 .set_ymin(-7.0)
1376 .set_ymax(33.0)
1377 .set_zmin(12.0)
1378 .set_zmax(34.0)
1379 .set_num_ticks_x(0)
1380 .set_num_ticks_x(8)
1381 .set_num_ticks_y(0)
1382 .set_num_ticks_y(5)
1383 .set_log_x(true)
1384 .set_log_y(true)
1385 .set_log_x(false)
1386 .set_log_y(false)
1387 .set_label_x("x-label")
1388 .set_label_y("y-label")
1389 .set_labels("x", "y")
1390 .set_camera(1.0, 10.0)
1391 .set_ticks_x(1.5, 0.5, "%.2f")
1392 .set_ticks_y(0.5, 0.1, "%g")
1393 .set_figure_size_inches(2.0, 2.0)
1394 .set_figure_size_points(7227.0, 7227.0)
1395 .clear_current_axes()
1396 .clear_current_figure();
1397 let b: &str = "set_equal_axes()\n\
1398 plt.gca().axes.set_aspect('auto')\n\
1399 plt.axis('off')\n\
1400 plt.gca().set_xlim(-1,1)\n\
1401 plt.gca().set_ylim(-1,1)\n\
1402 plt.gca().set_zlim(-1,1)\n\
1403 plt.axis([-1,1,-1,1])\n\
1404 plt.axis([0,1,0,1])\n\
1405 plt.gca().set_xlim([-80,800])\n\
1406 plt.gca().set_ylim([13,130])\n\
1407 plt.gca().set_zlim([44,444])\n\
1408 plt.gca().set_xlim([-3,None])\n\
1409 plt.gca().set_xlim([None,8])\n\
1410 plt.gca().set_ylim([-7,None])\n\
1411 plt.gca().set_ylim([None,33])\n\
1412 plt.gca().set_zlim([12,None])\n\
1413 plt.gca().set_zlim([None,34])\n\
1414 plt.gca().get_xaxis().set_ticks([])\n\
1415 plt.gca().get_xaxis().set_major_locator(tck.MaxNLocator(8))\n\
1416 plt.gca().get_yaxis().set_ticks([])\n\
1417 plt.gca().get_yaxis().set_major_locator(tck.MaxNLocator(5))\n\
1418 plt.gca().set_xscale('log')\n\
1419 plt.gca().set_yscale('log')\n\
1420 plt.gca().set_xscale('linear')\n\
1421 plt.gca().set_yscale('linear')\n\
1422 plt.gca().set_xlabel(r'x-label')\n\
1423 plt.gca().set_ylabel(r'y-label')\n\
1424 plt.gca().set_xlabel(r'x')\n\
1425 plt.gca().set_ylabel(r'y')\n\
1426 plt.gca().view_init(elev=1,azim=10)\n\
1427 major_locator = tck.MultipleLocator(1.5)\n\
1428 n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / 1.5\n\
1429 if n_ticks < major_locator.MAXTICKS * 0.9:\n\
1430 \x20\x20\x20\x20plt.gca().xaxis.set_major_locator(major_locator)\n\
1431 minor_locator = tck.MultipleLocator(0.5)\n\
1432 n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / 0.5\n\
1433 if n_ticks < minor_locator.MAXTICKS * 0.9:\n\
1434 \x20\x20\x20\x20plt.gca().xaxis.set_minor_locator(minor_locator)\n\
1435 major_formatter = tck.FormatStrFormatter(r'%.2f')\n\
1436 plt.gca().xaxis.set_major_formatter(major_formatter)\n\
1437 major_locator = tck.MultipleLocator(0.5)\n\
1438 n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / 0.5\n\
1439 if n_ticks < major_locator.MAXTICKS * 0.9:\n\
1440 \x20\x20\x20\x20plt.gca().yaxis.set_major_locator(major_locator)\n\
1441 minor_locator = tck.MultipleLocator(0.1)\n\
1442 n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / 0.1\n\
1443 if n_ticks < minor_locator.MAXTICKS * 0.9:\n\
1444 \x20\x20\x20\x20plt.gca().yaxis.set_minor_locator(minor_locator)\n\
1445 major_formatter = tck.FormatStrFormatter(r'%g')\n\
1446 plt.gca().yaxis.set_major_formatter(major_formatter)\n\
1447 plt.gcf().set_size_inches(2,2)\n\
1448 plt.gcf().set_size_inches(100,100)\n\
1449 plt.gca().cla()\n\
1450 plt.clf()\n";
1451 assert_eq!(plot.buffer, b);
1452 assert_eq!(plot.show_errors, true);
1453 }
1454
1455 #[test]
1456 fn set_functions_work_2() {
1457 let mut plot = Plot::new();
1458 plot.set_ticks_x_multiple_of_pi(0.0);
1459 let b: &str = "major_locator = tck.MultipleLocator(np.pi/2.0)\n\
1460 n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / (np.pi/2.0)\n\
1461 if n_ticks < major_locator.MAXTICKS * 0.9:\n\
1462 \x20\x20\x20\x20plt.gca().xaxis.set_major_locator(major_locator)\n\
1463 def multiple_of_pi_formatter(x, pos):\n\
1464 \x20\x20\x20\x20den = 2\n\
1465 \x20\x20\x20\x20num = int(np.rint(den*x/np.pi))\n\
1466 \x20\x20\x20\x20com = np.gcd(num,den)\n\
1467 \x20\x20\x20\x20(num,den) = (int(num/com),int(den/com))\n\
1468 \x20\x20\x20\x20if den==1:\n\
1469 \x20\x20\x20\x20\x20\x20\x20\x20if num==0: return r'$0$'\n\
1470 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\pi$'\n\
1471 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$-\\pi$'\n\
1472 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$%s\\pi$'%num\n\
1473 \x20\x20\x20\x20else:\n\
1474 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\frac{\\pi}{%s}$'%den\n\
1475 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$\\frac{-\\pi}{%s}$'%den\n\
1476 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$\\frac{%s\\pi}{%s}$'%(num,den)\n\
1477 major_formatter = tck.FuncFormatter(multiple_of_pi_formatter)\n\
1478 plt.gca().xaxis.set_major_formatter(major_formatter)\n";
1479 assert_eq!(plot.buffer, b);
1480
1481 let mut plot = Plot::new();
1482 plot.set_ticks_y_multiple_of_pi(0.0);
1483 let b: &str = "major_locator = tck.MultipleLocator(np.pi/2.0)\n\
1484 n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / (np.pi/2.0)\n\
1485 if n_ticks < major_locator.MAXTICKS * 0.9:\n\
1486 \x20\x20\x20\x20plt.gca().yaxis.set_major_locator(major_locator)\n\
1487 def multiple_of_pi_formatter(x, pos):\n\
1488 \x20\x20\x20\x20den = 2\n\
1489 \x20\x20\x20\x20num = int(np.rint(den*x/np.pi))\n\
1490 \x20\x20\x20\x20com = np.gcd(num,den)\n\
1491 \x20\x20\x20\x20(num,den) = (int(num/com),int(den/com))\n\
1492 \x20\x20\x20\x20if den==1:\n\
1493 \x20\x20\x20\x20\x20\x20\x20\x20if num==0: return r'$0$'\n\
1494 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\pi$'\n\
1495 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$-\\pi$'\n\
1496 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$%s\\pi$'%num\n\
1497 \x20\x20\x20\x20else:\n\
1498 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\frac{\\pi}{%s}$'%den\n\
1499 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$\\frac{-\\pi}{%s}$'%den\n\
1500 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$\\frac{%s\\pi}{%s}$'%(num,den)\n\
1501 major_formatter = tck.FuncFormatter(multiple_of_pi_formatter)\n\
1502 plt.gca().yaxis.set_major_formatter(major_formatter)\n";
1503 assert_eq!(plot.buffer, b);
1504
1505 let mut plot = Plot::new();
1506 plot.set_ticks_x_multiple_of_pi(1.0);
1507 let b: &str = "major_locator = tck.MultipleLocator(np.pi/2.0)\n\
1508 n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / (np.pi/2.0)\n\
1509 if n_ticks < major_locator.MAXTICKS * 0.9:\n\
1510 \x20\x20\x20\x20plt.gca().xaxis.set_major_locator(major_locator)\n\
1511 minor_locator = tck.MultipleLocator(1)\n\
1512 n_ticks = (plt.gca().axis()[1] - plt.gca().axis()[0]) / 1\n\
1513 if n_ticks < minor_locator.MAXTICKS * 0.9:\n\
1514 \x20\x20\x20\x20plt.gca().xaxis.set_minor_locator(minor_locator)\n\
1515 def multiple_of_pi_formatter(x, pos):\n\
1516 \x20\x20\x20\x20den = 2\n\
1517 \x20\x20\x20\x20num = int(np.rint(den*x/np.pi))\n\
1518 \x20\x20\x20\x20com = np.gcd(num,den)\n\
1519 \x20\x20\x20\x20(num,den) = (int(num/com),int(den/com))\n\
1520 \x20\x20\x20\x20if den==1:\n\
1521 \x20\x20\x20\x20\x20\x20\x20\x20if num==0: return r'$0$'\n\
1522 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\pi$'\n\
1523 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$-\\pi$'\n\
1524 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$%s\\pi$'%num\n\
1525 \x20\x20\x20\x20else:\n\
1526 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\frac{\\pi}{%s}$'%den\n\
1527 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$\\frac{-\\pi}{%s}$'%den\n\
1528 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$\\frac{%s\\pi}{%s}$'%(num,den)\n\
1529 major_formatter = tck.FuncFormatter(multiple_of_pi_formatter)\n\
1530 plt.gca().xaxis.set_major_formatter(major_formatter)\n";
1531 assert_eq!(plot.buffer, b);
1532
1533 let mut plot = Plot::new();
1534 plot.set_ticks_y_multiple_of_pi(1.0);
1535 let b: &str = "major_locator = tck.MultipleLocator(np.pi/2.0)\n\
1536 n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / (np.pi/2.0)\n\
1537 if n_ticks < major_locator.MAXTICKS * 0.9:\n\
1538 \x20\x20\x20\x20plt.gca().yaxis.set_major_locator(major_locator)\n\
1539 minor_locator = tck.MultipleLocator(1)\n\
1540 n_ticks = (plt.gca().axis()[3] - plt.gca().axis()[2]) / 1\n\
1541 if n_ticks < minor_locator.MAXTICKS * 0.9:\n\
1542 \x20\x20\x20\x20plt.gca().yaxis.set_minor_locator(minor_locator)\n\
1543 def multiple_of_pi_formatter(x, pos):\n\
1544 \x20\x20\x20\x20den = 2\n\
1545 \x20\x20\x20\x20num = int(np.rint(den*x/np.pi))\n\
1546 \x20\x20\x20\x20com = np.gcd(num,den)\n\
1547 \x20\x20\x20\x20(num,den) = (int(num/com),int(den/com))\n\
1548 \x20\x20\x20\x20if den==1:\n\
1549 \x20\x20\x20\x20\x20\x20\x20\x20if num==0: return r'$0$'\n\
1550 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\pi$'\n\
1551 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$-\\pi$'\n\
1552 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$%s\\pi$'%num\n\
1553 \x20\x20\x20\x20else:\n\
1554 \x20\x20\x20\x20\x20\x20\x20\x20if num==1: return r'$\\frac{\\pi}{%s}$'%den\n\
1555 \x20\x20\x20\x20\x20\x20\x20\x20elif num==-1: return r'$\\frac{-\\pi}{%s}$'%den\n\
1556 \x20\x20\x20\x20\x20\x20\x20\x20else: return r'$\\frac{%s\\pi}{%s}$'%(num,den)\n\
1557 major_formatter = tck.FuncFormatter(multiple_of_pi_formatter)\n\
1558 plt.gca().yaxis.set_major_formatter(major_formatter)\n";
1559 assert_eq!(plot.buffer, b);
1560 }
1561
1562 #[test]
1563 fn set_frame_functions_work() {
1564 let mut plot = Plot::new();
1565 plot.set_frame_border(false, false, false, false)
1566 .set_frame_border(true, true, true, true)
1567 .set_frame_borders(false);
1568 let b: &str = "plt.gca().spines['left'].set_visible(False)\n\
1569 plt.gca().spines['right'].set_visible(False)\n\
1570 plt.gca().spines['bottom'].set_visible(False)\n\
1571 plt.gca().spines['top'].set_visible(False)\n\
1572 plt.gca().spines['left'].set_visible(True)\n\
1573 plt.gca().spines['right'].set_visible(True)\n\
1574 plt.gca().spines['bottom'].set_visible(True)\n\
1575 plt.gca().spines['top'].set_visible(True)\n\
1576 plt.gca().spines['left'].set_visible(False)\n\
1577 plt.gca().spines['right'].set_visible(False)\n\
1578 plt.gca().spines['bottom'].set_visible(False)\n\
1579 plt.gca().spines['top'].set_visible(False)\n";
1580 assert_eq!(plot.buffer, b);
1581 }
1582
1583 #[test]
1584 fn additional_features_work() {
1585 let mut plot = Plot::new();
1586 plot.set_inv_x()
1587 .set_inv_y()
1588 .set_hide_xticks()
1589 .set_hide_yticks()
1590 .set_hide_zticks()
1591 .set_label_x_and_pad("X IS CLOSER NOW", -15.0)
1592 .set_label_y_and_pad("Y IS CLOSER NOW", -25.0)
1593 .set_label_z_and_pad("Z IS CLOSER NOW", -35.0)
1594 .extra("plt.show()\n");
1595 let b: &str = "plt.gca().invert_xaxis()\n\
1596 plt.gca().invert_yaxis()\n\
1597 plt.gca().set_xticklabels([])\n\
1598 plt.gca().set_yticklabels([])\n\
1599 plt.gca().set_zticklabels([])\n\
1600 plt.gca().set_xlabel(r'X IS CLOSER NOW',labelpad=-15)\n\
1601 plt.gca().set_ylabel(r'Y IS CLOSER NOW',labelpad=-25)\n\
1602 plt.gca().set_zlabel(r'Z IS CLOSER NOW',labelpad=-35)\n\
1603 plt.show()\n";
1604 assert_eq!(plot.buffer, b);
1605 }
1606
1607 #[test]
1608 fn gridspec_functions_work() {
1609 let mut plot = Plot::new();
1610 plot.set_gridspec("the_grid", 2, 2, "wspace=0.1,hspace=0.2")
1611 .set_subplot_grid("the_grid", "0:2", "0")
1612 .set_rotation_ticks_x(55.0)
1613 .set_rotation_ticks_y(45.0)
1614 .set_align_labels();
1615 let b: &str = "grid_the_grid=plt.GridSpec(2,2,wspace=0.1,hspace=0.2)\n\
1616 \nplt.subplot(grid_the_grid[0:2,0])\n\
1617 plt.gca().tick_params(axis='x',rotation=55)\n\
1618 plt.gca().tick_params(axis='y',rotation=45)\n\
1619 plt.gcf().align_labels()\n";
1620 assert_eq!(plot.buffer, b);
1621 }
1622
1623 #[test]
1624 fn set_labels_3d_works() {
1625 let mut plot = Plot::new();
1626 plot.set_label_z("Z").set_labels_3d("X", "Y", "Z");
1627 let b: &str = "plt.gca().set_zlabel(r'Z')\n\
1628 plt.gca().set_xlabel(r'X')\n\
1629 plt.gca().set_ylabel(r'Y')\n\
1630 plt.gca().set_zlabel(r'Z')\n";
1631 assert_eq!(plot.buffer, b);
1632 }
1633
1634 #[test]
1635 fn extra_functionality_works() {
1636 let mut plot = Plot::new();
1637 plot.set_horiz_line(-1.0, "blue", "-", 1.1)
1638 .set_vert_line(-2.0, "green", ":", 1.2)
1639 .set_cross(0.25, 0.75, "red", "--", 3.0);
1640 let b: &str = "plt.axhline(-1,color='blue',linestyle='-',linewidth=1.1)\n\
1641 plt.axvline(-2,color='green',linestyle=':',linewidth=1.2)\n\
1642 plt.axhline(0.75,color='red',linestyle='--',linewidth=3)\n\
1643 plt.axvline(0.25,color='red',linestyle='--',linewidth=3)\n";
1644 assert_eq!(plot.buffer, b);
1645 }
1646
1647 #[test]
1648 fn set_python_exe_works() {
1649 let mut plot = Plot::new();
1650 assert_eq!(plot.python_exe, "python3");
1651 plot.set_python_exe("python");
1652 assert_eq!(plot.python_exe, "python");
1653 }
1654}