module fortplot_context !! Abstract base class for plotting backends !! !! This module defines the common interface that all plotting backends !! (PNG, PDF, ASCII) must implement. Provides polymorphic interface !! for unified plotting operations across different output formats. !! !! Author: fortplot contributors use, intrinsic :: iso_fortran_env, only: wp => real64 use fortplot_plot_data, only: plot_data_t implicit none private public :: plot_context, setup_canvas type, abstract :: plot_context integer :: width, height real(wp) :: x_min, x_max, y_min, y_max ! 3D view angles (radians), matplotlib defaults: azim=-60 deg, elev=30 deg real(wp) :: view_azim = -1.0471975511965976_wp real(wp) :: view_elev = 0.5235987755982988_wp real(wp) :: view_dist = 10.0_wp ! Arrow rendering properties logical :: has_rendered_arrows = .false. logical :: uses_vector_arrows = .false. logical :: has_triangular_arrows = .false. contains procedure(line_interface), deferred :: line procedure(color_interface), deferred :: color procedure(text_interface), deferred :: text procedure(save_interface), deferred :: save procedure(line_width_interface), deferred :: set_line_width procedure(line_style_interface), deferred :: set_line_style procedure(marker_interface), deferred :: draw_marker procedure(marker_colors_interface), deferred :: set_marker_colors procedure(marker_colors_alpha_interface), deferred :: & set_marker_colors_with_alpha procedure(arrow_interface), deferred :: draw_arrow procedure(arrowhead_interface), deferred :: draw_arrowhead procedure(ascii_output_interface), deferred :: get_ascii_output !! Additional polymorphic methods to eliminate SELECT TYPE violations procedure(get_width_scale_interface), deferred :: get_width_scale procedure(get_height_scale_interface), deferred :: get_height_scale procedure(fill_quad_interface), deferred :: fill_quad procedure(fill_heatmap_interface), deferred :: fill_heatmap procedure(extract_rgb_data_interface), deferred :: extract_rgb_data procedure(get_png_data_interface), deferred :: get_png_data_backend procedure(prepare_3d_data_interface), deferred :: prepare_3d_data procedure(render_ylabel_interface), deferred :: render_ylabel procedure(draw_axes_and_labels_interface), deferred :: & draw_axes_and_labels_backend procedure(save_coordinates_interface), deferred :: save_coordinates procedure(set_coordinates_interface), deferred :: set_coordinates procedure(render_axes_interface), deferred :: render_axes !! Blank a horizontal run of cells behind a text block so overlays such !! as legends read against clear background. Raster/vector backends !! composite text on top already, so the base implementation is a no-op. procedure :: clear_text_background => context_clear_text_background !! Draw text as a fixed-position overlay. Raster/vector backends reuse !! their normal text path; the ASCII backend overrides this to place the !! glyphs at the exact cell without the auto-shift its buffered text path !! applies, so stacked legend rows never blend together. procedure :: draw_text_overlay => context_draw_text_overlay !! Select the text-backend charset ('ascii' or 'braille'). Only the !! text backend carries subpixel dots; raster/vector backends ignore it. procedure :: set_text_charset => context_set_text_charset !! Select the text-backend ANSI color mode. Only the text backend emits !! escapes; raster/vector backends ignore it (#2062). procedure :: set_text_color_mode => context_set_text_color_mode end type plot_context abstract interface subroutine line_interface(this, x1, y1, x2, y2) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: x1, y1, x2, y2 end subroutine line_interface subroutine color_interface(this, r, g, b) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: r, g, b end subroutine color_interface subroutine text_interface(this, x, y, text) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: x, y character(len=*), intent(in) :: text end subroutine text_interface subroutine save_interface(this, filename) import :: plot_context class(plot_context), intent(inout) :: this character(len=*), intent(in) :: filename end subroutine save_interface subroutine line_width_interface(this, width) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: width end subroutine line_width_interface subroutine line_style_interface(this, style) import :: plot_context class(plot_context), intent(inout) :: this character(len=*), intent(in) :: style end subroutine line_style_interface subroutine marker_interface(this, x, y, style, size) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: x, y character(len=*), intent(in) :: style real(wp), intent(in), optional :: size end subroutine marker_interface subroutine marker_colors_interface(this, edge_r, edge_g, edge_b, face_r, & face_g, face_b) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: edge_r, edge_g, edge_b real(wp), intent(in) :: face_r, face_g, face_b end subroutine marker_colors_interface subroutine marker_colors_alpha_interface(this, edge_r, edge_g, edge_b, & edge_alpha, face_r, face_g, face_b, & face_alpha) import :: plot_context, wp class(plot_context), 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 end subroutine marker_colors_alpha_interface subroutine arrow_interface(this, x, y, dx, dy, size, style) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: x, y, dx, dy, size character(len=*), intent(in) :: style end subroutine arrow_interface subroutine arrowhead_interface(this, x, y, dx, dy, size, style) !! Draw an arrowhead glyph only (no shaft). !! (x,y) is the tip in data coordinates; (dx,dy) is a unit !! direction in data coordinates pointing into the tip; size is !! the head scale in pixel-like units, independent of the data !! range. Used by streamplot, where draw_arrow's data-coord shaft !! is unwanted. import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: x, y, dx, dy, size character(len=*), intent(in) :: style end subroutine arrowhead_interface function ascii_output_interface(this) result(output) import :: plot_context class(plot_context), intent(in) :: this character(len=:), allocatable :: output end function ascii_output_interface function get_width_scale_interface(this) result(scale) import :: plot_context, wp class(plot_context), intent(in) :: this real(wp) :: scale end function get_width_scale_interface function get_height_scale_interface(this) result(scale) import :: plot_context, wp class(plot_context), intent(in) :: this real(wp) :: scale end function get_height_scale_interface subroutine fill_quad_interface(this, x_quad, y_quad) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: x_quad(4), y_quad(4) end subroutine fill_quad_interface subroutine fill_heatmap_interface(this, x_grid, y_grid, z_grid, z_min, z_max, colormap_name) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), contiguous, intent(in) :: x_grid(:), y_grid(:), z_grid(:, :) real(wp), intent(in) :: z_min, z_max character(len=*), intent(in), optional :: colormap_name end subroutine fill_heatmap_interface subroutine extract_rgb_data_interface(this, width, height, rgb_data) import :: plot_context, wp class(plot_context), intent(in) :: this integer, intent(in) :: width, height real(wp), intent(out) :: rgb_data(width, height, 3) end subroutine extract_rgb_data_interface subroutine get_png_data_interface(this, width, height, png_data, status) import :: plot_context class(plot_context), intent(in) :: this integer, intent(in) :: width, height integer(1), allocatable, intent(out) :: png_data(:) integer, intent(out) :: status end subroutine get_png_data_interface subroutine prepare_3d_data_interface(this, plots) import :: plot_context, plot_data_t class(plot_context), intent(inout) :: this type(plot_data_t), intent(in) :: plots(:) end subroutine prepare_3d_data_interface subroutine render_ylabel_interface(this, ylabel) import :: plot_context class(plot_context), intent(inout) :: this character(len=*), intent(in) :: ylabel end subroutine render_ylabel_interface subroutine draw_axes_and_labels_interface(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) import :: plot_context, wp class(plot_context), 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 end subroutine draw_axes_and_labels_interface subroutine save_coordinates_interface(this, x_min, x_max, y_min, y_max) import :: plot_context, wp class(plot_context), intent(in) :: this real(wp), intent(out) :: x_min, x_max, y_min, y_max end subroutine save_coordinates_interface subroutine set_coordinates_interface(this, x_min, x_max, y_min, y_max) import :: plot_context, wp class(plot_context), intent(inout) :: this real(wp), intent(in) :: x_min, x_max, y_min, y_max end subroutine set_coordinates_interface subroutine render_axes_interface(this, title_text, xlabel_text, ylabel_text) import :: plot_context class(plot_context), intent(inout) :: this character(len=*), intent(in), optional :: title_text, xlabel_text, & ylabel_text end subroutine render_axes_interface end interface contains subroutine setup_canvas(ctx, width, height) class(plot_context), intent(inout) :: ctx integer, intent(in) :: width, height ctx%width = width ctx%height = height ctx%x_min = -1.0_wp ctx%x_max = 1.0_wp ctx%y_min = -1.0_wp ctx%y_max = 1.0_wp end subroutine setup_canvas subroutine context_clear_text_background(this, x, y, width) !! Default no-op: backends that composite text over graphics need no !! explicit background clearing. class(plot_context), intent(inout) :: this real(wp), intent(in) :: x, y integer, intent(in) :: width associate (unused => this%width + nint(x) + nint(y) + width); end associate end subroutine context_clear_text_background subroutine context_draw_text_overlay(this, x, y, text) !! Default overlay: reuse the backend's normal text path. class(plot_context), intent(inout) :: this real(wp), intent(in) :: x, y character(len=*), intent(in) :: text call this%text(x, y, text) end subroutine context_draw_text_overlay subroutine context_set_text_charset(this, charset) !! Default no-op: only the text backend renders subpixel charsets. class(plot_context), intent(inout) :: this character(len=*), intent(in) :: charset associate (unused => this%width + len(charset)); end associate end subroutine context_set_text_charset subroutine context_set_text_color_mode(this, mode) !! Default no-op: only the text backend emits ANSI color escapes. class(plot_context), intent(inout) :: this character(len=*), intent(in) :: mode associate (unused => this%width + len(mode)); end associate end subroutine context_set_text_color_mode end module fortplot_context