module fortplot_matplotlib_axes !! Axes, scale, and labelling helpers for the matplotlib facade use, intrinsic :: iso_fortran_env, only: wp => real64 use fortplot_global, only: fig => global_figure use fortplot_logging, only: log_warning use fortplot_matplotlib_session, only: ensure_fig_init implicit none private public :: xlabel public :: ylabel public :: title public :: suptitle public :: legend public :: grid public :: xlim public :: ylim public :: view_init public :: set_xscale public :: set_yscale public :: xscale public :: yscale public :: set_line_width public :: set_text_charset public :: set_text_color_mode public :: set_ydata public :: use_axis public :: get_active_axis public :: minorticks_on public :: axis public :: tight_layout public :: axhline public :: axvline public :: hlines public :: vlines public :: set_xticks public :: set_yticks interface axis !! Set aspect ratio: axis('equal'), axis('auto'), or axis(2.0) module procedure axis_str module procedure axis_num end interface axis contains subroutine xlabel(label_text) !! Set the x-axis label text. !! !! Parameters !! label_text : character(len=*), intent(in) !! X-axis label. character(len=*), intent(in) :: label_text integer :: idx, nrows, ncols, row, col call ensure_fig_init() nrows = fig%subplot_rows ncols = fig%subplot_cols idx = fig%current_subplot if (nrows > 0 .and. ncols > 0 .and. idx >= 1 .and. idx <= nrows*ncols) then row = (idx - 1)/ncols + 1 col = mod(idx - 1, ncols) + 1 call fig%subplot_set_xlabel(row, col, label_text) else call fig%set_xlabel(label_text) end if end subroutine xlabel subroutine ylabel(label_text) !! Set the y-axis label text. !! !! Parameters !! label_text : character(len=*), intent(in) !! Y-axis label. character(len=*), intent(in) :: label_text integer :: idx, nrows, ncols, row, col call ensure_fig_init() nrows = fig%subplot_rows ncols = fig%subplot_cols idx = fig%current_subplot if (nrows > 0 .and. ncols > 0 .and. idx >= 1 .and. idx <= nrows*ncols) then row = (idx - 1)/ncols + 1 col = mod(idx - 1, ncols) + 1 call fig%subplot_set_ylabel(row, col, label_text) else call fig%set_ylabel(label_text) end if end subroutine ylabel subroutine title(title_text) !! Set the title for the current axes. !! !! Parameters !! title_text : character(len=*), intent(in) !! Title text. character(len=*), intent(in) :: title_text integer :: idx, nrows, ncols, row, col call ensure_fig_init() nrows = fig%subplot_rows ncols = fig%subplot_cols idx = fig%current_subplot if (nrows > 0 .and. ncols > 0 .and. idx >= 1 .and. idx <= nrows*ncols) then row = (idx - 1)/ncols + 1 col = mod(idx - 1, ncols) + 1 call fig%subplot_set_title(row, col, title_text) else call fig%set_title(title_text) end if end subroutine title subroutine suptitle(title_text, fontsize) !! Set a centered figure-level title above all subplots. !! !! Parameters !! title_text : character(len=*), intent(in) !! Figure title. !! fontsize : real(wp), optional !! Font size for the title. character(len=*), intent(in) :: title_text real(wp), intent(in), optional :: fontsize call ensure_fig_init() call fig%suptitle(title_text, fontsize) end subroutine suptitle subroutine set_text_charset(charset) character(len=*), intent(in) :: charset call ensure_fig_init() call fig%set_text_charset(charset) end subroutine set_text_charset subroutine set_text_color_mode(mode) character(len=*), intent(in) :: mode call ensure_fig_init() call fig%set_text_color_mode(mode) end subroutine set_text_color_mode subroutine legend(loc, box, fontsize, position) !! Display the figure legend. !! !! Parameters !! loc : character(len=*), optional !! Legend location string such as 'upper right' or 'best'. !! box : logical, optional !! Accepted for matplotlib parity. !! fontsize : integer, optional !! Accepted for matplotlib parity. !! position : character(len=*), optional !! Deprecated alias for loc. character(len=*), intent(in), optional :: loc logical, intent(in), optional :: box integer, intent(in), optional :: fontsize character(len=*), intent(in), optional :: position call ensure_fig_init() if (present(position) .and. .not. present(loc)) then call log_warning( & "legend: 'position' is deprecated; use 'loc' for matplotlib parity") call fig%legend(location=position) else if (present(loc)) then call fig%legend(location=loc) else call fig%legend() end if ! box and fontsize are accepted silently for matplotlib parity; ! styling passthrough is tracked by follow-up work and is not ! reported as an error on every call. call ignore_unused_legend_kwargs(box, fontsize) end subroutine legend subroutine ignore_unused_legend_kwargs(box, fontsize) !! Explicit no-op to document silent acceptance of matplotlib kwargs logical, intent(in), optional :: box integer, intent(in), optional :: fontsize if (present(box) .or. present(fontsize)) return end subroutine ignore_unused_legend_kwargs subroutine grid(visible, which, axis, alpha, linestyle, enabled) !! Toggle or style grid lines. !! !! Parameters !! visible : logical, optional !! Show the grid when .true. !! which : character(len=*), optional !! Grid selection: 'major', 'minor', or 'both'. !! axis : character(len=*), optional !! Axis selection: 'x', 'y', or 'both'. !! alpha : real(wp), optional !! Grid line transparency. !! linestyle : character(len=*), optional !! Grid line style. !! enabled : logical, optional !! Deprecated alias for visible. logical, intent(in), optional :: visible character(len=*), intent(in), optional :: which, axis, linestyle real(wp), intent(in), optional :: alpha logical, intent(in), optional :: enabled logical :: effective_visible logical :: has_visible has_visible = present(visible) .or. present(enabled) effective_visible = .true. if (present(visible)) then effective_visible = visible else if (present(enabled)) then call log_warning( & "grid: 'enabled' is deprecated; use 'visible' for matplotlib parity") effective_visible = enabled end if call ensure_fig_init() if (has_visible) then call fig%grid(enabled=effective_visible, which=which, axis=axis, & alpha=alpha, linestyle=linestyle) else call fig%grid(which=which, axis=axis, alpha=alpha, linestyle=linestyle) end if end subroutine grid subroutine xlim(xmin, xmax) !! Set the x-axis display limits. !! !! Parameters !! xmin : real(wp), intent(in) !! Lower x bound. !! xmax : real(wp), intent(in) !! Upper x bound. real(wp), intent(in) :: xmin, xmax call ensure_fig_init() call fig%set_xlim(xmin, xmax) end subroutine xlim subroutine ylim(ymin, ymax) !! Set the y-axis display limits. !! !! Parameters !! ymin : real(wp), intent(in) !! Lower y bound. !! ymax : real(wp), intent(in) !! Upper y bound. real(wp), intent(in) :: ymin, ymax call ensure_fig_init() call fig%set_ylim(ymin, ymax) end subroutine ylim subroutine view_init(elev, azim, dist) !! Set the 3D view angles in degrees. !! !! Parameters !! elev : real(wp), optional !! Elevation angle. !! azim : real(wp), optional !! Azimuth angle. !! dist : real(wp), optional !! Camera distance. real(wp), intent(in), optional :: elev, azim, dist call ensure_fig_init() call fig%set_view(elev=elev, azim=azim, dist=dist) end subroutine view_init subroutine set_xscale(scale, linthresh, threshold, base, linscale) !! Set the x-axis scale. !! !! Parameters !! scale : character(len=*), intent(in) !! 'linear', 'log', 'symlog', or 'logit'. !! linthresh : real(wp), optional !! Symlog linear range threshold. !! threshold : real(wp), optional !! Deprecated alias for linthresh. !! base : real(wp), optional !! Symlog logarithm base. !! linscale : real(wp), optional !! Symlog linear-region scaling factor. character(len=*), intent(in) :: scale real(wp), intent(in), optional :: linthresh, threshold, base, linscale real(wp) :: resolved_threshold logical :: has_threshold call ensure_fig_init() call resolve_scale_threshold(linthresh, threshold, resolved_threshold, & has_threshold) if (has_threshold) then call fig%set_xscale(scale, resolved_threshold, base=base, linscale=linscale) else call fig%set_xscale(scale, base=base, linscale=linscale) end if end subroutine set_xscale subroutine set_yscale(scale, linthresh, threshold, base, linscale) !! Set the y-axis scale. !! !! Parameters !! scale : character(len=*), intent(in) !! 'linear', 'log', 'symlog', or 'logit'. !! linthresh : real(wp), optional !! Symlog linear range threshold. !! threshold : real(wp), optional !! Deprecated alias for linthresh. !! base : real(wp), optional !! Symlog logarithm base. !! linscale : real(wp), optional !! Symlog linear-region scaling factor. character(len=*), intent(in) :: scale real(wp), intent(in), optional :: linthresh, threshold, base, linscale real(wp) :: resolved_threshold logical :: has_threshold call ensure_fig_init() call resolve_scale_threshold(linthresh, threshold, resolved_threshold, & has_threshold) if (has_threshold) then call fig%set_yscale(scale, resolved_threshold, base=base, linscale=linscale) else call fig%set_yscale(scale, base=base, linscale=linscale) end if end subroutine set_yscale subroutine xscale(scale, linthresh, threshold, base, linscale) !! Alias for set_xscale. !! !! Parameters !! scale : character(len=*), intent(in) !! 'linear', 'log', 'symlog', or 'logit'. character(len=*), intent(in) :: scale real(wp), intent(in), optional :: linthresh, threshold, base, linscale call set_xscale(scale, linthresh=linthresh, threshold=threshold, & base=base, linscale=linscale) end subroutine xscale subroutine yscale(scale, linthresh, threshold, base, linscale) !! Alias for set_yscale. !! !! Parameters !! scale : character(len=*), intent(in) !! 'linear', 'log', 'symlog', or 'logit'. character(len=*), intent(in) :: scale real(wp), intent(in), optional :: linthresh, threshold, base, linscale call set_yscale(scale, linthresh=linthresh, threshold=threshold, & base=base, linscale=linscale) end subroutine yscale subroutine resolve_scale_threshold(linthresh, threshold, value, present_out) real(wp), intent(in), optional :: linthresh, threshold real(wp), intent(out) :: value logical, intent(out) :: present_out value = 1.0_wp present_out = .false. if (present(linthresh)) then value = linthresh present_out = .true. else if (present(threshold)) then call log_warning( & "set_xscale/set_yscale: 'threshold' is deprecated; use 'linthresh'") value = threshold present_out = .true. end if end subroutine resolve_scale_threshold subroutine set_line_width(width) !! Set the default line width for subsequent plots. !! !! Parameters !! width : real(wp), intent(in) !! Line width. real(wp), intent(in) :: width call ensure_fig_init() call fig%set_line_width(width) end subroutine set_line_width subroutine set_ydata(ydata) !! Replace the y-data of the first plot line. !! !! Parameters !! ydata : real(wp), contiguous, intent(in) !! New y coordinates. real(wp), contiguous, intent(in) :: ydata(:) call ensure_fig_init() call fig%set_ydata(1, ydata) end subroutine set_ydata subroutine use_axis(axis_name) !! Switch the active axes by name. !! !! Parameters !! axis_name : character(len=*), intent(in) !! Target axes name. character(len=*), intent(in) :: axis_name call ensure_fig_init() call fig%use_axis(axis_name) end subroutine use_axis function get_active_axis() result(axis_name) !! Return the name of the currently active axes. !! !! Returns !! axis_name : character(len=10) !! Active axes name. character(len=10) :: axis_name call ensure_fig_init() axis_name = fig%get_active_axis() end function get_active_axis subroutine minorticks_on() !! Enable minor ticks on both axes. call ensure_fig_init() call fig%minorticks_on() end subroutine minorticks_on subroutine axis_str(aspect) !! Set axis aspect ratio using string mode. !! !! Parameters !! aspect : character(len=*), intent(in) !! 'equal' or 'auto'. character(len=*), intent(in) :: aspect call ensure_fig_init() call fig%set_aspect(aspect) end subroutine axis_str subroutine axis_num(ratio) !! Set axis aspect ratio using a numeric value. !! !! Parameters !! ratio : real(wp), intent(in) !! y-scale = ratio * x-scale. real(wp), intent(in) :: ratio call ensure_fig_init() call fig%set_aspect(ratio) end subroutine axis_num subroutine tight_layout(pad, w_pad, h_pad) !! Automatically adjust subplot parameters to give specified padding. !! !! Parameters !! pad : real(wp), optional !! Padding between figure edge and subplot edge. !! w_pad : real(wp), optional !! Padding between subplots in width. !! h_pad : real(wp), optional !! Padding between subplots in height. real(wp), intent(in), optional :: pad real(wp), intent(in), optional :: w_pad real(wp), intent(in), optional :: h_pad call ensure_fig_init() call fig%tight_layout(pad, w_pad, h_pad) end subroutine tight_layout subroutine axhline(y, xmin, xmax, color, linestyle, linewidth, label) !! Draw a horizontal reference line. !! !! Parameters !! y : real(wp), intent(in) !! Data value for the line. real(wp), intent(in) :: y real(wp), intent(in), optional :: xmin, xmax character(len=*), intent(in), optional :: color, linestyle, label real(wp), intent(in), optional :: linewidth call ensure_fig_init() call fig%axhline(y, xmin=xmin, xmax=xmax, color=color, & linestyle=linestyle, linewidth=linewidth, label=label) end subroutine axhline subroutine axvline(x, ymin, ymax, color, linestyle, linewidth, label) !! Draw a vertical reference line. !! !! Parameters !! x : real(wp), intent(in) !! Data value for the line. real(wp), intent(in) :: x real(wp), intent(in), optional :: ymin, ymax character(len=*), intent(in), optional :: color, linestyle, label real(wp), intent(in), optional :: linewidth call ensure_fig_init() call fig%axvline(x, ymin=ymin, ymax=ymax, color=color, & linestyle=linestyle, linewidth=linewidth, label=label) end subroutine axvline subroutine hlines(y, xmin, xmax, colors, linestyles, linewidth, label) !! Draw one or more horizontal lines. !! !! Parameters !! y : real(wp), contiguous, intent(in) !! Line positions. !! xmin : real(wp), intent(in) !! Line start value. !! xmax : real(wp), intent(in) !! Line end value. real(wp), contiguous, intent(in) :: y(:) real(wp), intent(in) :: xmin, xmax character(len=*), intent(in), optional :: colors, linestyles, label real(wp), intent(in), optional :: linewidth call ensure_fig_init() call fig%hlines(y, xmin=xmin, xmax=xmax, colors=colors, & linestyles=linestyles, linewidth=linewidth, label=label) end subroutine hlines subroutine vlines(x, ymin, ymax, colors, linestyles, linewidth, label) !! Draw one or more vertical lines. !! !! Parameters !! x : real(wp), contiguous, intent(in) !! Line positions. !! ymin : real(wp), intent(in) !! Line start value. !! ymax : real(wp), intent(in) !! Line end value. real(wp), contiguous, intent(in) :: x(:) real(wp), intent(in) :: ymin, ymax character(len=*), intent(in), optional :: colors, linestyles, label real(wp), intent(in), optional :: linewidth call ensure_fig_init() call fig%vlines(x, ymin=ymin, ymax=ymax, colors=colors, & linestyles=linestyles, linewidth=linewidth, label=label) end subroutine vlines subroutine set_xticks(positions, labels) !! Set custom x-axis tick positions and optional labels. !! !! Parameters !! positions : real(wp), contiguous, intent(in) !! Tick locations. !! labels : character(len=*), optional !! Tick labels. real(wp), contiguous, intent(in) :: positions(:) character(len=*), intent(in), optional :: labels(:) call ensure_fig_init() if (present(labels)) then call fig%set_xticks(positions, labels) else call fig%set_xticks(positions) end if end subroutine set_xticks subroutine set_yticks(positions, labels) !! Set custom y-axis tick positions and optional labels. !! !! Parameters !! positions : real(wp), contiguous, intent(in) !! Tick locations. !! labels : character(len=*), optional !! Tick labels. real(wp), contiguous, intent(in) :: positions(:) character(len=*), intent(in), optional :: labels(:) call ensure_fig_init() if (present(labels)) then call fig%set_yticks(positions, labels) else call fig%set_yticks(positions) end if end subroutine set_yticks end module fortplot_matplotlib_axes