Skip to main content

src/etui@widgets@chart.erl

-module(etui@widgets@chart).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/etui/widgets/chart.gleam").
-export([chart_new/1, with_fill/2, with_max/2, with_bar_width/2, with_gap/2, with_period/2, with_bg/2, with_style/2, render/4]).
-export_type([chart_fill/0, chart/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

-type chart_fill() :: {chart_solid, list(etui@style:color())} |
    {chart_gradient, list(etui@style:color())} |
    chart_rainbow |
    chart_animated_rainbow |
    {chart_vertical_gradient, list(etui@style:color())}.

-type chart() :: {chart,
        list(integer()),
        integer(),
        chart_fill(),
        integer(),
        integer(),
        binary(),
        etui@style:color(),
        integer()}.

-file("src/etui/widgets/chart.gleam", 48).
-spec chart_new(list(integer())) -> chart().
chart_new(Data) ->
    {chart, Data, 0, chart_rainbow, 2, 1, <<"â–ˆ"/utf8>>, default, 60}.

-file("src/etui/widgets/chart.gleam", 61).
-spec with_fill(chart(), chart_fill()) -> chart().
with_fill(C, Fill) ->
    {chart,
        erlang:element(2, C),
        erlang:element(3, C),
        Fill,
        erlang:element(5, C),
        erlang:element(6, C),
        erlang:element(7, C),
        erlang:element(8, C),
        erlang:element(9, C)}.

-file("src/etui/widgets/chart.gleam", 65).
-spec with_max(chart(), integer()) -> chart().
with_max(C, Max) ->
    {chart,
        erlang:element(2, C),
        gleam@int:max(1, Max),
        erlang:element(4, C),
        erlang:element(5, C),
        erlang:element(6, C),
        erlang:element(7, C),
        erlang:element(8, C),
        erlang:element(9, C)}.

-file("src/etui/widgets/chart.gleam", 69).
-spec with_bar_width(chart(), integer()) -> chart().
with_bar_width(C, W) ->
    {chart,
        erlang:element(2, C),
        erlang:element(3, C),
        erlang:element(4, C),
        gleam@int:max(1, W),
        erlang:element(6, C),
        erlang:element(7, C),
        erlang:element(8, C),
        erlang:element(9, C)}.

-file("src/etui/widgets/chart.gleam", 73).
-spec with_gap(chart(), integer()) -> chart().
with_gap(C, G) ->
    {chart,
        erlang:element(2, C),
        erlang:element(3, C),
        erlang:element(4, C),
        erlang:element(5, C),
        gleam@int:max(0, G),
        erlang:element(7, C),
        erlang:element(8, C),
        erlang:element(9, C)}.

-file("src/etui/widgets/chart.gleam", 77).
-spec with_period(chart(), integer()) -> chart().
with_period(C, Period) ->
    {chart,
        erlang:element(2, C),
        erlang:element(3, C),
        erlang:element(4, C),
        erlang:element(5, C),
        erlang:element(6, C),
        erlang:element(7, C),
        erlang:element(8, C),
        gleam@int:max(1, Period)}.

-file("src/etui/widgets/chart.gleam", 81).
-spec with_bg(chart(), etui@style:color()) -> chart().
with_bg(C, Bg) ->
    {chart,
        erlang:element(2, C),
        erlang:element(3, C),
        erlang:element(4, C),
        erlang:element(5, C),
        erlang:element(6, C),
        erlang:element(7, C),
        Bg,
        erlang:element(9, C)}.

-file("src/etui/widgets/chart.gleam", 85).
-spec with_style(chart(), etui@style:style()) -> chart().
with_style(C, S) ->
    {chart,
        erlang:element(2, C),
        erlang:element(3, C),
        erlang:element(4, C),
        erlang:element(5, C),
        erlang:element(6, C),
        erlang:element(7, C),
        erlang:element(3, S),
        erlang:element(9, C)}.

-file("src/etui/widgets/chart.gleam", 304).
-spec get_color_at(list(etui@style:color()), integer()) -> etui@style:color().
get_color_at(Colors, N) ->
    case {Colors, N} of
        {[], _} ->
            default;

        {[C | _], 0} ->
            C;

        {[_ | Rest], _} ->
            get_color_at(Rest, N - 1)
    end.

-file("src/etui/widgets/chart.gleam", 296).
-spec get_nth_color(list(etui@style:color()), integer()) -> etui@style:color().
get_nth_color(Colors, N) ->
    Len = erlang:length(Colors),
    case Len of
        0 ->
            default;

        _ ->
            get_color_at(Colors, case Len of
                    0 -> 0;
                    Gleam@denominator -> N rem Gleam@denominator
                end)
    end.

-file("src/etui/widgets/chart.gleam", 273).
-spec bar_color(
    chart_fill(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> etui@style:color().
bar_color(Fill, Bar_idx, N_bars, Dy, Height, Frame, Period) ->
    P = gleam@int:max(1, Period),
    N = gleam@int:max(1, N_bars),
    H = gleam@int:max(1, Height),
    case Fill of
        {chart_solid, Colors} ->
            get_nth_color(Colors, Bar_idx);

        {chart_gradient, Stops} ->
            etui@color:gradient(Stops, Bar_idx, N - 1);

        chart_rainbow ->
            etui@color:hue_to_rgb(case N of
                    0 -> 0;
                    Gleam@denominator -> Bar_idx * 360 div Gleam@denominator
                end);

        chart_animated_rainbow ->
            etui@color:hue_to_rgb(((case N of
                    0 -> 0;
                    Gleam@denominator@1 -> Bar_idx * 360 div Gleam@denominator@1
                end) + (case P of
                    0 -> 0;
                    Gleam@denominator@2 -> Frame * 360 div Gleam@denominator@2
                end)) rem 360);

        {chart_vertical_gradient, Stops@1} ->
            etui@color:gradient(Stops@1, (Height - 1) - Dy, H - 1)
    end.

-file("src/etui/widgets/chart.gleam", 226).
-spec render_bar_rows(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    chart(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> etui@buffer:buffer().
render_bar_rows(
    Buf,
    Area,
    C,
    Bar_idx,
    N_bars,
    X,
    Height,
    Filled_rows,
    Frame,
    Dy
) ->
    case Dy >= Height of
        true ->
            Buf;

        false ->
            Rows_from_bottom = (Height - 1) - Dy,
            Is_filled = Rows_from_bottom < Filled_rows,
            Pos = {position,
                erlang:element(2, erlang:element(2, Area)) + X,
                erlang:element(3, erlang:element(2, Area)) + Dy},
            Buf2 = case Is_filled of
                true ->
                    Fg = bar_color(
                        erlang:element(4, C),
                        Bar_idx,
                        N_bars,
                        Dy,
                        Height,
                        Frame,
                        erlang:element(9, C)
                    ),
                    etui@buffer:set_string(
                        Buf,
                        Pos,
                        erlang:element(7, C),
                        Fg,
                        erlang:element(8, C),
                        etui@style:none()
                    );

                false ->
                    Buf
            end,
            render_bar_rows(
                Buf2,
                Area,
                C,
                Bar_idx,
                N_bars,
                X,
                Height,
                Filled_rows,
                Frame,
                Dy + 1
            )
    end.

-file("src/etui/widgets/chart.gleam", 176).
-spec render_bar_cols_inner(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    chart(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> etui@buffer:buffer().
render_bar_cols_inner(
    Buf,
    Area,
    C,
    Bar_idx,
    N_bars,
    Col_start,
    Height,
    Filled_rows,
    Frame,
    Dc
) ->
    case Dc >= erlang:element(5, C) of
        true ->
            Buf;

        false ->
            X = Col_start + Dc,
            case X >= erlang:element(2, erlang:element(3, Area)) of
                true ->
                    Buf;

                false ->
                    Buf2 = render_bar_rows(
                        Buf,
                        Area,
                        C,
                        Bar_idx,
                        N_bars,
                        X,
                        Height,
                        Filled_rows,
                        Frame,
                        0
                    ),
                    render_bar_cols_inner(
                        Buf2,
                        Area,
                        C,
                        Bar_idx,
                        N_bars,
                        Col_start,
                        Height,
                        Filled_rows,
                        Frame,
                        Dc + 1
                    )
            end
    end.

-file("src/etui/widgets/chart.gleam", 151).
-spec render_bar_col(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    chart(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> etui@buffer:buffer().
render_bar_col(
    Buf,
    Area,
    C,
    Bar_idx,
    N_bars,
    Col_start,
    Height,
    Filled_rows,
    Frame
) ->
    render_bar_cols_inner(
        Buf,
        Area,
        C,
        Bar_idx,
        N_bars,
        Col_start,
        Height,
        Filled_rows,
        Frame,
        0
    ).

-file("src/etui/widgets/chart.gleam", 112).
-spec render_bars(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    chart(),
    list(integer()),
    integer(),
    integer(),
    integer(),
    integer()
) -> etui@buffer:buffer().
render_bars(Buf, Area, C, Data, Bar_idx, N_bars, Max, Frame) ->
    case Data of
        [] ->
            Buf;

        [Val | Rest] ->
            Col_start = Bar_idx * (erlang:element(5, C) + erlang:element(6, C)),
            case Col_start >= erlang:element(2, erlang:element(3, Area)) of
                true ->
                    Buf;

                false ->
                    H = erlang:element(3, erlang:element(3, Area)),
                    Filled_rows = case gleam@int:max(1, Max) of
                        0 -> 0;
                        Gleam@denominator -> Val * H div Gleam@denominator
                    end,
                    Buf2 = render_bar_col(
                        Buf,
                        Area,
                        C,
                        Bar_idx,
                        N_bars,
                        Col_start,
                        H,
                        Filled_rows,
                        Frame
                    ),
                    render_bars(
                        Buf2,
                        Area,
                        C,
                        Rest,
                        Bar_idx + 1,
                        N_bars,
                        Max,
                        Frame
                    )
            end
    end.

-file("src/etui/widgets/chart.gleam", 93).
?DOC(" Render chart into `area`. `frame` drives animated fills.\n").
-spec render(etui@buffer:buffer(), etui@geometry:rect(), chart(), integer()) -> etui@buffer:buffer().
render(Buf, Area, C, Frame) ->
    case (erlang:element(2, erlang:element(3, Area)) =< 0) orelse (erlang:element(
        3,
        erlang:element(3, Area)
    )
    =< 0) of
        true ->
            Buf;

        false ->
            Max = case erlang:element(3, C) of
                0 ->
                    gleam@list:fold(
                        erlang:element(2, C),
                        1,
                        fun gleam@int:max/2
                    );

                M ->
                    M
            end,
            N_bars = erlang:length(erlang:element(2, C)),
            render_bars(
                Buf,
                Area,
                C,
                erlang:element(2, C),
                0,
                N_bars,
                Max,
                Frame
            )
    end.