Skip to main content

src/sparklinekit@unicode.erl

-module(sparklinekit@unicode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sparklinekit/unicode.gleam").
-export([render/1, render_ints/1]).

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

?MODULEDOC(
    " Unicode-block sparklines for terminal output.\n"
    "\n"
    " ```gleam\n"
    " import sparklinekit/unicode\n"
    "\n"
    " pub fn shape() -> String {\n"
    "   unicode.render([1.0, 5.0, 22.0, 13.0, 5.0, 2.0, 7.0])\n"
    "   // -> \"▁▂█▅▂▁▃\"\n"
    " }\n"
    " ```\n"
    "\n"
    " The eight block characters `▁▂▃▄▅▆▇█` partition the value range\n"
    " into eight equal levels. Empty input returns `\"\"`. A single value,\n"
    " or a series where every value is the same, renders as a flat line\n"
    " at the middle level (`▄`).\n"
).

-file("src/sparklinekit/unicode.gleam", 68).
-spec block_for(float(), float(), float()) -> binary().
block_for(Value, Lo, Hi) ->
    Normalized = sparklinekit@internal@scale:unit(Value, Lo, Hi),
    Raw = begin
        _pipe = Normalized * erlang:float(8 - 1),
        erlang:round(_pipe)
    end,
    Index = sparklinekit@internal@scale:clamp_int(Raw, 0, 8 - 1),
    gleam@string:slice(<<"▁▂▃▄▅▆▇█"/utf8>>, Index, 1).

-file("src/sparklinekit/unicode.gleam", 64).
-spec middle_block() -> binary().
middle_block() ->
    gleam@string:slice(<<"▁▂▃▄▅▆▇█"/utf8>>, 3, 1).

-file("src/sparklinekit/unicode.gleam", 38).
?DOC(
    " Render `values` as a string of Unicode block characters.\n"
    "\n"
    " - `[]` produces `\"\"`.\n"
    " - `[v]` produces a single middle-level block (`▄`).\n"
    " - All-equal inputs produce a string of middle-level blocks.\n"
    " - Otherwise each value is mapped into one of eight levels by\n"
    "   normalising against the observed minimum and maximum.\n"
    "\n"
    " Use [`render_ints`](#render_ints) if your data series is `List(Int)`.\n"
).
-spec render(list(float())) -> binary().
render(Values) ->
    case Values of
        [] ->
            <<""/utf8>>;

        [_] ->
            middle_block();

        _ ->
            {Lo, Hi} = sparklinekit@internal@scale:min_max(Values),
            case Lo =:= Hi of
                true ->
                    _pipe = Values,
                    _pipe@1 = gleam@list:map(
                        _pipe,
                        fun(_) -> middle_block() end
                    ),
                    erlang:list_to_binary(_pipe@1);

                false ->
                    _pipe@2 = Values,
                    _pipe@3 = gleam@list:map(
                        _pipe@2,
                        fun(Value) -> block_for(Value, Lo, Hi) end
                    ),
                    erlang:list_to_binary(_pipe@3)
            end
    end.

-file("src/sparklinekit/unicode.gleam", 60).
?DOC(
    " Convenience wrapper around [`render/1`](#render) for `List(Int)`\n"
    " inputs. Equivalent to `render(list.map(values, int.to_float))`.\n"
).
-spec render_ints(list(integer())) -> binary().
render_ints(Values) ->
    render(gleam@list:map(Values, fun erlang:float/1)).