1use super::{GraphMaker, StrError};
2use crate::conversions::{matrix_to_array, vector_to_array};
3use crate::{AsMatrix, AsVector};
4use num_traits::Num;
5use std::fmt::Write;
6
7#[derive(Clone, Copy, Debug)]
11pub enum PolyCode {
12 MoveTo,
16
17 LineTo,
21
22 Curve3,
26
27 Curve4,
31}
32
33pub struct Canvas {
129 edge_color: String, face_color: String, line_width: f64, line_style: String, arrow_scale: f64, arrow_style: String, text_color: String, text_align_horizontal: String, text_align_vertical: String, text_fontsize: f64, text_rotation: f64, alt_text_color: String, alt_text_align_horizontal: String, alt_text_align_vertical: String, alt_text_fontsize: f64, alt_text_rotation: f64, stop_clip: bool, shading: bool, glyph_line_width: f64, glyph_size: f64, glyph_color_x: String, glyph_color_y: String, glyph_color_z: String, glyph_label_x: String, glyph_label_y: String, glyph_label_z: String, glyph_label_color: String, glyph_bbox_opt: String, buffer: String, }
170
171impl Canvas {
172 pub fn new() -> Self {
174 Canvas {
175 edge_color: "#427ce5".to_string(),
177 face_color: String::new(),
178 line_width: 0.0,
179 line_style: String::new(),
180 arrow_scale: 0.0,
181 arrow_style: String::new(),
182 text_color: "#343434".to_string(),
184 text_align_horizontal: "center".to_string(),
185 text_align_vertical: "center".to_string(),
186 text_fontsize: 10.0,
187 text_rotation: 0.0,
188 alt_text_color: "#a81414".to_string(),
190 alt_text_align_horizontal: String::new(),
191 alt_text_align_vertical: String::new(),
192 alt_text_fontsize: 8.0,
193 alt_text_rotation: 45.0,
194 stop_clip: false,
196 shading: true,
197 glyph_line_width: 2.0,
199 glyph_size: 1.0,
200 glyph_color_x: "red".to_string(),
201 glyph_color_y: "green".to_string(),
202 glyph_color_z: "blue".to_string(),
203 glyph_label_x: "X".to_string(),
204 glyph_label_y: "Y".to_string(),
205 glyph_label_z: "Z".to_string(),
206 glyph_label_color: String::new(),
207 glyph_bbox_opt: "boxstyle='circle,pad=0.1',facecolor='white',edgecolor='None'".to_string(),
208 buffer: String::new(),
210 }
211 }
212
213 pub fn draw_arc<T>(&mut self, xc: T, yc: T, r: T, ini_angle: T, fin_angle: T)
215 where
216 T: std::fmt::Display + Num,
217 {
218 let opt = self.options_shared();
219 write!(
220 &mut self.buffer,
221 "p=pat.Arc(({},{}),2*{},2*{},theta1={},theta2={},angle=0{})\n\
222 plt.gca().add_patch(p)\n",
223 xc, yc, r, r, ini_angle, fin_angle, &opt
224 )
225 .unwrap();
226 }
227
228 pub fn draw_arrow<T>(&mut self, xi: T, yi: T, xf: T, yf: T)
230 where
231 T: std::fmt::Display + Num,
232 {
233 let opt_shared = self.options_shared();
234 let opt_arrow = self.options_arrow();
235 write!(
236 &mut self.buffer,
237 "p=pat.FancyArrowPatch(({},{}),({},{})\
238 ,shrinkA=0,shrinkB=0\
239 ,path_effects=[pff.Stroke(joinstyle='miter')]\
240 {}{})\n\
241 plt.gca().add_patch(p)\n",
242 xi, yi, xf, yf, &opt_shared, &&opt_arrow,
243 )
244 .unwrap();
245 }
246
247 pub fn draw_circle<T>(&mut self, xc: T, yc: T, r: T)
249 where
250 T: std::fmt::Display + Num,
251 {
252 let opt = self.options_shared();
253 write!(
254 &mut self.buffer,
255 "p=pat.Circle(({},{}),{}{})\n\
256 plt.gca().add_patch(p)\n",
257 xc, yc, r, &opt
258 )
259 .unwrap();
260 }
261
262 pub fn draw_triangles<'a, T, U, C>(&mut self, xx: &'a T, yy: &'a T, connectivity: &'a C) -> &mut Self
266 where
267 T: AsVector<'a, U>,
268 U: 'a + std::fmt::Display + Num,
269 C: AsMatrix<'a, usize>,
270 {
271 vector_to_array(&mut self.buffer, "xx", xx);
272 vector_to_array(&mut self.buffer, "yy", yy);
273 matrix_to_array(&mut self.buffer, "triangles", connectivity);
274 let opt = self.options_triangles();
275 write!(&mut self.buffer, "plt.triplot(xx,yy,triangles{})\n", &opt).unwrap();
276 self
277 }
278
279 pub fn draw_triangles_3d<'a, T, U, C>(&mut self, xx: &'a T, yy: &'a T, zz: &'a T, connectivity: &'a C) -> &mut Self
285 where
286 T: AsVector<'a, U>,
287 U: 'a + std::fmt::Display + Num,
288 C: AsMatrix<'a, usize>,
289 {
290 vector_to_array(&mut self.buffer, "xx", xx);
292 vector_to_array(&mut self.buffer, "yy", yy);
293 vector_to_array(&mut self.buffer, "zz", zz);
294 matrix_to_array(&mut self.buffer, "triangles", connectivity);
295
296 let opt = self.options_triangles_3d();
307
308 let shade = if self.shading { "True" } else { "False" };
310 write!(
311 &mut self.buffer,
312 "poly_collection=ax3d().plot_trisurf(xx,yy,zz,triangles=triangles,shade={}{})\n",
313 shade, &opt
314 )
315 .unwrap();
316
317 if self.face_color != "" {
319 write!(
320 &mut self.buffer,
321 "colors=np.array(['{}']*len(triangles))\n\
322 poly_collection.set_facecolor(colors)\n",
323 self.face_color
324 )
325 .unwrap();
326 }
327
328 self
330 }
331
332 pub fn polycurve_begin(&mut self) -> &mut Self {
339 write!(&mut self.buffer, "dat=[",).unwrap();
340 self
341 }
342
343 pub fn polycurve_add<T>(&mut self, x: T, y: T, code: PolyCode) -> &mut Self
350 where
351 T: std::fmt::Display + Num,
352 {
353 let keyword = match code {
354 PolyCode::MoveTo => "MOVETO",
355 PolyCode::LineTo => "LINETO",
356 PolyCode::Curve3 => "CURVE3",
357 PolyCode::Curve4 => "CURVE4",
358 };
359 write!(&mut self.buffer, "[pth.Path.{},({},{})],", keyword, x, y).unwrap();
360 self
361 }
362
363 pub fn polycurve_end(&mut self, closed: bool) -> &mut Self {
370 if closed {
371 write!(&mut self.buffer, "[pth.Path.CLOSEPOLY,(None,None)]").unwrap();
372 }
373 let opt = self.options_shared();
374 write!(
375 &mut self.buffer,
376 "]\n\
377 cmd,pts=zip(*dat)\n\
378 h=pth.Path(pts,cmd)\n\
379 p=pat.PathPatch(h{})\n\
380 plt.gca().add_patch(p)\n",
381 &opt
382 )
383 .unwrap();
384 self
385 }
386
387 pub fn draw_polycurve<'a, T, U>(&mut self, points: &'a T, codes: &[PolyCode], closed: bool) -> Result<(), StrError>
391 where
392 T: AsMatrix<'a, U>,
393 U: 'a + std::fmt::Display,
394 {
395 let (npoint, ndim) = points.size();
396 if npoint < 3 {
397 return Err("npoint must be ≥ 3");
398 }
399 if ndim != 2 {
400 return Err("ndim must be equal to 2");
401 }
402 if codes.len() != npoint {
403 return Err("codes.len() must be equal to npoint");
404 }
405 write!(
406 &mut self.buffer,
407 "dat=[[pth.Path.MOVETO,({},{})]",
408 points.at(0, 0),
409 points.at(0, 1)
410 )
411 .unwrap();
412 for i in 1..npoint {
413 let keyword = match codes[i] {
414 PolyCode::MoveTo => "MOVETO",
415 PolyCode::LineTo => "LINETO",
416 PolyCode::Curve3 => "CURVE3",
417 PolyCode::Curve4 => "CURVE4",
418 };
419 write!(
420 &mut self.buffer,
421 ",[pth.Path.{},({},{})]",
422 keyword,
423 points.at(i, 0),
424 points.at(i, 1)
425 )
426 .unwrap();
427 }
428 if closed {
429 write!(&mut self.buffer, ",[pth.Path.CLOSEPOLY,(None,None)]").unwrap();
430 }
431 let opt = self.options_shared();
432 write!(
433 &mut self.buffer,
434 "]\n\
435 cmd,pts=zip(*dat)\n\
436 h=pth.Path(pts,cmd)\n\
437 p=pat.PathPatch(h{})\n\
438 plt.gca().add_patch(p)\n",
439 &opt
440 )
441 .unwrap();
442 Ok(())
443 }
444
445 pub fn polyline_3d_begin(&mut self) -> &mut Self {
452 write!(&mut self.buffer, "xyz=np.array([").unwrap();
453 self
454 }
455
456 pub fn polyline_3d_add<T>(&mut self, x: T, y: T, z: T) -> &mut Self
463 where
464 T: std::fmt::Display + Num,
465 {
466 write!(&mut self.buffer, "[{},{},{}],", x, y, z).unwrap();
467 self
468 }
469
470 pub fn polyline_3d_end(&mut self) -> &mut Self {
477 let opt = self.options_line_3d();
478 write!(
479 &mut self.buffer,
480 "])\nax3d().plot(xyz[:,0],xyz[:,1],xyz[:,2]{})\n",
481 &opt
482 )
483 .unwrap();
484 self
485 }
486
487 pub fn draw_polyline<'a, T, U>(&mut self, points: &'a T, closed: bool)
489 where
490 T: AsMatrix<'a, U>,
491 U: 'a + std::fmt::Display + Num,
492 {
493 let (npoint, ndim) = points.size();
494 if npoint < 2 {
495 return;
496 }
497 if ndim == 2 {
498 write!(
499 &mut self.buffer,
500 "dat=[[pth.Path.MOVETO,({},{})]",
501 points.at(0, 0),
502 points.at(0, 1)
503 )
504 .unwrap();
505 for i in 1..npoint {
506 write!(
507 &mut self.buffer,
508 ",[pth.Path.LINETO,({},{})]",
509 points.at(i, 0),
510 points.at(i, 1)
511 )
512 .unwrap();
513 }
514 if closed {
515 write!(&mut self.buffer, ",[pth.Path.CLOSEPOLY,(None,None)]").unwrap();
516 }
517 let opt = self.options_shared();
518 write!(
519 &mut self.buffer,
520 "]\n\
521 cmd,pts=zip(*dat)\n\
522 h=pth.Path(pts,cmd)\n\
523 p=pat.PathPatch(h{})\n\
524 plt.gca().add_patch(p)\n",
525 &opt
526 )
527 .unwrap();
528 }
529 if ndim == 3 {
530 self.polyline_3d_begin();
531 for i in 0..npoint {
532 self.polyline_3d_add(points.at(i, 0), points.at(i, 1), points.at(i, 2));
533 }
534 if closed && npoint > 2 {
535 self.polyline_3d_add(points.at(0, 0), points.at(0, 1), points.at(0, 2));
536 }
537 self.polyline_3d_end();
538 }
539 }
540
541 pub fn draw_rectangle<T>(&mut self, x: T, y: T, width: T, height: T) -> &mut Self
543 where
544 T: std::fmt::Display + Num,
545 {
546 let opt = self.options_shared();
547 write!(
548 &mut self.buffer,
549 "p=pat.Rectangle(({},{}),{},{}{})\n\
550 plt.gca().add_patch(p)\n",
551 x, y, width, height, &opt
552 )
553 .unwrap();
554 self
555 }
556
557 pub fn draw_text<T>(&mut self, x: T, y: T, label: &str) -> &mut Self
559 where
560 T: std::fmt::Display + Num,
561 {
562 self.text(2, &[x, y, T::zero()], label, false);
563 self
564 }
565
566 pub fn draw_alt_text<T>(&mut self, x: T, y: T, label: &str) -> &mut Self
568 where
569 T: std::fmt::Display + Num,
570 {
571 self.text(2, &[x, y, T::zero()], label, true);
572 self
573 }
574
575 pub fn draw_glyph_3d<T>(&mut self, x: T, y: T, z: T) -> &mut Self
577 where
578 T: std::fmt::Display + Num,
579 {
580 let size = self.glyph_size;
581 let lx = &self.glyph_label_x;
582 let ly = &self.glyph_label_y;
583 let lz = &self.glyph_label_z;
584 let lw = self.glyph_line_width;
585 let r = &self.glyph_color_x;
586 let g = &self.glyph_color_y;
587 let b = &self.glyph_color_z;
588 let tr = if self.glyph_label_color == "" {
589 &self.glyph_color_x
590 } else {
591 &self.glyph_label_color
592 };
593 let tg = if self.glyph_label_color == "" {
594 &self.glyph_color_y
595 } else {
596 &self.glyph_label_color
597 };
598 let tb = if self.glyph_label_color == "" {
599 &self.glyph_color_z
600 } else {
601 &self.glyph_label_color
602 };
603 write!(
604 &mut self.buffer,
605 "plt.gca().plot([{x},{x}+{size}],[{y},{y}],[{z},{z}],color='{r}',linewidth={lw})\n\
606 plt.gca().plot([{x},{x}],[{y},{y}+{size}],[{z},{z}],color='{g}',linewidth={lw})\n\
607 plt.gca().plot([{x},{x}],[{y},{y}],[{z},{z}+{size}],color='{b}',linewidth={lw})\n\
608 tx=plt.gca().text({x}+{size},{y},{z},'{lx}',color='{tr}',ha='center',va='center')\n\
609 ty=plt.gca().text({x},{y}+{size},{z},'{ly}',color='{tg}',ha='center',va='center')\n\
610 tz=plt.gca().text({x},{y},{z}+{size},'{lz}',color='{tb}',ha='center',va='center')\n"
611 )
612 .unwrap();
613 if self.glyph_bbox_opt != "" {
614 write!(
615 &mut self.buffer,
616 "tx.set_bbox(dict({}))\n\
617 ty.set_bbox(dict({}))\n\
618 tz.set_bbox(dict({}))\n",
619 self.glyph_bbox_opt, self.glyph_bbox_opt, self.glyph_bbox_opt
620 )
621 .unwrap();
622 }
623 self
624 }
625
626 pub fn draw_grid(
636 &mut self,
637 xmin: &[f64],
638 xmax: &[f64],
639 ndiv: &[usize],
640 with_point_ids: bool,
641 with_cell_ids: bool,
642 ) -> Result<(), StrError> {
643 let ndim = ndiv.len();
645 if ndim < 2 || ndim > 3 {
646 return Err("len(ndiv) == ndim must be 2 or 3");
647 }
648 if xmin.len() != ndim {
649 return Err("size of xmin must equal ndim == len(ndiv)");
650 }
651 if xmax.len() != ndim {
652 return Err("size of xmax must equal ndim == len(ndiv)");
653 }
654
655 let mut npoint = [1; 3];
657 let mut delta = [0.0; 3];
658 for i in 0..ndim {
659 npoint[i] = ndiv[i] + 1;
660 delta[i] = xmax[i] - xmin[i];
661 if delta[i] <= 0.0 {
662 return Err("xmax must be greater than xmin");
663 }
664 delta[i] /= ndiv[i] as f64;
665 }
666
667 let mut a = [0.0; 3];
669 let mut b = [0.0; 3];
670
671 if ndim == 2 {
673 write!(&mut self.buffer, "dat=[\n").unwrap();
674 }
675 let opt = self.options_shared();
676 let mut id_point = 0;
677 for k in 0..npoint[2] {
678 if ndim == 3 {
679 a[2] = xmin[2] + delta[2] * (k as f64);
680 b[2] = a[2];
681 }
682
683 a[1] = xmin[1];
685 b[1] = xmax[1];
686 for i in 0..npoint[0] {
687 a[0] = xmin[0] + delta[0] * (i as f64);
688 b[0] = a[0];
689 self.line(ndim, &a, &b);
690 }
691
692 a[0] = xmin[0];
694 b[0] = xmax[0];
695 for j in 0..npoint[1] {
696 a[1] = xmin[1] + delta[1] * (j as f64);
697 b[1] = a[1];
698 self.line(ndim, &a, &b);
699 }
700
701 if ndim == 2 {
703 write!(
704 &mut self.buffer,
705 "]\n\
706 cmd,pts=zip(*dat)\n\
707 h=pth.Path(pts,cmd)\n\
708 p=pat.PathPatch(h{})\n\
709 plt.gca().add_patch(p)\n",
710 &opt
711 )
712 .unwrap();
713 }
714
715 if with_point_ids {
717 for j in 0..npoint[1] {
718 a[1] = xmin[1] + delta[1] * (j as f64);
719 for i in 0..npoint[0] {
720 a[0] = xmin[0] + delta[0] * (i as f64);
721 let txt = format!("{}", id_point);
722 self.text(ndim, &a, &txt, true);
723 id_point += 1;
724 }
725 }
726 }
727 }
728
729 if with_cell_ids {
731 let mut id_cell = 0;
732 let nz = if ndim == 2 { 1 } else { ndiv[2] };
733 for k in 0..nz {
734 if ndim == 3 {
735 a[2] = xmin[2] + delta[2] * (k as f64);
736 b[2] = a[2] + delta[2] / 2.0;
737 }
738 for j in 0..ndiv[1] {
739 a[1] = xmin[1] + delta[1] * (j as f64);
740 b[1] = a[1] + delta[1] / 2.0;
741 for i in 0..ndiv[0] {
742 a[0] = xmin[0] + delta[0] * (i as f64);
743 b[0] = a[0] + delta[0] / 2.0;
744 let txt = format!("{}", id_cell);
745 self.text(ndim, &b, &txt, false);
746 id_cell += 1;
747 }
748 }
749 }
750 }
751
752 if ndim == 3 {
754 a[2] = xmin[2];
755 b[2] = xmax[2];
756 for j in 0..npoint[1] {
757 a[1] = xmin[1] + delta[1] * (j as f64);
758 b[1] = a[1];
759 for i in 0..npoint[0] {
760 a[0] = xmin[0] + delta[0] * (i as f64);
761 b[0] = a[0];
762 self.line(ndim, &a, &b);
763 }
764 }
765 }
766
767 self.limits(ndim, xmin, xmax);
769
770 Ok(())
772 }
773
774 pub fn set_edge_color(&mut self, color: &str) -> &mut Self {
776 self.edge_color = String::from(color);
777 self
778 }
779
780 pub fn set_face_color(&mut self, color: &str) -> &mut Self {
782 self.face_color = String::from(color);
783 self
784 }
785
786 pub fn set_line_width(&mut self, width: f64) -> &mut Self {
788 self.line_width = width;
789 self
790 }
791
792 pub fn set_line_style(&mut self, style: &str) -> &mut Self {
799 self.line_style = String::from(style);
800 self
801 }
802
803 pub fn set_arrow_scale(&mut self, scale: f64) -> &mut Self {
805 self.arrow_scale = scale;
806 self
807 }
808
809 pub fn set_arrow_style(&mut self, style: &str) -> &mut Self {
829 self.arrow_style = String::from(style);
830 self
831 }
832
833 pub fn set_text_color(&mut self, color: &str) -> &mut Self {
835 self.text_color = String::from(color);
836 self
837 }
838
839 pub fn set_text_align_horizontal(&mut self, option: &str) -> &mut Self {
843 self.text_align_horizontal = String::from(option);
844 self
845 }
846
847 pub fn set_text_align_vertical(&mut self, option: &str) -> &mut Self {
851 self.text_align_vertical = String::from(option);
852 self
853 }
854
855 pub fn set_text_fontsize(&mut self, fontsize: f64) -> &mut Self {
857 self.text_fontsize = fontsize;
858 self
859 }
860
861 pub fn set_text_rotation(&mut self, rotation: f64) -> &mut Self {
863 self.text_rotation = rotation;
864 self
865 }
866
867 pub fn set_alt_text_color(&mut self, color: &str) -> &mut Self {
869 self.alt_text_color = String::from(color);
870 self
871 }
872
873 pub fn set_alt_text_align_horizontal(&mut self, option: &str) -> &mut Self {
877 self.alt_text_align_horizontal = String::from(option);
878 self
879 }
880
881 pub fn set_alt_text_align_vertical(&mut self, option: &str) -> &mut Self {
885 self.alt_text_align_vertical = String::from(option);
886 self
887 }
888
889 pub fn set_alt_text_fontsize(&mut self, fontsize: f64) -> &mut Self {
891 self.alt_text_fontsize = fontsize;
892 self
893 }
894
895 pub fn set_alt_text_rotation(&mut self, rotation: f64) -> &mut Self {
897 self.alt_text_rotation = rotation;
898 self
899 }
900
901 pub fn set_stop_clip(&mut self, flag: bool) -> &mut Self {
903 self.stop_clip = flag;
904 self
905 }
906
907 pub fn set_shading(&mut self, flag: bool) -> &mut Self {
913 self.shading = flag;
914 self
915 }
916
917 pub fn set_glyph_line_width(&mut self, width: f64) -> &mut Self {
919 self.glyph_line_width = width;
920 self
921 }
922
923 pub fn set_glyph_size(&mut self, size: f64) -> &mut Self {
925 self.glyph_size = size;
926 self
927 }
928
929 pub fn set_glyph_color_x(&mut self, color: &str) -> &mut Self {
931 self.glyph_color_x = String::from(color);
932 self
933 }
934
935 pub fn set_glyph_color_y(&mut self, color: &str) -> &mut Self {
937 self.glyph_color_y = String::from(color);
938 self
939 }
940
941 pub fn set_glyph_color_z(&mut self, color: &str) -> &mut Self {
943 self.glyph_color_z = String::from(color);
944 self
945 }
946
947 pub fn set_glyph_label_x(&mut self, label: &str) -> &mut Self {
949 self.glyph_label_x = String::from(label);
950 self
951 }
952
953 pub fn set_glyph_label_y(&mut self, label: &str) -> &mut Self {
955 self.glyph_label_y = String::from(label);
956 self
957 }
958
959 pub fn set_glyph_label_z(&mut self, label: &str) -> &mut Self {
961 self.glyph_label_z = String::from(label);
962 self
963 }
964
965 pub fn set_glyph_label_color(&mut self, label_clr: &str) -> &mut Self {
969 self.glyph_label_color = String::from(label_clr);
970 self
971 }
972
973 pub fn set_glyph_bbox(&mut self, bbox_dict: &str) -> &mut Self {
983 self.glyph_bbox_opt = String::from(bbox_dict);
984 self
985 }
986
987 fn options_triangles(&self) -> String {
989 let mut opt = String::new();
990 if self.edge_color != "" {
991 write!(&mut opt, ",color='{}'", self.edge_color).unwrap();
992 }
993 if self.line_width > 0.0 {
994 write!(&mut opt, ",linewidth={}", self.line_width).unwrap();
995 }
996 if self.line_style != "" {
997 write!(&mut opt, ",linestyle='{}'", self.line_style).unwrap();
998 }
999 if self.stop_clip {
1000 write!(&mut opt, ",clip_on=False").unwrap();
1001 }
1002 opt
1003 }
1004
1005 fn options_triangles_3d(&self) -> String {
1007 let mut opt = String::new();
1008 if self.edge_color != "" {
1009 write!(&mut opt, ",edgecolor='{}'", self.edge_color).unwrap();
1010 }
1011 if self.line_width > 0.0 {
1012 write!(&mut opt, ",linewidth={}", self.line_width).unwrap();
1013 }
1014 if self.line_style != "" {
1015 write!(&mut opt, ",linestyle='{}'", self.line_style).unwrap();
1016 }
1017 if self.stop_clip {
1018 write!(&mut opt, ",clip_on=False").unwrap();
1019 }
1020 opt
1021 }
1022
1023 fn options_shared(&self) -> String {
1025 let mut opt = String::new();
1026 if self.edge_color != "" {
1027 write!(&mut opt, ",edgecolor='{}'", self.edge_color).unwrap();
1028 }
1029 if self.face_color != "" {
1030 write!(&mut opt, ",facecolor='{}'", self.face_color).unwrap();
1031 }
1032 if self.line_width > 0.0 {
1033 write!(&mut opt, ",linewidth={}", self.line_width).unwrap();
1034 }
1035 if self.line_style != "" {
1036 write!(&mut opt, ",linestyle='{}'", self.line_style).unwrap();
1037 }
1038 if self.stop_clip {
1039 write!(&mut opt, ",clip_on=False").unwrap();
1040 }
1041 opt
1042 }
1043
1044 fn options_arrow(&self) -> String {
1046 let mut opt = String::new();
1047 if self.arrow_scale > 0.0 {
1048 write!(&mut opt, ",mutation_scale={}", self.arrow_scale).unwrap();
1049 }
1050 if self.arrow_style != "" {
1051 write!(&mut opt, ",arrowstyle='{}'", self.arrow_style).unwrap();
1052 }
1053 opt
1054 }
1055
1056 fn options_text(&self) -> String {
1058 let mut opt = String::new();
1059 if self.text_color != "" {
1060 write!(&mut opt, ",color='{}'", self.text_color).unwrap();
1061 }
1062 if self.text_align_horizontal != "" {
1063 write!(&mut opt, ",ha='{}'", self.text_align_horizontal).unwrap();
1064 }
1065 if self.text_align_vertical != "" {
1066 write!(&mut opt, ",va='{}'", self.text_align_vertical).unwrap();
1067 }
1068 if self.text_fontsize > 0.0 {
1069 write!(&mut opt, ",fontsize={}", self.text_fontsize).unwrap();
1070 }
1071 if self.text_rotation > 0.0 {
1072 write!(&mut opt, ",rotation={}", self.text_rotation).unwrap();
1073 }
1074 opt
1075 }
1076
1077 fn options_alt_text(&self) -> String {
1079 let mut opt = String::new();
1080 if self.alt_text_color != "" {
1081 write!(&mut opt, ",color='{}'", self.alt_text_color).unwrap();
1082 }
1083 if self.alt_text_align_horizontal != "" {
1084 write!(&mut opt, ",ha='{}'", self.alt_text_align_horizontal).unwrap();
1085 }
1086 if self.alt_text_align_vertical != "" {
1087 write!(&mut opt, ",va='{}'", self.alt_text_align_vertical).unwrap();
1088 }
1089 if self.alt_text_fontsize > 0.0 {
1090 write!(&mut opt, ",fontsize={}", self.alt_text_fontsize).unwrap();
1091 }
1092 if self.alt_text_rotation > 0.0 {
1093 write!(&mut opt, ",rotation={}", self.alt_text_rotation).unwrap();
1094 }
1095 opt
1096 }
1097
1098 fn options_line_3d(&self) -> String {
1100 let mut opt = String::new();
1101 if self.edge_color != "" {
1102 write!(&mut opt, ",color='{}'", self.edge_color).unwrap();
1103 }
1104 if self.line_width > 0.0 {
1105 write!(&mut opt, ",linewidth={}", self.line_width).unwrap();
1106 }
1107 if self.line_style != "" {
1108 write!(&mut opt, ",linestyle='{}'", self.line_style).unwrap();
1109 }
1110 opt
1111 }
1112
1113 fn line<T>(&mut self, ndim: usize, a: &[T; 3], b: &[T; 3])
1115 where
1116 T: std::fmt::Display,
1117 {
1118 if ndim == 2 {
1119 write!(
1120 &mut self.buffer,
1121 " [pth.Path.MOVETO,({},{})],[pth.Path.LINETO,({},{})],\n",
1122 a[0], a[1], b[0], b[1]
1123 )
1124 .unwrap();
1125 } else {
1126 let opt = self.options_line_3d();
1127 write!(
1128 &mut self.buffer,
1129 "ax3d().plot([{},{}],[{},{}],[{},{}]{})\n",
1130 a[0], b[0], a[1], b[1], a[2], b[2], opt,
1131 )
1132 .unwrap();
1133 }
1134 }
1135
1136 fn text<T>(&mut self, ndim: usize, a: &[T; 3], txt: &str, alternative: bool)
1138 where
1139 T: std::fmt::Display,
1140 {
1141 let opt = if alternative {
1142 self.options_alt_text()
1143 } else {
1144 self.options_text()
1145 };
1146 if ndim == 2 {
1147 write!(&mut self.buffer, "plt.text({},{},'{}'{})\n", a[0], a[1], txt, &opt).unwrap();
1148 } else {
1149 write!(
1150 &mut self.buffer,
1151 "ax3d().text({},{},{},'{}'{})\n",
1152 a[0], a[1], a[2], txt, &opt
1153 )
1154 .unwrap();
1155 }
1156 }
1157
1158 fn limits(&mut self, ndim: usize, xmin: &[f64], xmax: &[f64]) {
1160 const FACTOR: f64 = 0.1;
1161 let mut gap = [0.0; 3];
1162 for i in 0..ndim {
1163 gap[i] = (xmax[i] - xmin[i]) * FACTOR;
1164 }
1165 if ndim == 2 {
1166 write!(
1167 &mut self.buffer,
1168 "plt.axis([{},{},{},{}])\n",
1169 xmin[0] - gap[0],
1170 xmax[0] + gap[0],
1171 xmin[1] - gap[1],
1172 xmax[1] + gap[1]
1173 )
1174 .unwrap();
1175 } else {
1176 write!(
1177 &mut self.buffer,
1178 "ax3d().set_xlim3d({},{})\n\
1179 ax3d().set_ylim3d({},{})\n\
1180 ax3d().set_zlim3d({},{})\n",
1181 xmin[0] - gap[0],
1182 xmax[0] + gap[0],
1183 xmin[1] - gap[1],
1184 xmax[1] + gap[1],
1185 xmin[2] - gap[2],
1186 xmax[2] + gap[2]
1187 )
1188 .unwrap();
1189 }
1190 }
1191}
1192
1193impl GraphMaker for Canvas {
1194 fn get_buffer<'a>(&'a self) -> &'a String {
1195 &self.buffer
1196 }
1197 fn clear_buffer(&mut self) {
1198 self.buffer.clear();
1199 }
1200}
1201
1202#[cfg(test)]
1205mod tests {
1206 use super::Canvas;
1207 use crate::{GraphMaker, PolyCode};
1208
1209 #[test]
1210 fn derive_works() {
1211 let code = PolyCode::Curve3;
1212 let clone = code.clone();
1213 let correct = "Curve3";
1214 assert_eq!(format!("{:?}", code), correct);
1215 assert_eq!(format!("{:?}", clone), correct);
1216 }
1217
1218 #[test]
1219 fn new_works() {
1220 let canvas = Canvas::new();
1221 assert_eq!(canvas.edge_color.len(), 7);
1222 assert_eq!(canvas.face_color.len(), 0);
1223 assert_eq!(canvas.line_width, 0.0);
1224 assert_eq!(canvas.line_style.len(), 0);
1225 assert_eq!(canvas.arrow_scale, 0.0);
1226 assert_eq!(canvas.arrow_style.len(), 0);
1227 assert_eq!(canvas.buffer.len(), 0);
1228 }
1229
1230 #[test]
1231 fn options_shared_works() {
1232 let mut canvas = Canvas::new();
1233 canvas
1234 .set_edge_color("red")
1235 .set_face_color("blue")
1236 .set_line_width(2.5)
1237 .set_line_style("--")
1238 .set_stop_clip(true);
1239 let opt = canvas.options_shared();
1240 assert_eq!(
1241 opt,
1242 ",edgecolor='red'\
1243 ,facecolor='blue'\
1244 ,linewidth=2.5\
1245 ,linestyle='--'\
1246 ,clip_on=False"
1247 );
1248 }
1249
1250 #[test]
1251 fn options_arrow_works() {
1252 let mut canvas = Canvas::new();
1253 canvas.set_arrow_scale(25.0).set_arrow_style("fancy");
1254 let opt = canvas.options_arrow();
1255 assert_eq!(
1256 opt,
1257 ",mutation_scale=25\
1258 ,arrowstyle='fancy'"
1259 );
1260 }
1261
1262 #[test]
1263 fn options_text_works() {
1264 let mut canvas = Canvas::new();
1265 canvas
1266 .set_text_color("red")
1267 .set_text_align_horizontal("center")
1268 .set_text_align_vertical("center")
1269 .set_text_fontsize(8.0)
1270 .set_text_rotation(45.0);
1271 let opt = canvas.options_text();
1272 assert_eq!(
1273 opt,
1274 ",color='red'\
1275 ,ha='center'\
1276 ,va='center'\
1277 ,fontsize=8\
1278 ,rotation=45"
1279 );
1280 }
1281
1282 #[test]
1283 fn options_alt_text_works() {
1284 let mut canvas = Canvas::new();
1285 canvas
1286 .set_alt_text_color("blue")
1287 .set_alt_text_align_horizontal("right")
1288 .set_alt_text_align_vertical("bottom")
1289 .set_alt_text_fontsize(10.0)
1290 .set_alt_text_rotation(30.0);
1291 let opt = canvas.options_alt_text();
1292 assert_eq!(
1293 opt,
1294 ",color='blue'\
1295 ,ha='right'\
1296 ,va='bottom'\
1297 ,fontsize=10\
1298 ,rotation=30"
1299 );
1300 }
1301
1302 #[test]
1303 fn options_line_3d_works() {
1304 let mut canvas = Canvas::new();
1305 canvas.set_edge_color("red");
1306 let opt = canvas.options_line_3d();
1307 assert_eq!(opt, ",color='red'");
1308
1309 let mut canvas = Canvas::new();
1310 canvas.set_edge_color("red").set_line_width(5.0).set_line_style(":");
1311 let opt = canvas.options_line_3d();
1312 assert_eq!(opt, ",color='red',linewidth=5,linestyle=':'");
1313 }
1314
1315 #[test]
1316 fn glyph_setters_work() {
1317 let mut canvas = Canvas::new();
1318 canvas
1319 .set_glyph_line_width(4.5)
1320 .set_glyph_size(2.0)
1321 .set_glyph_color_x("orange")
1322 .set_glyph_color_y("cyan")
1323 .set_glyph_color_z("magenta")
1324 .set_glyph_label_x("Ux")
1325 .set_glyph_label_y("Uy")
1326 .set_glyph_label_z("Uz");
1327 assert_eq!(canvas.glyph_line_width, 4.5);
1328 assert_eq!(canvas.glyph_size, 2.0);
1329 assert_eq!(canvas.glyph_color_x, "orange");
1330 assert_eq!(canvas.glyph_color_y, "cyan");
1331 assert_eq!(canvas.glyph_color_z, "magenta");
1332 assert_eq!(canvas.glyph_label_x, "Ux");
1333 assert_eq!(canvas.glyph_label_y, "Uy");
1334 assert_eq!(canvas.glyph_label_z, "Uz");
1335 }
1336
1337 #[test]
1338 fn line_works() {
1339 let mut canvas = Canvas::new();
1340 let a = [0.0; 3];
1341 let b = [0.0; 3];
1342 canvas.line(2, &a, &b);
1343 canvas.line(3, &a, &b);
1344 assert_eq!(
1345 canvas.buffer,
1346 "\x20\x20\x20\x20[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(0,0)],\n\
1347 ax3d().plot([0,0],[0,0],[0,0],color='#427ce5')\n"
1348 );
1349 canvas.clear_buffer();
1350 assert_eq!(canvas.buffer, "");
1351 }
1352
1353 #[test]
1354 fn text_works() {
1355 let mut canvas = Canvas::new();
1356 let a = [0.0; 3];
1357 canvas.text(2, &a, "hello", true);
1358 canvas.text(3, &a, "hello", false);
1359 assert_eq!(
1360 canvas.buffer,
1361 "plt.text(0,0,'hello',color='#a81414',fontsize=8,rotation=45)\n\
1362 ax3d().text(0,0,0,'hello',color='#343434',ha='center',va='center',fontsize=10)\n"
1363 );
1364 }
1365
1366 #[test]
1367 fn limits_works() {
1368 let mut canvas = Canvas::new();
1369 let xmin = [0.0; 3];
1370 let xmax = [0.0; 3];
1371 canvas.limits(2, &xmin, &xmax);
1372 canvas.limits(3, &xmin, &xmax);
1373 assert_eq!(
1374 canvas.buffer,
1375 "plt.axis([0,0,0,0])\n\
1376 ax3d().set_xlim3d(0,0)\n\
1377 ax3d().set_ylim3d(0,0)\n\
1378 ax3d().set_zlim3d(0,0)\n"
1379 );
1380 }
1381
1382 #[test]
1383 fn arc_works() {
1384 let mut canvas = Canvas::new();
1385 canvas.draw_arc(0.0, 0.0, 1.0, 30.0, 60.0);
1386 let b: &str = "p=pat.Arc((0,0),2*1,2*1,theta1=30,theta2=60,angle=0,edgecolor='#427ce5')\n\
1387 plt.gca().add_patch(p)\n";
1388 assert_eq!(canvas.buffer, b);
1389 }
1390
1391 #[test]
1392 fn arrow_woks() {
1393 let mut canvas = Canvas::new();
1394 canvas.draw_arrow(0.0, 0.0, 1.0, 1.0);
1395 let b: &str =
1396 "p=pat.FancyArrowPatch((0,0),(1,1),shrinkA=0,shrinkB=0,path_effects=[pff.Stroke(joinstyle='miter')],edgecolor='#427ce5')\n\
1397 plt.gca().add_patch(p)\n";
1398 assert_eq!(canvas.buffer, b);
1399 }
1400
1401 #[test]
1402 fn circle_works() {
1403 let mut canvas = Canvas::new();
1404 canvas.draw_circle(0.0, 0.0, 1.0);
1405 let b: &str = "p=pat.Circle((0,0),1,edgecolor='#427ce5')\n\
1406 plt.gca().add_patch(p)\n";
1407 assert_eq!(canvas.buffer, b);
1408 }
1409
1410 #[test]
1411 fn polycurve_methods_work() {
1412 let mut canvas = Canvas::new();
1414 canvas.polycurve_begin();
1415 assert_eq!(canvas.buffer, "dat=[");
1416 canvas.polycurve_add(0, 0, PolyCode::MoveTo);
1417 assert_eq!(canvas.buffer, "dat=[[pth.Path.MOVETO,(0,0)],");
1418 canvas.polycurve_add(1, 0, PolyCode::LineTo);
1419 assert_eq!(canvas.buffer, "dat=[[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(1,0)],");
1420 canvas.polycurve_add(2, 0, PolyCode::Curve3);
1421 assert_eq!(
1422 canvas.buffer,
1423 "dat=[[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(1,0)],[pth.Path.CURVE3,(2,0)],"
1424 );
1425 canvas.polycurve_add(3, 0, PolyCode::Curve4);
1426 assert_eq!(
1427 canvas.buffer,
1428 "dat=[[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(1,0)],[pth.Path.CURVE3,(2,0)],[pth.Path.CURVE4,(3,0)],"
1429 );
1430 canvas.polycurve_end(true);
1431 assert_eq!(
1432 canvas.buffer,
1433 "dat=[[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(1,0)],[pth.Path.CURVE3,(2,0)],[pth.Path.CURVE4,(3,0)],[pth.Path.CLOSEPOLY,(None,None)]]\n\
1434 cmd,pts=zip(*dat)\n\
1435 h=pth.Path(pts,cmd)\n\
1436 p=pat.PathPatch(h,edgecolor='#427ce5')\n\
1437 plt.gca().add_patch(p)\n"
1438 );
1439 }
1440
1441 #[test]
1442 fn polycurve_capture_errors() {
1443 let mut canvas = Canvas::new();
1444 assert_eq!(
1445 canvas.draw_polycurve(&[[0, 0]], &[PolyCode::MoveTo], true).err(),
1446 Some("npoint must be ≥ 3")
1447 );
1448 assert_eq!(
1449 canvas
1450 .draw_polycurve(
1451 &[[0], [0], [0]],
1452 &[PolyCode::MoveTo, PolyCode::LineTo, PolyCode::LineTo],
1453 true
1454 )
1455 .err(),
1456 Some("ndim must be equal to 2")
1457 );
1458 assert_eq!(
1459 canvas
1460 .draw_polycurve(&[[0, 0], [0, 0], [0, 0]], &[PolyCode::MoveTo], true)
1461 .err(),
1462 Some("codes.len() must be equal to npoint")
1463 );
1464 }
1465
1466 #[test]
1467 fn polycurve_works() {
1468 let mut canvas = Canvas::new();
1469 let points = &[[0, 0], [1, 0], [1, 1]];
1470 let codes = &[PolyCode::MoveTo, PolyCode::Curve3, PolyCode::Curve3];
1471 canvas.draw_polycurve(points, codes, true).unwrap();
1472 let b: &str = "dat=[[pth.Path.MOVETO,(0,0)],[pth.Path.CURVE3,(1,0)],[pth.Path.CURVE3,(1,1)],[pth.Path.CLOSEPOLY,(None,None)]]\n\
1473 cmd,pts=zip(*dat)\n\
1474 h=pth.Path(pts,cmd)\n\
1475 p=pat.PathPatch(h,edgecolor='#427ce5')\n\
1476 plt.gca().add_patch(p)\n";
1477 assert_eq!(canvas.buffer, b);
1478 }
1479
1480 #[test]
1481 fn polyline_works_2d() {
1482 let mut canvas = Canvas::new();
1483 let points = &[[1.0, 1.0], [2.0, 1.0], [1.5, 1.866]];
1484 canvas.draw_polyline(points, true);
1485 let b: &str = "dat=[[pth.Path.MOVETO,(1,1)],[pth.Path.LINETO,(2,1)],[pth.Path.LINETO,(1.5,1.866)],[pth.Path.CLOSEPOLY,(None,None)]]\n\
1486 cmd,pts=zip(*dat)\n\
1487 h=pth.Path(pts,cmd)\n\
1488 p=pat.PathPatch(h,edgecolor='#427ce5')\n\
1489 plt.gca().add_patch(p)\n";
1490 assert_eq!(canvas.buffer, b);
1491 }
1492
1493 #[test]
1494 fn polyline_3d_methods_work() {
1495 let mut canvas = Canvas::new();
1496 canvas
1497 .polyline_3d_begin()
1498 .polyline_3d_add(1, 2, 3)
1499 .polyline_3d_add(4, 5, 6)
1500 .polyline_3d_end();
1501 let b: &str = "\
1502 xyz=np.array([[1,2,3],[4,5,6],])\n\
1503 ax3d().plot(xyz[:,0],xyz[:,1],xyz[:,2],color='#427ce5')\n";
1504 assert_eq!(canvas.buffer, b);
1505 }
1506
1507 #[test]
1508 fn polyline_works_3d() {
1509 let mut nothing = Canvas::new();
1510 nothing.draw_polyline(&[[0.0, 0.0]], true);
1511 assert_eq!(nothing.buffer, "");
1512
1513 #[rustfmt::skip]
1514 let points = &[
1515 [2.0, 1.0, 0.0],
1516 [0.0, 1.0, 0.0],
1517 [0.0, 1.0, 3.0],
1518 [2.0, 1.0, 3.0],
1519 ];
1520
1521 let mut open = Canvas::new();
1522 open.draw_polyline(points, false);
1523 let b: &str = "\
1524 xyz=np.array([[2,1,0],[0,1,0],[0,1,3],[2,1,3],])\n\
1525 ax3d().plot(xyz[:,0],xyz[:,1],xyz[:,2],color='#427ce5')\n";
1526 assert_eq!(open.buffer, b);
1527
1528 let mut closed = Canvas::new();
1529 closed.draw_polyline(points, true);
1530 let b: &str = "\
1531 xyz=np.array([[2,1,0],[0,1,0],[0,1,3],[2,1,3],[2,1,0],])\n\
1532 ax3d().plot(xyz[:,0],xyz[:,1],xyz[:,2],color='#427ce5')\n";
1533 assert_eq!(closed.buffer, b);
1534
1535 #[rustfmt::skip]
1536 let points = &[
1537 [2.0, 1.0, 0.0],
1538 [0.0, 1.0, 0.0],
1539 ];
1540
1541 let mut closed_few_points = Canvas::new();
1542 closed_few_points.draw_polyline(points, true);
1543 let b: &str = "\
1544 xyz=np.array([[2,1,0],[0,1,0],])\n\
1545 ax3d().plot(xyz[:,0],xyz[:,1],xyz[:,2],color='#427ce5')\n";
1546 assert_eq!(closed_few_points.buffer, b);
1547 }
1548
1549 #[test]
1550 fn grid_fails_on_wrong_input() {
1551 let mut canvas = Canvas::new();
1552 let res = canvas.draw_grid(&[0.0, 0.0], &[1.0, 1.0], &[1], true, false);
1553 assert_eq!(res, Err("len(ndiv) == ndim must be 2 or 3"));
1554 let res = canvas.draw_grid(&[0.0], &[1.0, 1.0], &[1, 1], true, false);
1555 assert_eq!(res, Err("size of xmin must equal ndim == len(ndiv)"));
1556 let res = canvas.draw_grid(&[0.0, 0.0], &[1.0], &[1, 1], true, false);
1557 assert_eq!(res, Err("size of xmax must equal ndim == len(ndiv)"));
1558 let res = canvas.draw_grid(&[0.0, 0.0], &[0.0, 1.0], &[1, 1], true, false);
1559 assert_eq!(res, Err("xmax must be greater than xmin"));
1560 }
1561
1562 #[test]
1563 fn grid_no_ids_works() {
1564 let mut canvas = Canvas::new();
1565 canvas
1566 .draw_grid(&[0.0, 0.0], &[1.0, 1.0], &[1, 1], false, false)
1567 .unwrap();
1568 let b: &str = "dat=[\n\
1569 \x20\x20\x20\x20[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(0,1)],\n\
1570 \x20\x20\x20\x20[pth.Path.MOVETO,(1,0)],[pth.Path.LINETO,(1,1)],\n\
1571 \x20\x20\x20\x20[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(1,0)],\n\
1572 \x20\x20\x20\x20[pth.Path.MOVETO,(0,1)],[pth.Path.LINETO,(1,1)],\n\
1573 ]\n\
1574 cmd,pts=zip(*dat)\n\
1575 h=pth.Path(pts,cmd)\n\
1576 p=pat.PathPatch(h,edgecolor='#427ce5')\n\
1577 plt.gca().add_patch(p)\n\
1578 plt.axis([-0.1,1.1,-0.1,1.1])\n";
1579 assert_eq!(canvas.buffer, b);
1580 }
1581
1582 #[test]
1583 fn grid_2d_works() {
1584 let mut canvas = Canvas::new();
1585 canvas.draw_grid(&[0.0, 0.0], &[1.0, 1.0], &[1, 1], true, true).unwrap();
1586 let b: &str = "dat=[\n\
1587 \x20\x20\x20\x20[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(0,1)],\n\
1588 \x20\x20\x20\x20[pth.Path.MOVETO,(1,0)],[pth.Path.LINETO,(1,1)],\n\
1589 \x20\x20\x20\x20[pth.Path.MOVETO,(0,0)],[pth.Path.LINETO,(1,0)],\n\
1590 \x20\x20\x20\x20[pth.Path.MOVETO,(0,1)],[pth.Path.LINETO,(1,1)],\n\
1591 ]\n\
1592 cmd,pts=zip(*dat)\n\
1593 h=pth.Path(pts,cmd)\n\
1594 p=pat.PathPatch(h,edgecolor='#427ce5')\n\
1595 plt.gca().add_patch(p)\n\
1596 plt.text(0,0,'0',color='#a81414',fontsize=8,rotation=45)\n\
1597 plt.text(1,0,'1',color='#a81414',fontsize=8,rotation=45)\n\
1598 plt.text(0,1,'2',color='#a81414',fontsize=8,rotation=45)\n\
1599 plt.text(1,1,'3',color='#a81414',fontsize=8,rotation=45)\n\
1600 plt.text(0.5,0.5,'0',color='#343434',ha='center',va='center',fontsize=10)\n\
1601 plt.axis([-0.1,1.1,-0.1,1.1])\n";
1602 assert_eq!(canvas.buffer, b);
1603 }
1604
1605 #[test]
1606 fn grid_3d_works() {
1607 let mut canvas = Canvas::new();
1608 canvas
1609 .draw_grid(&[0.0, 0.0, 0.0], &[1.0, 1.0, 1.0], &[1, 1, 1], true, true)
1610 .unwrap();
1611 let b: &str = "\
1612 ax3d().plot([0,0],[0,1],[0,0],color='#427ce5')\n\
1613 ax3d().plot([1,1],[0,1],[0,0],color='#427ce5')\n\
1614 ax3d().plot([0,1],[0,0],[0,0],color='#427ce5')\n\
1615 ax3d().plot([0,1],[1,1],[0,0],color='#427ce5')\n\
1616 ax3d().text(0,0,0,'0',color='#a81414',fontsize=8,rotation=45)\n\
1617 ax3d().text(1,0,0,'1',color='#a81414',fontsize=8,rotation=45)\n\
1618 ax3d().text(0,1,0,'2',color='#a81414',fontsize=8,rotation=45)\n\
1619 ax3d().text(1,1,0,'3',color='#a81414',fontsize=8,rotation=45)\n\
1620 ax3d().plot([0,0],[0,1],[1,1],color='#427ce5')\n\
1621 ax3d().plot([1,1],[0,1],[1,1],color='#427ce5')\n\
1622 ax3d().plot([0,1],[0,0],[1,1],color='#427ce5')\n\
1623 ax3d().plot([0,1],[1,1],[1,1],color='#427ce5')\n\
1624 ax3d().text(0,0,1,'4',color='#a81414',fontsize=8,rotation=45)\n\
1625 ax3d().text(1,0,1,'5',color='#a81414',fontsize=8,rotation=45)\n\
1626 ax3d().text(0,1,1,'6',color='#a81414',fontsize=8,rotation=45)\n\
1627 ax3d().text(1,1,1,'7',color='#a81414',fontsize=8,rotation=45)\n\
1628 ax3d().text(0.5,0.5,0.5,'0',color='#343434',ha='center',va='center',fontsize=10)\n\
1629 ax3d().plot([0,0],[0,0],[0,1],color='#427ce5')\n\
1630 ax3d().plot([1,1],[0,0],[0,1],color='#427ce5')\n\
1631 ax3d().plot([0,0],[1,1],[0,1],color='#427ce5')\n\
1632 ax3d().plot([1,1],[1,1],[0,1],color='#427ce5')\n\
1633 ax3d().set_xlim3d(-0.1,1.1)\n\
1634 ax3d().set_ylim3d(-0.1,1.1)\n\
1635 ax3d().set_zlim3d(-0.1,1.1)\n";
1636 assert_eq!(canvas.buffer, b);
1637 }
1638}