Current section

Files

Jump to
plotex lib plotex axis number_units.ex
Raw

lib/plotex/axis/number_units.ex

defmodule Plotex.Axis.Units.Numeric do
require Logger
alias Plotex.ViewRange
@default_number_basis [1, 2, 5, 10, 20, 50, 100]
defstruct basis: @default_number_basis, ticks: 10
@doc """
Get units for a given date range, using the number of ticks.
"""
def units_for(x_a, x_b, config) do
xmax = max(x_a, x_b)
xdiff = abs(x_a - x_b)
xmin = 1.0e-9
xmin =
if xmax / 1.0e3 < xmin do
xmax / 1.0e3
else
xmin
end
xdiff! =
if xdiff < xmin do
xmin
else
xdiff
end
xdiff! |> optimize_units(config)
end
@spec range_from(Enumerable.t()) :: {Number.t(), Number.t()}
def range_from(data) do
a = Enum.min(data)
b = Enum.max(data)
{a, b}
end
def optimize_units(xdiff, config) do
count = config.ticks
# Logger.warn("xdiff: #{inspect xdiff}")
r = rank(xdiff, count)
# Logger.warn("rank: #{inspect r}")
b = find_basis(config.basis, xdiff, r, count)
# Logger.warn("basis: #{inspect b}")
%{val: xdiff, rank: r, basis: :math.pow(10, r) * b}
end
def find_basis(number_basis, x, rank, count) do
number_basis
|> Enum.map(&{&1, x / (&1 * :math.pow(count, 1 * rank))})
|> Enum.min_by(fn {_base, val} -> abs(count - val) end)
|> elem(0)
end
@doc """
Calculate the base-10 rank of a number.
"""
def rank(0, _b), do: raise(%ArgumentError{message: "scale must needs to be non-zero"})
# def rank(0.1, _b), do: raise %ArgumentError{message: "scale must needs to be non-zero"}
def rank(x, b), do: trunc(:math.log10((x + 1.0e-8) / b) - 1)
end
defimpl Plotex.Axis.Units, for: Plotex.Axis.Units.Numeric do
alias Plotex.ViewRange
alias Plotex.Axis.Units
def scale(%Plotex.Axis.Units.Numeric{} = config, %ViewRange{start: x_a, stop: x_b}) do
%{basis: basis} = _units = Units.Numeric.units_for(x_a, x_b, config)
# Logger.warn("x_basis: #{inspect units}")
# stride = round(basis_count / Keyword.get(config, :ticks, 10))
# Logger.warn("x_stride: #{inspect(stride)}")
# Logger.warn("x_a: #{x_a}")
# Logger.warn("x_fmod: #{inspect :math.fmod(x_a, basis)}")
x_start =
unless x_a < 0.0 do
trunc(x_a / basis) * basis
else
trunc(x_a / basis) * basis - basis
end
# x_start = x_a - :math.fmod(x_a, basis)
x_stop = x_b + basis
# Logger.warn("x_start: #{inspect x_start}")
rng =
0..1_000_000_000
|> Stream.map(fn i -> x_start + i * basis end)
|> Stream.take_while(fn x -> x < x_stop end)
%{data: rng, basis: basis}
end
end