module fortplot_bar_rendering !! Bar plot rendering for memory/vector backends !! !! Renders vertical and horizontal bars using backend quadrilateral fills. use, intrinsic :: iso_fortran_env, only: wp => real64 use fortplot_context use fortplot_plot_data, only: plot_data_t use fortplot_scales, only: apply_scale_transform implicit none private public :: render_bar_plot, render_histogram_plot contains subroutine render_bar_plot(backend, plot_data, xscale, yscale, symlog_threshold) !! Render bar plots (vertical or horizontal) for the active backend class(plot_context), intent(inout) :: backend type(plot_data_t), intent(in) :: plot_data character(len=*), intent(in) :: xscale, yscale real(wp), intent(in) :: symlog_threshold call render_rectangular_bars(backend, plot_data, xscale, yscale, & symlog_threshold, draw_edges=.true.) end subroutine render_bar_plot subroutine render_histogram_plot(backend, plot_data, xscale, yscale, symlog_threshold) !! Render histogram bins as solid quads without outline seams between bins class(plot_context), intent(inout) :: backend type(plot_data_t), intent(in) :: plot_data character(len=*), intent(in) :: xscale, yscale real(wp), intent(in) :: symlog_threshold call render_rectangular_bars(backend, plot_data, xscale, yscale, & symlog_threshold, draw_edges=.false.) end subroutine render_histogram_plot subroutine render_rectangular_bars(backend, plot_data, xscale, yscale, & symlog_threshold, draw_edges) class(plot_context), intent(inout) :: backend type(plot_data_t), intent(in) :: plot_data character(len=*), intent(in) :: xscale, yscale real(wp), intent(in) :: symlog_threshold logical, intent(in) :: draw_edges integer :: i, j, n real(wp), parameter :: DEFAULT_BAR_WIDTH = 0.8_wp real(wp) :: half_width real(wp) :: x_data(4), y_data(4) real(wp) :: x_screen(4), y_screen(4) real(wp) :: effective_width real(wp) :: base_val, top_val real(wp) :: fill_color(3), edge_color(3) real(wp) :: alpha if (.not. allocated(plot_data%bar_x)) return if (.not. allocated(plot_data%bar_heights)) return n = min(size(plot_data%bar_x), size(plot_data%bar_heights)) if (n <= 0) return effective_width = abs(plot_data%bar_width) if (effective_width <= 0.0_wp) effective_width = DEFAULT_BAR_WIDTH half_width = 0.5_wp * effective_width if (plot_data%bar_color_per_bar_set) then fill_color = plot_data%bar_color_per_bar(:, 1) else fill_color = plot_data%color end if if (draw_edges .and. plot_data%bar_edgecolor_set) then edge_color = plot_data%bar_edgecolor else edge_color = fill_color end if alpha = max(0.0_wp, min(1.0_wp, plot_data%fill_alpha)) if (alpha <= 1.0e-6_wp) return do i = 1, n fill_color = plot_data%color if (plot_data%bar_color_per_bar_set) then fill_color = plot_data%bar_color_per_bar(:, i) end if if (draw_edges .and. plot_data%bar_edgecolor_per_bar_set) then edge_color = plot_data%bar_edgecolor_per_bar(:, i) else if (draw_edges .and. plot_data%bar_edgecolor_set) then edge_color = plot_data%bar_edgecolor else edge_color = fill_color end if fill_color = blend_color(fill_color, alpha) edge_color = blend_color(edge_color, alpha) ! Get base offset (bottom for vertical, left for horizontal) if (allocated(plot_data%bar_bottom) .and. i <= size(plot_data%bar_bottom)) then base_val = plot_data%bar_bottom(i) else base_val = 0.0_wp end if top_val = base_val + plot_data%bar_heights(i) if (plot_data%bar_horizontal) then ! Horizontal bars: base_val is left edge, top_val is right edge x_data = [base_val, top_val, top_val, base_val] y_data = [plot_data%bar_x(i) - half_width, & plot_data%bar_x(i) - half_width, & plot_data%bar_x(i) + half_width, & plot_data%bar_x(i) + half_width] else ! Vertical bars: base_val is bottom, top_val is top x_data = [plot_data%bar_x(i) - half_width, & plot_data%bar_x(i) + half_width, & plot_data%bar_x(i) + half_width, & plot_data%bar_x(i) - half_width] y_data = [base_val, base_val, top_val, top_val] end if do j = 1, 4 x_screen(j) = apply_scale_transform(x_data(j), xscale, symlog_threshold) y_screen(j) = apply_scale_transform(y_data(j), yscale, symlog_threshold) end do call backend%color(fill_color(1), fill_color(2), fill_color(3)) call backend%fill_quad(x_screen, y_screen) if (draw_edges) then call backend%color(edge_color(1), edge_color(2), edge_color(3)) call backend%line(x_screen(1), y_screen(1), x_screen(2), y_screen(2)) call backend%line(x_screen(2), y_screen(2), x_screen(3), y_screen(3)) call backend%line(x_screen(3), y_screen(3), x_screen(4), y_screen(4)) call backend%line(x_screen(4), y_screen(4), x_screen(1), y_screen(1)) end if end do end subroutine render_rectangular_bars pure function blend_color(color, alpha) result(blended) real(wp), intent(in) :: color(3) real(wp), intent(in) :: alpha real(wp) :: blended(3) real(wp) :: alpha_clamped alpha_clamped = max(0.0_wp, min(1.0_wp, alpha)) blended = alpha_clamped*color + (1.0_wp - alpha_clamped) end function blend_color end module fortplot_bar_rendering