fortplot_spy_backend.f90 Source File


Source Code

module fortplot_spy_backend
    use, intrinsic :: iso_fortran_env, only: wp => real64
    use fortplot_context, only: plot_context
    use fortplot_plot_data, only: plot_data_t
    implicit none

    private
    public :: spy_context_t

    type, extends(plot_context) :: spy_context_t
        real(wp) :: current_color(3) = [0.0_wp, 0.0_wp, 0.0_wp]
        real(wp) :: expected_fill(3) = [0.0_wp, 0.0_wp, 0.0_wp]
        real(wp) :: expected_edge(3) = [0.0_wp, 0.0_wp, 0.0_wp]
        integer :: fill_calls = 0
        integer :: line_calls = 0
        integer :: unexpected_calls = 0
        logical :: fill_color_ok = .true.
        logical :: line_color_ok = .true.
    contains
        procedure :: reset => spy_reset
        procedure :: line => spy_line
        procedure :: color => spy_color
        procedure :: text => spy_text
        procedure :: save => spy_save
        procedure :: set_line_width => spy_set_line_width
        procedure :: set_line_style => spy_set_line_style
        procedure :: draw_marker => spy_draw_marker
        procedure :: set_marker_colors => spy_set_marker_colors
        procedure :: set_marker_colors_with_alpha => spy_set_marker_colors_with_alpha
        procedure :: draw_arrow => spy_draw_arrow
        procedure :: get_ascii_output => spy_get_ascii_output
        procedure :: get_width_scale => spy_get_width_scale
        procedure :: get_height_scale => spy_get_height_scale
        procedure :: fill_quad => spy_fill_quad
        procedure :: fill_heatmap => spy_fill_heatmap
        procedure :: extract_rgb_data => spy_extract_rgb_data
        procedure :: get_png_data_backend => spy_get_png_data_backend
        procedure :: prepare_3d_data => spy_prepare_3d_data
        procedure :: render_ylabel => spy_render_ylabel
        procedure :: draw_axes_and_labels_backend => spy_draw_axes_and_labels_backend
        procedure :: save_coordinates => spy_save_coordinates
        procedure :: set_coordinates => spy_set_coordinates
        procedure :: render_axes => spy_render_axes
    end type spy_context_t

