Option Explicit

Sub 차트_막대별_색상_적용()

    '====================================================
    ' [1] 차트 1, 차트 2에 적용할 색상
    '====================================================
    Dim fixedColors As Variant
    fixedColors = Array( _
        "#845EC2", _
        "#D65DB1", _
        "#FF6F91", _
        "#FFC75F" _
    )

    '====================================================
    ' [2] 차트 3 상위 5개에 사용할 강조 색상 팔레트
    '====================================================
    Dim top5Palette As Variant
    top5Palette = Array( _
        "#845EC2", _
        "#2C73D2", _
        "#0081CF" _
    )

    '====================================================
    ' [3] 차트 1, 차트 2는 막대별로 고정 색상 적용
    '     화면 표시 순서 기준으로 색상이 적용되도록 역순 처리
    '====================================================
    ApplyPointColorsReverse ActiveSheet.ChartObjects("차트 1").Chart, fixedColors
    ApplyPointColorsReverse ActiveSheet.ChartObjects("차트 2").Chart, fixedColors

    '====================================================
    ' [4] 차트 3은 화면 표시 기준 1~5위 강조색, 6위 이후 회색 적용
    '     화면 표시 순서 기준으로 색상이 적용되도록 역순 처리
    '====================================================
    ApplyTop5AndGrayColorsReverse ActiveSheet.ChartObjects("차트 3").Chart, top5Palette, "#BFBFBF"

    MsgBox "차트 막대별 색상 적용이 완료되었습니다.", vbInformation

End Sub


Private Sub ApplyPointColorsReverse(ByVal cht As Chart, ByVal colorList As Variant)

    '====================================================
    ' 차트의 첫 번째 계열 안에 있는 각 막대(Point)에 색상 적용
    '
    ' 가로막대 차트에서 화면상 위쪽 막대부터 색상이 들어가도록
    ' Points 순서를 역순으로 계산
    '====================================================

    Dim srs As Series
    Dim i As Long
    Dim displayIndex As Long
    Dim colorIndex As Long
    Dim pointCount As Long
    Dim colorCount As Long

    Set srs = cht.SeriesCollection(1)

    pointCount = srs.Points.Count
    colorCount = UBound(colorList) - LBound(colorList) + 1

    For i = 1 To pointCount

        ' 화면 표시 순서 기준 인덱스
        ' i = 1인 Point는 화면상 아래쪽일 수 있으므로 역순으로 계산
        displayIndex = pointCount - i + 1

        ' 화면상 위에서부터 1, 2, 3, 4 순서로 색상 적용
        colorIndex = (displayIndex - 1) Mod colorCount

        With srs.Points(i).Format.Fill
            .Visible = msoTrue
            .Solid
            .ForeColor.RGB = HexToRGB(CStr(colorList(colorIndex)))
        End With

        With srs.Points(i).Format.Line
            .Visible = msoTrue
            .ForeColor.RGB = HexToRGB(CStr(colorList(colorIndex)))
        End With

    Next i

End Sub


Private Sub ApplyTop5AndGrayColorsReverse(ByVal cht As Chart, ByVal topPalette As Variant, ByVal grayColor As String)

    '====================================================
    ' 차트 3 전용 색상 적용
    '
    ' [조건]
    ' 화면상 1~5위: 지정한 팔레트를 기준으로 그라데이션 색상 적용
    ' 화면상 6위 이후: 회색 적용
    '
    ' 가로막대 차트의 Points 순서가 화면 표시 순서와 반대일 수 있으므로
    ' displayIndex를 역순으로 계산
    '====================================================

    Dim srs As Series
    Dim i As Long
    Dim displayIndex As Long
    Dim pointCount As Long
    Dim rgbColor As Long
    Dim topCount As Long

    Set srs = cht.SeriesCollection(1)

    pointCount = srs.Points.Count
    topCount = 5

    For i = 1 To pointCount

        ' 화면 표시 순서 기준 순위
        displayIndex = pointCount - i + 1

        If displayIndex <= topCount Then
            ' 화면상 1~5위는 보라 → 파랑 계열 그라데이션
            rgbColor = GetGradientColor(topPalette, displayIndex, topCount)
        Else
            ' 화면상 6위 이후는 회색
            rgbColor = HexToRGB(grayColor)
        End If

        With srs.Points(i).Format.Fill
            .Visible = msoTrue
            .Solid
            .ForeColor.RGB = rgbColor
        End With

        With srs.Points(i).Format.Line
            .Visible = msoTrue
            .ForeColor.RGB = rgbColor
        End With

    Next i

End Sub


Private Function GetGradientColor(ByVal palette As Variant, ByVal position As Long, ByVal totalCount As Long) As Long

    '====================================================
    ' 여러 개의 기준 색상 팔레트 사이를 선형 보간하여
    ' 필요한 개수만큼 자연스러운 중간 색상을 생성
    '====================================================

    Dim paletteCount As Long
    Dim scaledPos As Double
    Dim lowerIndex As Long
    Dim upperIndex As Long
    Dim ratio As Double

    Dim r1 As Long, g1 As Long, b1 As Long
    Dim r2 As Long, g2 As Long, b2 As Long
    Dim r As Long, g As Long, b As Long

    paletteCount = UBound(palette) - LBound(palette) + 1

    If totalCount <= 1 Then
        GetGradientColor = HexToRGB(CStr(palette(0)))
        Exit Function
    End If

    scaledPos = (position - 1) * (paletteCount - 1) / (totalCount - 1)

    lowerIndex = Int(scaledPos)
    upperIndex = lowerIndex + 1

    If upperIndex > paletteCount - 1 Then
        upperIndex = paletteCount - 1
    End If

    ratio = scaledPos - lowerIndex

    HexToRGBParts CStr(palette(lowerIndex)), r1, g1, b1
    HexToRGBParts CStr(palette(upperIndex)), r2, g2, b2

    r = CLng(r1 + (r2 - r1) * ratio)
    g = CLng(g1 + (g2 - g1) * ratio)
    b = CLng(b1 + (b2 - b1) * ratio)

    GetGradientColor = RGB(r, g, b)

End Function


Private Function HexToRGB(ByVal hexColor As String) As Long

    '====================================================
    ' HEX 색상값을 VBA RGB 색상값으로 변환
    ' 예: "#0081CF" → RGB(0, 129, 207)
    '====================================================

    Dim r As Long, g As Long, b As Long

    HexToRGBParts hexColor, r, g, b

    HexToRGB = RGB(r, g, b)

End Function


Private Sub HexToRGBParts(ByVal hexColor As String, ByRef r As Long, ByRef g As Long, ByRef b As Long)

    '====================================================
    ' HEX 색상값을 R, G, B 값으로 분해
    '====================================================

    hexColor = Replace(hexColor, "#", "")

    r = CLng("&H" & Mid(hexColor, 1, 2))
    g = CLng("&H" & Mid(hexColor, 3, 2))
    b = CLng("&H" & Mid(hexColor, 5, 2))

End Sub

