Numeric helpers shared across Drafter widgets for data visualization.
Covers value normalization, scaling, range calculation, zone lookup, string duplication, and color cycling — everything widgets need to convert raw data into rendered characters.
Summary
Functions
Computes bar height in whole cells plus a fractional sub-cell index given a normalized value, cell height, and a sub-cell level list.
Calculates a {min, max} range for a list of numbers, optionally
padded so that zero is always visible (include_zero: true).
Returns {0, 1} for an empty list.
Clamps value to the range [lo, hi].
Returns the color at index in palette, cycling if index
exceeds the palette length. An empty palette gives white.
Finds the first zone in zones whose threshold the value meets.
zones is a list of {threshold, term} pairs sorted ascending.
Returns the value from the last zone if none match. An empty zones list raises
FunctionClauseError.
Formats value as a compact human-readable string.
Maps a normalized value in [0.0, 1.0] to an index into levels,
where index 0 is "empty" and the last index is "full".
Normalizes value into the range [0.0, 1.0] given min and max.
Returns 0.0 when min == max. Values outside the range are clamped.
Repeats string exactly count times. Returns "" when count <= 0.
Scales value from [src_min, src_max] to [dst_min, dst_max].
Returns dst_min when the source range is empty. The source value is clamped
first, so the result never leaves the destination range.
Functions
@spec bar_height(float(), non_neg_integer(), [term()]) :: {non_neg_integer(), non_neg_integer()}
Computes bar height in whole cells plus a fractional sub-cell index given a normalized value, cell height, and a sub-cell level list.
Returns {whole_cells, sub_index}, where sub_index indexes into levels for the
partially filled cell above the whole ones.
Examples
iex> Drafter.Visualization.bar_height(0.5, 4, [1, 2, 3, 4, 5, 6, 7, 8])
{2, 0}
iex> Drafter.Visualization.bar_height(1.0, 4, [1, 2, 3, 4, 5, 6, 7, 8])
{4, 0}
Calculates a {min, max} range for a list of numbers, optionally
padded so that zero is always visible (include_zero: true).
Returns {0, 1} for an empty list.
Options
:include_zero- widen the range so0falls inside it. Default:false.
Examples
iex> Drafter.Visualization.calculate_range([3, 7, 9])
{3, 9}
iex> Drafter.Visualization.calculate_range([3, 7, 9], include_zero: true)
{0, 9}
iex> Drafter.Visualization.calculate_range([])
{0, 1}
Clamps value to the range [lo, hi].
Examples
iex> Drafter.Visualization.clamp(15, 0, 10)
10
iex> Drafter.Visualization.clamp(-1, 0, 10)
0
@spec cycle_color(non_neg_integer(), [{integer(), integer(), integer()}]) :: {integer(), integer(), integer()}
Returns the color at index in palette, cycling if index
exceeds the palette length. An empty palette gives white.
Examples
iex> Drafter.Visualization.cycle_color(4, [{1, 1, 1}, {2, 2, 2}])
{1, 1, 1}
iex> Drafter.Visualization.cycle_color(3, [{1, 1, 1}, {2, 2, 2}])
{2, 2, 2}
iex> Drafter.Visualization.cycle_color(0, [])
{255, 255, 255}
Finds the first zone in zones whose threshold the value meets.
zones is a list of {threshold, term} pairs sorted ascending.
Returns the value from the last zone if none match. An empty zones list raises
FunctionClauseError.
Examples
iex> Drafter.Visualization.find_zone(5, [{10, :low}, {20, :mid}, {30, :high}])
:low
iex> Drafter.Visualization.find_zone(15, [{10, :low}, {20, :mid}, {30, :high}])
:mid
iex> Drafter.Visualization.find_zone(99, [{10, :low}, {20, :mid}])
:mid
@spec format_number(number(), non_neg_integer()) :: String.t()
Formats value as a compact human-readable string.
Numbers >= 1_000_000 use M suffix, >= 1_000 use K suffix,
otherwise formatted with up to precision decimal places with
trailing zeros removed. precision defaults to 1 and is ignored for the K and
M forms, which always keep one decimal place.
Examples
iex> Drafter.Visualization.format_number(1_500)
"1.5K"
iex> Drafter.Visualization.format_number(2_500_000)
"2.5M"
iex> Drafter.Visualization.format_number(3.14159, 2)
"3.14"
iex> Drafter.Visualization.format_number(42)
"42"
@spec level_index(float(), [term()]) :: non_neg_integer()
Maps a normalized value in [0.0, 1.0] to an index into levels,
where index 0 is "empty" and the last index is "full".
Examples
iex> Drafter.Visualization.level_index(0.0, [:a, :b, :c, :d, :e])
0
iex> Drafter.Visualization.level_index(0.5, [:a, :b, :c, :d, :e])
2
iex> Drafter.Visualization.level_index(1.0, [:a, :b, :c, :d, :e])
4
Normalizes value into the range [0.0, 1.0] given min and max.
Returns 0.0 when min == max. Values outside the range are clamped.
Examples
iex> Drafter.Visualization.normalize(5, 0, 10)
0.5
iex> Drafter.Visualization.normalize(15, 0, 10)
1.0
iex> Drafter.Visualization.normalize(3, 7, 7)
0.0
Repeats string exactly count times. Returns "" when count <= 0.
Examples
iex> Drafter.Visualization.safe_duplicate("ab", 3)
"ababab"
iex> Drafter.Visualization.safe_duplicate("ab", -2)
""
Scales value from [src_min, src_max] to [dst_min, dst_max].
Returns dst_min when the source range is empty. The source value is clamped
first, so the result never leaves the destination range.
Examples
iex> Drafter.Visualization.scale(5, 0, 10, 0, 100)
50.0
iex> Drafter.Visualization.scale(5, 7, 7, 3, 100)
3.0