contains

    pure logical function colors_close(a, b) result(ok)
        real(wp), intent(in) :: a(3), b(3)
        real(wp), parameter :: tol = 1.0e-12_wp

        ok = all(abs(a - b) <= tol)
    end function colors_close

    subroutine spy_reset(this)
        class(spy_context_t), intent(inout) :: this

        this%current_color = [0.0_wp, 0.0_wp, 0.0_wp]
        this%expected_fill = [0.0_wp, 0.0_wp, 0.0_wp]
        this%expected_edge = [0.0_wp, 0.0_wp, 0.0_wp]
        this%fill_calls = 0
        this%line_calls = 0
        this%unexpected_calls = 0
        this%fill_color_ok = .true.
        this%line_color_ok = .true.

        this%width = 0
        this%height = 0
        this%x_min = -1.0_wp
        this%x_max = 1.0_wp
        this%y_min = -1.0_wp
        this%y_max = 1.0_wp
        this%has_rendered_arrows = .false.
        this%uses_vector_arrows = .false.
        this%has_triangular_arrows = .false.
    end subroutine spy_reset

    subroutine spy_line(this, x1, y1, x2, y2)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: x1, y1, x2, y2

        this%line_calls = this%line_calls + 1
        this%line_color_ok = this%line_color_ok .and. &
                             colors_close(this%current_color, this%expected_edge)
    end subroutine spy_line

    subroutine spy_color(this, r, g, b)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: r, g, b

        this%current_color = [r, g, b]
    end subroutine spy_color

    subroutine spy_text(this, x, y, text)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: x, y
        character(len=*), intent(in) :: text

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_text

    subroutine spy_save(this, filename)
        class(spy_context_t), intent(inout) :: this
        character(len=*), intent(in) :: filename

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_save

    subroutine spy_set_line_width(this, width)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: width

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_set_line_width

    subroutine spy_set_line_style(this, style)
        class(spy_context_t), intent(inout) :: this
        character(len=*), intent(in) :: style

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_set_line_style

    subroutine spy_draw_marker(this, x, y, style)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: x, y
        character(len=*), intent(in) :: style

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_draw_marker

    subroutine spy_set_marker_colors(this, edge_r, edge_g, edge_b, face_r, face_g, &
                                     face_b)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: edge_r, edge_g, edge_b
        real(wp), intent(in) :: face_r, face_g, face_b

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_set_marker_colors

    subroutine spy_set_marker_colors_with_alpha(this, edge_r, edge_g, edge_b, &
                                                edge_alpha, face_r, face_g, face_b, &
                                                face_alpha)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: edge_r, edge_g, edge_b, edge_alpha
        real(wp), intent(in) :: face_r, face_g, face_b, face_alpha

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_set_marker_colors_with_alpha

    subroutine spy_draw_arrow(this, x, y, dx, dy, size, style)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: x, y, dx, dy, size
        character(len=*), intent(in) :: style

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_draw_arrow

    function spy_get_ascii_output(this) result(output)
        class(spy_context_t), intent(in) :: this
        character(len=:), allocatable :: output

        output = ""
    end function spy_get_ascii_output

    function spy_get_width_scale(this) result(scale)
        class(spy_context_t), intent(in) :: this
        real(wp) :: scale

        scale = 1.0_wp
    end function spy_get_width_scale

    function spy_get_height_scale(this) result(scale)
        class(spy_context_t), intent(in) :: this
        real(wp) :: scale

        scale = 1.0_wp
    end function spy_get_height_scale

    subroutine spy_fill_quad(this, x_quad, y_quad)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: x_quad(4), y_quad(4)

        this%fill_calls = this%fill_calls + 1
        this%fill_color_ok = this%fill_color_ok .and. &
                             colors_close(this%current_color, this%expected_fill)
    end subroutine spy_fill_quad

    subroutine spy_fill_heatmap(this, x_grid, y_grid, z_grid, z_min, z_max)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: x_grid(:), y_grid(:), z_grid(:, :)
        real(wp), intent(in) :: z_min, z_max

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_fill_heatmap

    subroutine spy_extract_rgb_data(this, width, height, rgb_data)
        class(spy_context_t), intent(in) :: this
        integer, intent(in) :: width, height
        real(wp), intent(out) :: rgb_data(width, height, 3)

        rgb_data = 0.0_wp
    end subroutine spy_extract_rgb_data

    subroutine spy_get_png_data_backend(this, width, height, png_data, status)
        class(spy_context_t), intent(in) :: this
        integer, intent(in) :: width, height
        integer(1), allocatable, intent(out) :: png_data(:)
        integer, intent(out) :: status

        allocate (png_data(0))
        status = 1
    end subroutine spy_get_png_data_backend

    subroutine spy_prepare_3d_data(this, plots)
        class(spy_context_t), intent(inout) :: this
        type(plot_data_t), intent(in) :: plots(:)

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_prepare_3d_data

    subroutine spy_render_ylabel(this, ylabel)
        class(spy_context_t), intent(inout) :: this
        character(len=*), intent(in) :: ylabel

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_render_ylabel

    subroutine spy_draw_axes_and_labels_backend(this, xscale, yscale, &
                                                symlog_threshold, x_min, x_max, &
                                                y_min, y_max, title, xlabel, ylabel, &
                                                x_date_format, y_date_format, z_min, &
                                                z_max, has_3d_plots)
        class(spy_context_t), intent(inout) :: this
        character(len=*), intent(in) :: xscale, yscale
        real(wp), intent(in) :: symlog_threshold
        real(wp), intent(in) :: x_min, x_max, y_min, y_max
        character(len=:), allocatable, intent(in), optional :: title, xlabel, ylabel
        character(len=*), intent(in), optional :: x_date_format, y_date_format
        real(wp), intent(in), optional :: z_min, z_max
        logical, intent(in) :: has_3d_plots

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_draw_axes_and_labels_backend

    subroutine spy_save_coordinates(this, x_min, x_max, y_min, y_max)
        class(spy_context_t), intent(in) :: this
        real(wp), intent(out) :: x_min, x_max, y_min, y_max

        x_min = this%x_min
        x_max = this%x_max
        y_min = this%y_min
        y_max = this%y_max
    end subroutine spy_save_coordinates

    subroutine spy_set_coordinates(this, x_min, x_max, y_min, y_max)
        class(spy_context_t), intent(inout) :: this
        real(wp), intent(in) :: x_min, x_max, y_min, y_max

        this%x_min = x_min
        this%x_max = x_max
        this%y_min = y_min
        this%y_max = y_max
        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_set_coordinates

    subroutine spy_render_axes(this, title_text, xlabel_text, ylabel_text)
        class(spy_context_t), intent(inout) :: this
        character(len=*), intent(in), optional :: title_text, xlabel_text, ylabel_text

        this%unexpected_calls = this%unexpected_calls + 1
    end subroutine spy_render_axes

end module fortplot_spy_backend