fortplot_line_styles.f90 Source File


Source Code

module fortplot_line_styles
    !! Line pattern and style rendering algorithms
    !! 
    !! This module contains line style pattern rendering and segment
    !! drawing algorithms following the Single Responsibility Principle.
    !! 
    !! SOLID: Single responsibility for line style implementation
    
    use, intrinsic :: iso_fortran_env, only: wp => real64
    use fortplot_constants, only: SOLID_LINE_PATTERN_LENGTH
    implicit none

    private
    public :: get_line_pattern, get_pattern_length
    public :: should_draw_at_distance, advance_pattern_state

contains

    subroutine get_line_pattern(linestyle, pattern, pattern_size)
        !! Get line pattern array for given style
        !! Following DRY principle - centralized pattern definitions
        character(len=*), intent(in) :: linestyle
        real(wp), intent(out) :: pattern(20)
        integer, intent(out) :: pattern_size
        
        real(wp) :: dash_len, dot_len, gap_len
        
        ! Base pattern dimensions (designed for PATTERN_SCALE_FACTOR = 5.0)
        ! These values are scaled by 5.0 in raster rendering for proper appearance
        dash_len = 3.0_wp    ! 15 pixels when scaled - proper dash length
        dot_len = 0.4_wp     ! 2 pixels when scaled - visible dot
        gap_len = 1.0_wp     ! 5 pixels when scaled - clear separation
        
        select case (trim(linestyle))
        case ('-', 'solid')
            pattern(1) = SOLID_LINE_PATTERN_LENGTH  ! Very long solid segment
            pattern_size = 1
            
        case ('--', 'dashed')
            pattern(1) = dash_len
            pattern(2) = gap_len
            pattern_size = 2
            
        case (':', 'dotted')
            pattern(1) = dot_len
            pattern(2) = gap_len
            pattern_size = 2
            
        case ('-.', 'dashdot')
            pattern(1) = dash_len
            pattern(2) = gap_len
            pattern(3) = dot_len
            pattern(4) = gap_len
            pattern_size = 4
            
        case default
            pattern(1) = SOLID_LINE_PATTERN_LENGTH  ! Default to solid
            pattern_size = 1
        end select
        
    end subroutine get_line_pattern

    real(wp) function get_pattern_length(pattern, pattern_size)
        !! Calculate total length of pattern cycle
        !! Following KISS principle - simple sum calculation
        real(wp), intent(in) :: pattern(20)
        integer, intent(in) :: pattern_size
        
        integer :: i
        
        get_pattern_length = 0.0_wp
        do i = 1, pattern_size
            get_pattern_length = get_pattern_length + pattern(i)
        end do
        
    end function get_pattern_length

    logical function should_draw_at_distance(distance, pattern, pattern_size, pattern_length)
        !! Determine if line should be drawn at given distance in pattern
        !! Following SRP - handles only pattern position logic
        real(wp), intent(in) :: distance, pattern_length
        real(wp), intent(in) :: pattern(20)
        integer, intent(in) :: pattern_size
        
        real(wp) :: pos_in_cycle, cumulative
        integer :: i
        
        ! Handle edge cases
        if (pattern_size <= 0 .or. pattern_length <= 0.0_wp) then
            should_draw_at_distance = .true.
            return
        end if
        
        ! Find position within pattern cycle
        pos_in_cycle = mod(distance, pattern_length)
        
        ! Determine if we're in a draw or gap segment
        cumulative = 0.0_wp
        do i = 1, pattern_size
            cumulative = cumulative + pattern(i)
            if (pos_in_cycle <= cumulative) then
                ! Odd indices (1,3,5...) are draw segments
                ! Even indices (2,4,6...) are gap segments
                should_draw_at_distance = (mod(i, 2) == 1)
                return
            end if
        end do
        
        ! Fallback
        should_draw_at_distance = .true.
        
    end function should_draw_at_distance

    subroutine advance_pattern_state(current_distance, segment_length, pattern, pattern_size, &
                                   pattern_length, new_distance)
        !! Advance pattern state for continuous rendering
        !! Following SRP - handles only state advancement
        real(wp), intent(in) :: current_distance, segment_length, pattern_length
        real(wp), intent(in) :: pattern(20)
        integer, intent(in) :: pattern_size
        real(wp), intent(out) :: new_distance
        
        new_distance = current_distance + segment_length
        
        ! Keep distance within reasonable bounds to avoid overflow
        if (pattern_length > 0.0_wp .and. new_distance > SOLID_LINE_PATTERN_LENGTH * pattern_length) then
            new_distance = mod(new_distance, pattern_length)
        end if
        
    end subroutine advance_pattern_state

end module fortplot_line_styles