fortplot_figure_render_steps.f90 Source File


Source Code

module fortplot_figure_render_steps
    !! Rendering step procedures extracted from fortplot_figure_render_engine
    !! for size compliance (refs #1694)

    use, intrinsic :: iso_fortran_env, only: wp => real64
    use fortplot_plot_data, only: plot_data_t, PLOT_TYPE_PIE, &
                                  PLOT_TYPE_CONTOUR, PLOT_TYPE_SURFACE, &
                                  PLOT_TYPE_SCATTER, PLOT_TYPE_PCOLORMESH
    use fortplot_figure_initialization, only: figure_state_t
    use fortplot_figure_rendering_pipeline, only: setup_coordinate_system, &
                                                  render_figure_background, &
                                                  render_figure_axes, &
                                                  render_all_plots, &
                                                  render_streamplot_arrows, &
                                                  render_figure_axes_labels_only, &
                                                  render_title_only, &
                                                  render_polar_axes
    use fortplot_figure_grid, only: render_grid_lines
    use fortplot_annotation_rendering, only: render_figure_annotations
    use fortplot_figure_aspect, only: contains_pie_plot, enforce_pie_axis_equal, &
                                      only_pie_plots, enforce_aspect_ratio
    use fortplot_margins, only: plot_area_t
    use fortplot_figure_colorbar, only: render_colorbar
    use fortplot_png, only: png_context
    use fortplot_pdf, only: pdf_context
    use fortplot_ascii, only: ascii_context, ASCII_CHAR_ASPECT
    use fortplot_legend, only: legend_render
    implicit none

    private
    public :: render_background_and_grid
    public :: render_axes_and_plots
    public :: render_labels_overlay
    public :: render_decorations
    public :: regenerate_pie_legend_for_backend
    public :: apply_aspect_ratio_if_needed
    public :: render_colorbar_with_state
    public :: resolve_plot_colorbar_request

contains

    subroutine render_background_and_grid(state, ascii_bk)
        type(figure_state_t), intent(inout) :: state
        logical, intent(in) :: ascii_bk

        call setup_coordinate_system(state%backend, &
                                     state%x_min_transformed, state%x_max_transformed, &
                                     state%y_min_transformed, state%y_max_transformed)
        call render_figure_background(state%backend)
        if (state%grid_enabled .and. .not. state%polar_projection .and. .not. ascii_bk) then
            call render_grid_lines(state%backend, state%grid_enabled, &
                                   state%grid_which, state%grid_axis, &
                                   state%grid_alpha, state%width, state%height, &
                                   state%margin_left, state%margin_right, &
                                   state%margin_bottom, state%margin_top, &
                                   state%xscale, state%yscale, &
                                   state%symlog_threshold, state%x_min, state%x_max, &
                                   state%y_min, state%y_max, &
                                   state%x_min_transformed, state%x_max_transformed, &
                                   state%y_min_transformed, state%y_max_transformed, &
                                   state%grid_linestyle, state%grid_color)
        end if
        if (state%polar_projection) then
            call render_polar_axes(state%backend, state%x_min_transformed, &
                                   state%x_max_transformed, &
                                   state%y_min_transformed, &
                                   state%y_max_transformed, state)
        end if
    end subroutine render_background_and_grid

    subroutine render_axes_and_plots(state, plots, plot_count, pie_only)
        type(figure_state_t), intent(inout) :: state
        type(plot_data_t), intent(inout) :: plots(:)
        integer, intent(in) :: plot_count
        logical, intent(in) :: pie_only

        if (.not. pie_only .and. .not. state%polar_projection) then
            call render_figure_axes(state%backend, state%xscale, state%yscale, &
                                    state%symlog_threshold, state%x_min, state%x_max, &
                                    state%y_min, state%y_max, state%title, &
                                    state%xlabel, state%ylabel, plots, plot_count, &
                                    has_twinx=state%has_twinx, &
                                    twinx_y_min=state%twinx_y_min, &
                                    twinx_y_max=state%twinx_y_max, &
                                    twinx_ylabel=state%twinx_ylabel, &
                                    twinx_yscale=state%twinx_yscale, &
                                    has_twiny=state%has_twiny, &
                                    twiny_x_min=state%twiny_x_min, &
                                    twiny_x_max=state%twiny_x_max, &
                                    twiny_xlabel=state%twiny_xlabel, &
                                    twiny_xscale=state%twiny_xscale, &
                                    state=state)
        else
            call render_title_only(state%backend, state%title, state%x_min, &
                                   state%x_max, state%y_min, state%y_max, &
                                   state%title_font_size)
        end if
        if (plot_count > 0) then
            call render_all_plots(state%backend, plots, plot_count, &
                                  state%x_min_transformed, state%x_max_transformed, &
                                  state%y_min_transformed, state%y_max_transformed, &
                                  state%xscale, state%yscale, state%symlog_threshold, &
                                  state%width, state%height, &
                                  state%margin_left, state%margin_right, &
                                  state%margin_bottom, state%margin_top, state=state)
        end if
        if (allocated(state%stream_arrows)) then
            if (size(state%stream_arrows) > 0) then
                call render_streamplot_arrows(state%backend, state%stream_arrows)
            end if
        end if
    end subroutine render_axes_and_plots

    subroutine render_labels_overlay(state, plots, plot_count, pie_only, &
                                     x_fmt, y_fmt, twinx_fmt, twiny_fmt)
        type(figure_state_t), intent(inout) :: state
        type(plot_data_t), intent(in) :: plots(:)
        integer, intent(in) :: plot_count
        logical, intent(in) :: pie_only
        character(len=64), intent(in) :: x_fmt, y_fmt, twinx_fmt, twiny_fmt

        if (pie_only .or. state%polar_projection) return
        call render_figure_axes_labels_only( &
            state%backend, state%xscale, state%yscale, &
            state%symlog_threshold, &
            state%x_min, state%x_max, state%y_min, state%y_max, &
            state%title, state%xlabel, state%ylabel, plots, plot_count, &
            has_twinx=state%has_twinx, &
            twinx_y_min=state%twinx_y_min, twinx_y_max=state%twinx_y_max, &
            twinx_ylabel=state%twinx_ylabel, twinx_yscale=state%twinx_yscale, &
            has_twiny=state%has_twiny, &
            twiny_x_min=state%twiny_x_min, twiny_x_max=state%twiny_x_max, &
            twiny_xlabel=state%twiny_xlabel, twiny_xscale=state%twiny_xscale, &
            custom_xticks=state%custom_xtick_positions, &
            custom_xtick_labels=state%custom_xtick_labels, &
            custom_yticks=state%custom_ytick_positions, &
            custom_ytick_labels=state%custom_ytick_labels, &
            x_date_format=x_fmt, y_date_format=y_fmt, &
            twinx_y_date_format=twinx_fmt, twiny_x_date_format=twiny_fmt)
    end subroutine render_labels_overlay

    subroutine render_decorations(state, plots, plot_count, have_cbar, &
                                  cbar_pa, vmin, vmax, cmap, saved_pa, &
                                  pa_supported, annotations, ann_count)
        use fortplot_annotations, only: text_annotation_t
        type(figure_state_t), intent(inout) :: state
        type(plot_data_t), intent(inout) :: plots(:)
        integer, intent(in) :: plot_count
        logical, intent(in) :: have_cbar, pa_supported
        type(plot_area_t), intent(in) :: cbar_pa, saved_pa
        real(wp), intent(in) :: vmin, vmax
        character(len=20), intent(in) :: cmap
        type(text_annotation_t), intent(in), optional :: annotations(:)
        integer, intent(in), optional :: ann_count

        if (have_cbar) then
            call render_colorbar_with_state(state, cbar_pa, vmin, vmax, cmap)
        end if
        if (state%show_legend .and. state%legend_data%num_entries > 0) then
            call regenerate_pie_legend_for_backend(state, plots, plot_count)
            call legend_render(state%legend_data, state%backend)
        end if
        if (present(annotations) .and. present(ann_count)) then
            if (ann_count > 0) then
                call render_figure_annotations(state%backend, annotations, &
                                               ann_count, &
                                               state%x_min, state%x_max, &
                                               state%y_min, state%y_max, &
                                               state%width, state%height, state%dpi, &
                                               state%margin_left, state%margin_right, &
                                               state%margin_bottom, state%margin_top)
            end if
        end if
        if (have_cbar .and. pa_supported) then
            select type (bk => state%backend)
            class is (png_context)
                bk%plot_area = saved_pa
            class is (pdf_context)
                bk%plot_area = saved_pa
            class default
            end select
        end if
    end subroutine render_decorations

    subroutine regenerate_pie_legend_for_backend(state, plots, plot_count)
        use fortplot_figure_plot_management, only: setup_figure_legend
        type(figure_state_t), intent(inout) :: state
        type(plot_data_t), intent(in) :: plots(:)
        integer, intent(in) :: plot_count

        integer :: i
        logical :: has_pie_charts

        has_pie_charts = .false.
        do i = 1, plot_count
            if (plots(i)%plot_type == PLOT_TYPE_PIE) then
                has_pie_charts = .true.
                exit
            end if
        end do

        if (has_pie_charts) then
            call state%legend_data%clear()
            call setup_figure_legend(state%legend_data, state%show_legend, &
                                     plots, plot_count, 'east', state%backend_name)
        end if
    end subroutine regenerate_pie_legend_for_backend

    subroutine apply_aspect_ratio_if_needed(state, has_pie_plots)
        type(figure_state_t), intent(inout) :: state
        logical, intent(in) :: has_pie_plots

        real(wp) :: plot_width_px, plot_height_px

        if (has_pie_plots) return
        if (state%aspect_mode == 'auto') return

        select type (bk => state%backend)
        class is (png_context)
            plot_width_px = real(max(1, bk%plot_area%width), wp)
            plot_height_px = real(max(1, bk%plot_area%height), wp)
        class is (pdf_context)
            plot_width_px = real(max(1, bk%plot_area%width), wp)
            plot_height_px = real(max(1, bk%plot_area%height), wp)
        class is (ascii_context)
            plot_width_px = real(max(1, bk%plot_width - 3), wp)
            plot_height_px = real(max(1, bk%plot_height - 3), wp)*ASCII_CHAR_ASPECT
        class default
            return
        end select

        call enforce_aspect_ratio(state, plot_width_px, plot_height_px)
    end subroutine apply_aspect_ratio_if_needed

    subroutine render_colorbar_with_state(state, plot_area, vmin, vmax, colormap)
        type(figure_state_t), intent(inout) :: state
        type(plot_area_t), intent(in) :: plot_area
        real(wp), intent(in) :: vmin, vmax
        character(len=*), intent(in) :: colormap

        if (state%colorbar_ticks_set .and. state%colorbar_ticklabels_set) then
            if (state%colorbar_label_set) then
                call render_colorbar(state%backend, plot_area, vmin, vmax, &
                                     colormap, state%colorbar_location, &
                                     state%colorbar_label, state%colorbar_ticks, &
                                     state%colorbar_ticklabels, &
                                     state%colorbar_label_fontsize)
            else
                call render_colorbar(state%backend, plot_area, vmin, vmax, &
                                     colormap, state%colorbar_location, &
                                     custom_ticks=state%colorbar_ticks, &
                                     custom_ticklabels=state%colorbar_ticklabels)
            end if
        else if (state%colorbar_ticks_set) then
            if (state%colorbar_label_set) then
                call render_colorbar(state%backend, plot_area, vmin, vmax, &
                                     colormap, state%colorbar_location, &
                                     state%colorbar_label, state%colorbar_ticks, &
                                     label_fontsize=state%colorbar_label_fontsize)
            else
                call render_colorbar(state%backend, plot_area, vmin, vmax, &
                                     colormap, state%colorbar_location, &
                                     custom_ticks=state%colorbar_ticks)
            end if
        else if (state%colorbar_label_set) then
            call render_colorbar(state%backend, plot_area, vmin, vmax, &
                                 colormap, state%colorbar_location, &
                                 state%colorbar_label, &
                                 label_fontsize=state%colorbar_label_fontsize)
        else
            call render_colorbar(state%backend, plot_area, vmin, vmax, &
                                 colormap, state%colorbar_location)
        end if
    end subroutine render_colorbar_with_state

    subroutine resolve_plot_colorbar_request(plots, plot_count, enabled, plot_idx)
        type(plot_data_t), intent(in) :: plots(:)
        integer, intent(in) :: plot_count
        logical, intent(out) :: enabled
        integer, intent(out) :: plot_idx

        integer :: i

        enabled = .false.
        plot_idx = 0

        do i = 1, plot_count
            if (plots(i)%plot_type == PLOT_TYPE_CONTOUR) then
                if (plots(i)%fill_contours .and. plots(i)%show_colorbar) then
                    if (allocated(plots(i)%z_grid) .and. size(plots(i)%z_grid) > 0) then
                        enabled = .true.
                        plot_idx = i
                        return
                    end if
                end if
            end if

            if (plots(i)%plot_type == PLOT_TYPE_SURFACE) then
                if (plots(i)%surface_show_colorbar) then
                    if (allocated(plots(i)%z_grid) .and. size(plots(i)%z_grid) > 0) then
                        enabled = .true.
                        plot_idx = i
                        return
                    end if
                end if
            end if

            if (plots(i)%plot_type == PLOT_TYPE_SCATTER) then
                if (plots(i)%scatter_colorbar) then
                    if (allocated(plots(i)%scatter_colors) .and. &
                        size(plots(i)%scatter_colors) > 0) then
                        enabled = .true.
                        plot_idx = i
                        return
                    end if
                end if
            end if

            if (plots(i)%plot_type == PLOT_TYPE_PCOLORMESH) then
                if (allocated(plots(i)%pcolormesh_data%c_values) .and. &
                    size(plots(i)%pcolormesh_data%c_values) > 0) then
                    enabled = .true.
                    plot_idx = i
                    return
                end if
            end if
        end do
    end subroutine resolve_plot_colorbar_request

end module fortplot_figure_render_steps