fortplot_figure_initialization.f90 Source File


Source Code

module fortplot_figure_initialization
    !! Figure initialization and configuration module
    !! 
    !! Single Responsibility: Initialize figures and manage basic configuration
    !! Extracted from fortplot_figure_core to reduce file size and improve modularity
    
    use, intrinsic :: iso_fortran_env, only: wp => real64
    use fortplot_context
    use fortplot_utils, only: initialize_backend
    use fortplot_legend, only: legend_t
    use fortplot_plot_data, only: plot_data_t
    implicit none
    
    private
    public :: figure_state_t, initialize_figure_state, reset_figure_state
    public :: setup_figure_backend, configure_figure_dimensions
    public :: set_figure_labels, set_figure_scales, set_figure_limits
    
    type :: figure_state_t
        !! Figure state and configuration data
        !! Encapsulates all configuration and state management
        class(plot_context), allocatable :: backend
        integer :: plot_count = 0
        logical :: rendered = .false.
        
        ! Figure dimensions
        integer :: width = 640
        integer :: height = 480
        
        ! Plot area settings
        real(wp) :: margin_left = 0.15_wp
        real(wp) :: margin_right = 0.05_wp
        real(wp) :: margin_bottom = 0.15_wp
        real(wp) :: margin_top = 0.05_wp
        
        ! Scale settings
        character(len=10) :: xscale = 'linear'
        character(len=10) :: yscale = 'linear'
        real(wp) :: symlog_threshold = 1.0_wp
        
        ! Axis limits
        real(wp) :: x_min, x_max, y_min, y_max
        real(wp) :: x_min_transformed, x_max_transformed
        real(wp) :: y_min_transformed, y_max_transformed
        logical :: xlim_set = .false., ylim_set = .false.
        
        ! Labels
        character(len=:), allocatable :: title
        character(len=:), allocatable :: xlabel
        character(len=:), allocatable :: ylabel
        
        ! Color palette: seaborn colorblind palette
        real(wp), dimension(3,6) :: colors = reshape([ &
            0.0_wp,   0.447_wp, 0.698_wp,  & ! #0072B2 (blue)
            0.0_wp,   0.619_wp, 0.451_wp,  & ! #009E73 (green)
            0.835_wp, 0.369_wp, 0.0_wp,    & ! #D55E00 (orange)
            0.8_wp,   0.475_wp, 0.655_wp,  & ! #CC79A7 (purple)
            0.941_wp, 0.894_wp, 0.259_wp,  & ! #F0E442 (yellow)
            0.337_wp, 0.702_wp, 0.914_wp], & ! #56B4E9 (cyan)
            [3,6])
        
        ! Legend support
        type(legend_t) :: legend_data
        logical :: show_legend = .false.
        integer :: max_plots = 500
        
        ! Drawing properties
        real(wp) :: current_line_width = 1.0_wp
        logical :: has_error = .false.
        
        ! Grid settings
        logical :: grid_enabled = .false.
        character(len=10) :: grid_which = 'both'
        character(len=1) :: grid_axis = 'b'
        real(wp) :: grid_alpha = 0.3_wp
        character(len=10) :: grid_linestyle = '-'
    end type figure_state_t
    
contains
    
    subroutine initialize_figure_state(state, width, height, backend)
        !! Initialize figure state with specified parameters
        type(figure_state_t), intent(inout) :: state
        integer, intent(in), optional :: width, height
        character(len=*), intent(in), optional :: backend
        
        if (present(width)) state%width = width
        if (present(height)) state%height = height
        
        ! Initialize backend - default to PNG if not specified
        if (present(backend)) then
            call initialize_backend(state%backend, backend, state%width, state%height)
        else
            ! Default to PNG backend to prevent uninitialized backend
            if (.not. allocated(state%backend)) then
                call initialize_backend(state%backend, 'png', state%width, state%height)
            end if
        end if
        
        ! Reset state
        ! Reset state manually to avoid legend issues
        state%plot_count = 0
        state%rendered = .false.
        state%show_legend = .false.
        state%xlim_set = .false.
        state%ylim_set = .false.
        state%has_error = .false.
        
        ! Proper legend initialization - allocate entries array
        state%legend_data%num_entries = 0
        if (allocated(state%legend_data%entries)) then
            deallocate(state%legend_data%entries)
        end if
        allocate(state%legend_data%entries(0))
    end subroutine initialize_figure_state
    
    subroutine reset_figure_state(state)
        !! Reset figure state to initial values
        type(figure_state_t), intent(inout) :: state
        
        state%plot_count = 0
        state%rendered = .false.
        state%show_legend = .false.
        
        ! Initialize legend data (safe initialization)
        state%legend_data%num_entries = 0
        if (allocated(state%legend_data%entries)) then
            deallocate(state%legend_data%entries)
        end if
        allocate(state%legend_data%entries(0))
        
        ! Reset axis limits and labels
        state%xlim_set = .false.
        state%ylim_set = .false.
        if (allocated(state%title)) deallocate(state%title)
        if (allocated(state%xlabel)) deallocate(state%xlabel)
        if (allocated(state%ylabel)) deallocate(state%ylabel)
        
        state%has_error = .false.
    end subroutine reset_figure_state
    
    subroutine setup_figure_backend(state, backend_name)
        !! Setup or change the figure backend
        type(figure_state_t), intent(inout) :: state
        character(len=*), intent(in) :: backend_name
        
        ! Deallocate current backend and initialize new one
        if (allocated(state%backend)) deallocate(state%backend)
        call initialize_backend(state%backend, backend_name, state%width, state%height)
        
        ! Force re-rendering with new backend
        state%rendered = .false.
    end subroutine setup_figure_backend
    
    subroutine configure_figure_dimensions(state, width, height)
        !! Configure figure dimensions
        type(figure_state_t), intent(inout) :: state
        integer, intent(in), optional :: width, height
        
        if (present(width)) state%width = width
        if (present(height)) state%height = height
        
        ! If backend exists, reinitialize with new dimensions
        if (allocated(state%backend)) then
            ! Get current backend type and reinitialize
            state%rendered = .false.
        end if
    end subroutine configure_figure_dimensions
    
    subroutine set_figure_labels(state, title, xlabel, ylabel)
        !! Set figure labels
        type(figure_state_t), intent(inout) :: state
        character(len=*), intent(in), optional :: title, xlabel, ylabel
        
        if (present(title)) state%title = title
        if (present(xlabel)) state%xlabel = xlabel
        if (present(ylabel)) state%ylabel = ylabel
    end subroutine set_figure_labels
    
    subroutine set_figure_scales(state, xscale, yscale, threshold)
        !! Set axis scale types
        type(figure_state_t), intent(inout) :: state
        character(len=*), intent(in), optional :: xscale, yscale
        real(wp), intent(in), optional :: threshold
        
        if (present(xscale)) state%xscale = xscale
        if (present(yscale)) state%yscale = yscale
        if (present(threshold)) state%symlog_threshold = threshold
    end subroutine set_figure_scales
    
    subroutine set_figure_limits(state, x_min, x_max, y_min, y_max)
        !! Set axis limits
        type(figure_state_t), intent(inout) :: state
        real(wp), intent(in), optional :: x_min, x_max, y_min, y_max
        
        if (present(x_min) .and. present(x_max)) then
            state%x_min = x_min
            state%x_max = x_max
            state%xlim_set = .true.
        end if
        
        if (present(y_min) .and. present(y_max)) then
            state%y_min = y_min
            state%y_max = y_max
            state%ylim_set = .true.
        end if
    end subroutine set_figure_limits

end module fortplot_figure_initialization