@@ -51,6 +51,7 @@ module pyplot_module
51
51
52
52
procedure , public :: add_plot ! ! add a 2d plot to pyplot instance
53
53
procedure , public :: add_3d_plot ! ! add a 3d plot to pyplot instance
54
+ procedure , public :: add_sphere ! ! add a 3d sphere to pyplot instance
54
55
procedure , public :: add_contour ! ! add a contour plot to pyplot instance
55
56
procedure , public :: add_bar ! ! add a barplot to pyplot instance
56
57
procedure , public :: add_imshow ! ! add an image plot (using `imshow`)
@@ -549,6 +550,83 @@ subroutine add_3d_plot(me, x, y, z, label, linestyle, markersize, linewidth, ist
549
550
end subroutine add_3d_plot
550
551
! *****************************************************************************************
551
552
553
+ ! *****************************************************************************************
554
+ ! > author: Jacob Williams
555
+ !
556
+ ! Add a sphere to a 3D x,y,z plot.
557
+ !
558
+ ! @note Must initialize the class with `mplot3d=.true.` and `use_numpy=.true.`.
559
+
560
+ subroutine add_sphere (me , r , xc , yc , zc , n_facets , linewidth , antialiased , color , istat )
561
+
562
+ implicit none
563
+
564
+ class(pyplot), intent (inout ) :: me ! ! pyplot handler
565
+ real (wp), intent (in ) :: r ! ! radius of the sphere
566
+ real (wp), intent (in ) :: xc ! ! x value of sphere center
567
+ real (wp), intent (in ) :: yc ! ! y value of sphere center
568
+ real (wp), intent (in ) :: zc ! ! z value of sphere center
569
+ integer , intent (in ), optional :: n_facets ! ! [default is 100]
570
+ integer , intent (in ), optional :: linewidth ! ! line width
571
+ logical , intent (in ), optional :: antialiased ! ! enabled anti-aliasing
572
+ character (len=* ), intent (in ), optional :: color ! ! color of the contour line
573
+ integer , intent (out ) :: istat ! ! status output (0 means no problems)
574
+
575
+ character (len= :), allocatable :: rstr ! ! `r` value stringified
576
+ character (len= :), allocatable :: xcstr ! ! `xc` value stringified
577
+ character (len= :), allocatable :: ycstr ! ! `yc` value stringified
578
+ character (len= :), allocatable :: zcstr ! ! `zc` value stringified
579
+ character (len=* ), parameter :: xname = ' x' ! ! `x` variable name for script
580
+ character (len=* ), parameter :: yname = ' y' ! ! `y` variable name for script
581
+ character (len=* ), parameter :: zname = ' z' ! ! `z` variable name for script
582
+
583
+ character (len= max_int_len) :: linewidth_str ! ! `linewidth` input stringified
584
+ character (len= :), allocatable :: antialiased_str ! ! `antialised` input stringified
585
+ character (len= max_int_len) :: n_facets_str ! ! `n_facets` input stringified
586
+ character (len= :), allocatable :: extras ! ! optional stuff string
587
+
588
+ if (allocated (me% str)) then
589
+
590
+ ! get optional inputs (if not present, set default value):
591
+ call optional_int_to_string(n_facets, n_facets_str, ' 100' )
592
+ extras = ' '
593
+ if (present (linewidth)) then
594
+ call optional_int_to_string(linewidth, linewidth_str, ' 1' )
595
+ extras = extras// ' ,' // ' linewidth=' // linewidth_str
596
+ end if
597
+ if (present (antialiased)) then
598
+ call optional_logical_to_string(antialiased, antialiased_str, ' False' )
599
+ extras = extras// ' ,' // ' antialiased=' // antialiased_str
600
+ end if
601
+ if (present (color)) then
602
+ extras = extras// ' ,' // ' color="' // trim (color)// ' "'
603
+ end if
604
+
605
+ istat = 0
606
+
607
+ ! convert the arrays to strings:
608
+ call real_to_string(r , me% real_fmt, rstr)
609
+ call real_to_string(xc, me% real_fmt, xcstr)
610
+ call real_to_string(yc, me% real_fmt, ycstr)
611
+ call real_to_string(zc, me% real_fmt, zcstr)
612
+
613
+ ! sphere code:
614
+ call me% add_str(' u = np.linspace(0, 2 * np.pi, ' // n_facets_str// ' )' )
615
+ call me% add_str(' v = np.linspace(0, np.pi, ' // n_facets_str// ' )' )
616
+ call me% add_str(xname// ' = ' // xcstr// ' + ' // rstr// ' * np.outer(np.cos(u), np.sin(v))' )
617
+ call me% add_str(yname// ' = ' // ycstr// ' + ' // rstr// ' * np.outer(np.sin(u), np.sin(v))' )
618
+ call me% add_str(zname// ' = ' // zcstr// ' + ' // rstr// ' * np.outer(np.ones(np.size(u)), np.cos(v))' )
619
+ call me% add_str(' ax.plot_surface(' // xname// ' , ' // yname// ' , ' // zname// extras// ' )' )
620
+ call me% add_str(' ' )
621
+
622
+ else
623
+ istat = - 1
624
+ write (error_unit,' (A)' ) ' Error in add_sphere: pyplot class not properly initialized.'
625
+ end if
626
+
627
+ end subroutine add_sphere
628
+ ! *****************************************************************************************
629
+
552
630
! *****************************************************************************************
553
631
! > author: Jacob Williams
554
632
!
@@ -762,6 +840,35 @@ subroutine integer_to_string(i, s)
762
840
end subroutine integer_to_string
763
841
! *****************************************************************************************
764
842
843
+ ! *****************************************************************************************
844
+ ! > author: Jacob Williams
845
+ !
846
+ ! Real scalar to string.
847
+
848
+ subroutine real_to_string (v , fmt , str )
849
+
850
+ real (wp), intent (in ) :: v ! ! real values
851
+ character (len=* ), intent (in ) :: fmt ! ! real format string
852
+ character (len= :), allocatable , intent (out ) :: str ! ! real values stringified
853
+
854
+ integer :: istat ! ! IO status
855
+ character (len= max_real_len) :: tmp ! ! dummy string
856
+
857
+ if (fmt==' *' ) then
858
+ write (tmp, * , iostat= istat) v
859
+ else
860
+ write (tmp, fmt, iostat= istat) v
861
+ end if
862
+ if (istat/= 0 ) then
863
+ write (error_unit,' (A)' ) ' Error in real_to_string'
864
+ str = ' ****'
865
+ else
866
+ str = trim (adjustl (tmp))
867
+ end if
868
+
869
+ end subroutine real_to_string
870
+ ! *****************************************************************************************
871
+
765
872
! *****************************************************************************************
766
873
! > author: Jacob Williams
767
874
!
@@ -937,7 +1044,29 @@ subroutine finish_ops(me)
937
1044
call me% add_str(' ' )
938
1045
end if
939
1046
if (me% axis_equal) then
940
- call me% add_str(' ax.axis("equal")' )
1047
+ if (me% mplot3d) then
1048
+ call me% add_str(' ax.set_aspect("equal")' )
1049
+ call me% add_str(' ' )
1050
+
1051
+ call me% add_str(' def set_axes_equal(ax):' )
1052
+ call me% add_str(' x_limits = ax.get_xlim3d()' )
1053
+ call me% add_str(' y_limits = ax.get_ylim3d()' )
1054
+ call me% add_str(' z_limits = ax.get_zlim3d()' )
1055
+ call me% add_str(' x_range = abs(x_limits[1] - x_limits[0])' )
1056
+ call me% add_str(' x_middle = np.mean(x_limits)' )
1057
+ call me% add_str(' y_range = abs(y_limits[1] - y_limits[0])' )
1058
+ call me% add_str(' y_middle = np.mean(y_limits)' )
1059
+ call me% add_str(' z_range = abs(z_limits[1] - z_limits[0])' )
1060
+ call me% add_str(' z_middle = np.mean(z_limits)' )
1061
+ call me% add_str(' plot_radius = 0.5*max([x_range, y_range, z_range])' )
1062
+ call me% add_str(' ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])' )
1063
+ call me% add_str(' ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])' )
1064
+ call me% add_str(' ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])' )
1065
+ call me% add_str(' set_axes_equal(ax)' )
1066
+
1067
+ else
1068
+ call me% add_str(' ax.axis("equal")' )
1069
+ end if
941
1070
call me% add_str(' ' )
942
1071
end if
943
1072
0 commit comments