module fortplot_subplot_rendering use, intrinsic :: iso_fortran_env, only: wp => real64 use fortplot_plot_data, only: subplot_data_t use fortplot_figure_initialization, only: figure_state_t use fortplot_figure_rendering_pipeline, only: calculate_figure_data_ranges, & setup_coordinate_system, & render_figure_axes, & render_all_plots, & render_figure_axes_labels_only use fortplot_margins, only: calculate_plot_area use fortplot_pdf_coordinate, only: calculate_pdf_plot_area use fortplot_subplot_layout, only: compute_tight_subplot_margins use fortplot_png, only: png_context use fortplot_pdf, only: pdf_context use fortplot_ascii, only: ascii_context implicit none private public :: render_subplots contains subroutine render_subplots(state, subplots_array, subplot_rows, subplot_cols) type(figure_state_t), intent(inout) :: state type(subplot_data_t), intent(in) :: subplots_array(:, :) integer, intent(in) :: subplot_rows, subplot_cols integer :: nr, nc, i, j real(wp), allocatable :: left_f(:, :), right_f(:, :) real(wp), allocatable :: bottom_f(:, :), top_f(:, :) logical :: have_tight real(wp) :: base_left, base_right, base_bottom, base_top real(wp) :: wspace, hspace real(wp) :: total_w, total_h real(wp) :: ax_w, ax_h real(wp) :: gap_w, gap_h real(wp) :: subplot_left, subplot_right real(wp) :: subplot_bottom, subplot_top real(wp) :: lxmin, lxmax, lymin, lymax real(wp) :: lxmin_t, lxmax_t, lymin_t, lymax_t character(len=64) :: x_date_format, y_date_format nr = subplot_rows nc = subplot_cols x_date_format = '' y_date_format = '' if (allocated(state%xaxis_date_format)) x_date_format = state%xaxis_date_format if (allocated(state%yaxis_date_format)) y_date_format = state%yaxis_date_format have_tight = .false. call compute_tight_subplot_margins(state%backend, subplots_array, nr, nc, & state%xscale, state%yscale, & state%symlog_threshold, & left_f, right_f, & bottom_f, top_f, have_tight) if (.not. have_tight) then base_left = 0.125_wp base_right = 0.90_wp base_bottom = 0.11_wp base_top = 0.88_wp wspace = 0.20_wp hspace = 0.20_wp total_w = base_right - base_left total_h = base_top - base_bottom ax_w = total_w/ & (real(nc, wp) + wspace*real(max(0, nc - 1), wp)) gap_w = wspace*ax_w ax_h = total_h/ & (real(nr, wp) + hspace*real(max(0, nr - 1), wp)) gap_h = hspace*ax_h end if do i = 1, nr do j = 1, nc if (have_tight) then call set_subplot_margins(state%backend, left_f(i, j), & right_f(i, j), bottom_f(i, j), & top_f(i, j)) else subplot_left = base_left + real(j - 1, wp)*(ax_w + gap_w) subplot_right = subplot_left + ax_w subplot_top = base_top - real(i - 1, wp)*(ax_h + gap_h) subplot_bottom = subplot_top - ax_h call set_subplot_margins(state%backend, subplot_left, & subplot_right, subplot_bottom, & subplot_top) end if call calculate_figure_data_ranges(subplots_array(i, j)%plots, & subplots_array(i, j)%plot_count, & subplots_array(i, j)%xlim_set, & subplots_array(i, j)%ylim_set, & lxmin, lxmax, lymin, lymax, & lxmin_t, lxmax_t, lymin_t, lymax_t, & state%xscale, state%yscale, & state%symlog_threshold) call setup_coordinate_system(state%backend, lxmin_t, lxmax_t, & lymin_t, lymax_t) call render_figure_axes(state%backend, state%xscale, state%yscale, & state%symlog_threshold, lxmin, lxmax, & lymin, lymax, subplots_array(i, j)%title, & subplots_array(i, j)%xlabel, & subplots_array(i, j)%ylabel, & subplots_array(i, j)%plots, & subplots_array(i, j)%plot_count, & has_twinx=.false., has_twiny=.false., & state=state) if (subplots_array(i, j)%plot_count > 0) then call render_all_plots(state%backend, subplots_array(i, j)%plots, & subplots_array(i, j)%plot_count, & lxmin_t, lxmax_t, & lymin_t, lymax_t, state%xscale, & state%yscale, & state%symlog_threshold, state%width, & state%height, & state%margin_left, state%margin_right, & state%margin_bottom, & state%margin_top) end if call render_figure_axes_labels_only(state%backend, state%xscale, & state%yscale, & state%symlog_threshold, lxmin, & lxmax, lymin, & lymax, subplots_array(i, j)%title, & subplots_array(i, j)%xlabel, & subplots_array(i, j)%ylabel, & subplots_array(i, j)%plots, & subplots_array(i, j)%plot_count, & has_twinx=.false., & has_twiny=.false., & x_date_format=trim(x_date_format), & y_date_format=trim(y_date_format)) end do end do call render_suptitle(state) end subroutine render_subplots subroutine render_suptitle(state) !! Render the figure-level suptitle above all subplots use fortplot_raster_labels, only: render_title_centered_with_size use fortplot_pdf_text, only: estimate_pdf_text_width use fortplot_pdf_core, only: PDF_TITLE_SIZE type(figure_state_t), intent(inout) :: state real(wp) :: suptitle_y_frac, center_x integer :: suptitle_y_px real(wp) :: font_scale if (.not. allocated(state%suptitle)) return if (len_trim(state%suptitle) == 0) return font_scale = state%suptitle_fontsize/12.0_wp select type (bk => state%backend) class is (png_context) suptitle_y_frac = 0.96_wp suptitle_y_px = int(real(bk%height, wp)*suptitle_y_frac) center_x = real(bk%width, wp)/2.0_wp call render_title_centered_with_size(bk%raster, bk%width, bk%height, & int(center_x), suptitle_y_px, & trim(state%suptitle), font_scale) class is (pdf_context) block real(wp) :: title_width, x_center, y_pos real(wp) :: scaled_font_size real(wp) :: black_color(3) scaled_font_size = PDF_TITLE_SIZE*font_scale title_width = estimate_pdf_text_width(trim(state%suptitle), & scaled_font_size) x_center = real(bk%width, wp)/2.0_wp y_pos = real(bk%height, wp)*0.96_wp black_color = [0.0_wp, 0.0_wp, 0.0_wp] call bk%draw_text_styled(x_center, y_pos, trim(state%suptitle), & scaled_font_size, 0.0_wp, 'center', 'bottom', & .false., black_color) end block class is (ascii_context) call bk%set_title(trim(state%suptitle)) class default end select end subroutine render_suptitle subroutine set_subplot_margins(backend, left_f, right_f, bottom_f, top_f) class(*), intent(inout) :: backend real(wp), intent(in) :: left_f, right_f, bottom_f, top_f select type (bk => backend) class is (png_context) bk%margins%left = left_f bk%margins%right = right_f bk%margins%bottom = bottom_f bk%margins%top = top_f call calculate_plot_area(bk%width, bk%height, bk%margins, bk%plot_area) class is (pdf_context) bk%margins%left = left_f bk%margins%right = right_f bk%margins%bottom = bottom_f bk%margins%top = top_f call calculate_pdf_plot_area(bk%width, bk%height, bk%margins, bk%plot_area) class is (ascii_context) class default end select end subroutine set_subplot_margins end module fortplot_subplot_rendering