@@ -51,6 +51,7 @@ module pyplot_module
5151
5252 procedure , public :: add_plot ! ! add a 2d plot to pyplot instance
5353 procedure , public :: add_3d_plot ! ! add a 3d plot to pyplot instance
54+ procedure , public :: add_sphere ! ! add a 3d sphere to pyplot instance
5455 procedure , public :: add_contour ! ! add a contour plot to pyplot instance
5556 procedure , public :: add_bar ! ! add a barplot to pyplot instance
5657 procedure , public :: add_imshow ! ! add an image plot (using `imshow`)
@@ -549,6 +550,58 @@ subroutine add_3d_plot(me, x, y, z, label, linestyle, markersize, linewidth, ist
549550 end subroutine add_3d_plot
550551! *****************************************************************************************
551552
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 , 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 (out ) :: istat ! ! status output (0 means no problems)
570+
571+ character (len= :), allocatable :: rstr ! ! r value stringified
572+ character (len= :), allocatable :: xcstr ! ! xc value stringified
573+ character (len= :), allocatable :: ycstr ! ! yc value stringified
574+ character (len= :), allocatable :: zcstr ! ! zc value stringified
575+ character (len=* ), parameter :: xname = ' x' ! ! x variable name for script
576+ character (len=* ), parameter :: yname = ' y' ! ! y variable name for script
577+ character (len=* ), parameter :: zname = ' z' ! ! z variable name for script
578+
579+ if (allocated (me% str)) then
580+
581+ istat = 0
582+
583+ ! convert the arrays to strings:
584+ call real_to_string(r , me% real_fmt, rstr)
585+ call real_to_string(xc, me% real_fmt, xcstr)
586+ call real_to_string(yc, me% real_fmt, ycstr)
587+ call real_to_string(zc, me% real_fmt, zcstr)
588+
589+ call me% add_str(' u = np.linspace(0, 2 * np.pi, 100)' )
590+ call me% add_str(' v = np.linspace(0, np.pi, 100)' )
591+ call me% add_str(xname// ' = ' // xcstr// ' + ' // rstr// ' * np.outer(np.cos(u), np.sin(v))' )
592+ call me% add_str(yname// ' = ' // ycstr// ' + ' // rstr// ' * np.outer(np.sin(u), np.sin(v))' )
593+ call me% add_str(zname// ' = ' // zcstr// ' + ' // rstr// ' * np.outer(np.ones(np.size(u)), np.cos(v))' )
594+ call me% add_str(' ax.plot_surface(' // xname// ' , ' // yname// ' , ' // zname// ' , color="Grey")' )
595+ call me% add_str(' ' )
596+
597+ else
598+ istat = - 1
599+ write (error_unit,' (A)' ) ' Error in add_sphere: pyplot class not properly initialized.'
600+ end if
601+
602+ end subroutine add_sphere
603+ ! *****************************************************************************************
604+
552605! *****************************************************************************************
553606! > author: Jacob Williams
554607!
@@ -762,6 +815,35 @@ subroutine integer_to_string(i, s)
762815 end subroutine integer_to_string
763816! *****************************************************************************************
764817
818+ ! *****************************************************************************************
819+ ! > author: Jacob Williams
820+ !
821+ ! Real scalar to string.
822+
823+ subroutine real_to_string (v , fmt , str )
824+
825+ real (wp), intent (in ) :: v ! ! real values
826+ character (len=* ), intent (in ) :: fmt ! ! real format string
827+ character (len= :), allocatable , intent (out ) :: str ! ! real values stringified
828+
829+ integer :: istat ! ! IO status
830+ character (len= max_real_len) :: tmp ! ! dummy string
831+
832+ if (fmt==' *' ) then
833+ write (tmp, * , iostat= istat) v
834+ else
835+ write (tmp, fmt, iostat= istat) v
836+ end if
837+ if (istat/= 0 ) then
838+ write (error_unit,' (A)' ) ' Error in real_to_string'
839+ str = ' ****'
840+ else
841+ str = trim (adjustl (tmp))
842+ end if
843+
844+ end subroutine real_to_string
845+ ! *****************************************************************************************
846+
765847! *****************************************************************************************
766848! > author: Jacob Williams
767849!
@@ -937,7 +1019,29 @@ subroutine finish_ops(me)
9371019 call me% add_str(' ' )
9381020 end if
9391021 if (me% axis_equal) then
940- call me% add_str(' ax.axis("equal")' )
1022+ if (me% mplot3d) then
1023+ call me% add_str(' ax.set_aspect("equal")' )
1024+ call me% add_str(' ' )
1025+
1026+ call me% add_str(' def set_axes_equal(ax):' )
1027+ call me% add_str(' x_limits = ax.get_xlim3d()' )
1028+ call me% add_str(' y_limits = ax.get_ylim3d()' )
1029+ call me% add_str(' z_limits = ax.get_zlim3d()' )
1030+ call me% add_str(' x_range = abs(x_limits[1] - x_limits[0])' )
1031+ call me% add_str(' x_middle = np.mean(x_limits)' )
1032+ call me% add_str(' y_range = abs(y_limits[1] - y_limits[0])' )
1033+ call me% add_str(' y_middle = np.mean(y_limits)' )
1034+ call me% add_str(' z_range = abs(z_limits[1] - z_limits[0])' )
1035+ call me% add_str(' z_middle = np.mean(z_limits)' )
1036+ call me% add_str(' plot_radius = 0.5*max([x_range, y_range, z_range])' )
1037+ call me% add_str(' ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])' )
1038+ call me% add_str(' ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])' )
1039+ call me% add_str(' ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])' )
1040+ call me% add_str(' set_axes_equal(ax)' )
1041+
1042+ else
1043+ call me% add_str(' ax.axis("equal")' )
1044+ end if
9411045 call me% add_str(' ' )
9421046 end if
9431047
0 commit comments