fortplot_pie_rendering.f90 Source File


Source Code

module fortplot_pie_rendering
    !! Pie chart rendering helpers for polygon backends

    use 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_pie_plot

contains

    subroutine render_pie_plot(backend, plot_data, xscale, yscale, symlog_threshold)
        !! Render pie slices using triangle fans per wedge
        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

        integer :: slice_count, seg_count, i, j
        real(wp) :: angle_start, angle_end, angle_span, mid_angle
        real(wp) :: step_angle, angle_a, angle_b
        real(wp) :: center_x, center_y, radius, offset_value
        real(wp) :: center_x_t, center_y_t
        real(wp) :: x_quad(4), y_quad(4)
        real(wp) :: x_a, y_a, x_b, y_b
        real(wp) :: x_a_t, y_a_t, x_b_t, y_b_t
        real(wp), parameter :: PI = acos(-1.0_wp)
        real(wp), parameter :: MIN_SEGMENT_ANGLE = 5.0_wp * PI / 180.0_wp

        slice_count = plot_data%pie_slice_count
        if (slice_count <= 0) return
        if (.not. allocated(plot_data%pie_start)) return
        if (.not. allocated(plot_data%pie_end)) return
        if (.not. allocated(plot_data%pie_colors)) return

        radius = plot_data%pie_radius

        do i = 1, slice_count
            angle_start = plot_data%pie_start(i)
            angle_end = plot_data%pie_end(i)
            angle_span = angle_end - angle_start
            if (abs(angle_span) < 1.0e-9_wp) cycle

            mid_angle = angle_start + 0.5_wp * angle_span
            offset_value = 0.0_wp
            if (allocated(plot_data%pie_offsets)) then
                if (size(plot_data%pie_offsets) >= i) then
                    offset_value = plot_data%pie_offsets(i)
                end if
            end if

            center_x = plot_data%pie_center(1) + offset_value * cos(mid_angle)
            center_y = plot_data%pie_center(2) + offset_value * sin(mid_angle)
            center_x_t = apply_scale_transform(center_x, xscale, symlog_threshold)
            center_y_t = apply_scale_transform(center_y, yscale, symlog_threshold)

            seg_count = max(8, int(abs(angle_span) / MIN_SEGMENT_ANGLE) + 1)

            call backend%color(plot_data%pie_colors(1, i), plot_data%pie_colors(2, i), &
                               plot_data%pie_colors(3, i))

            step_angle = angle_span / real(seg_count, wp)
            do j = 0, seg_count - 1
                angle_a = angle_start + real(j, wp) * step_angle
                angle_b = angle_a + step_angle

                x_a = center_x + radius * cos(angle_a)
                y_a = center_y + radius * sin(angle_a)
                x_b = center_x + radius * cos(angle_b)
                y_b = center_y + radius * sin(angle_b)

                x_a_t = apply_scale_transform(x_a, xscale, symlog_threshold)
                y_a_t = apply_scale_transform(y_a, yscale, symlog_threshold)
                x_b_t = apply_scale_transform(x_b, xscale, symlog_threshold)
                y_b_t = apply_scale_transform(y_b, yscale, symlog_threshold)

                x_quad(1) = center_x_t
                y_quad(1) = center_y_t
                x_quad(2) = x_a_t
                y_quad(2) = y_a_t
                x_quad(3) = x_b_t
                y_quad(3) = y_b_t
                x_quad(4) = center_x_t
                y_quad(4) = center_y_t

                call backend%fill_quad(x_quad, y_quad)
            end do

            ! matplotlib default pie wedges use edgecolor 'none': fill only,
            ! no outline stroke around the slices.
        end do
    end subroutine render_pie_plot

end module fortplot_pie_rendering