From 2204643ee05a6a3e81944b76a241e897a8870715 Mon Sep 17 00:00:00 2001 From: Joshua Bauer Date: Sun, 5 May 2024 16:30:48 +0200 Subject: [PATCH 1/2] add new type: :stacked_absolute --- lib/chart/barchart.ex | 56 ++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/lib/chart/barchart.ex b/lib/chart/barchart.ex index a9f2209..4a809ff 100644 --- a/lib/chart/barchart.ex +++ b/lib/chart/barchart.ex @@ -67,7 +67,7 @@ defmodule Contex.BarChart do @type t() :: %__MODULE__{} @type orientation() :: :vertical | :horizontal - @type plot_type() :: :stacked | :grouped + @type plot_type() :: :stacked | :stacked_absolute | :grouped @type selected_item() :: %{category: any(), series: any()} @doc ~S""" @@ -75,7 +75,7 @@ defmodule Contex.BarChart do Options may be passed to control the settings for the barchart. Options available are: - - `:type` : `:stacked` (default) or `:grouped` + - `:type` : `:stacked` (default), `:stacked_absolute` or `:grouped` - `:orientation` : `:vertical` (default) or `:horizontal` - `:axis_label_rotation` : `:auto` (default), 45 or 90 @@ -178,7 +178,7 @@ defmodule Contex.BarChart do end @doc """ - Specifies whether the bars are drawn stacked or grouped. + Specifies whether the bars are drawn stacked, stacked_absolute or grouped. """ @deprecated "Set in new/2 options" @spec type(Contex.BarChart.t(), plot_type()) :: Contex.BarChart.t() @@ -493,23 +493,48 @@ defmodule Contex.BarChart do # Transforms the raw value for each series into a list of range tuples the bar has to cover, scaled to the display area defp prepare_bar_values(series_values, scale, :stacked) do - {results, _last_val} = - Enum.reduce(series_values, {[], 0}, fn data_val, {points, last_val} -> - end_val = data_val + last_val - new = {Scale.domain_to_range(scale, last_val), Scale.domain_to_range(scale, end_val)} - {[new | points], end_val} - end) + Enum.reduce(series_values, {[], 0}, fn data_val, {points, last_val} -> + end_val = data_val + last_val + new = {Scale.domain_to_range(scale, last_val), Scale.domain_to_range(scale, end_val)} + {[new | points], end_val} + end) + |> elem(0) + |> Enum.reverse() + end + + defp prepare_bar_values(series_values, scale, :stacked_absolute) do + series_values + |> Enum.with_index() + |> Enum.reverse() + |> Enum.sort_by(&abs(elem(&1, 0)), :asc) + |> Enum.reduce({[], 0, 0}, fn {data_val, index}, + {points, last_pos_val, last_neg_val} -> + if data_val >= 0 do + new_point = + {Scale.domain_to_range(scale, last_pos_val), + Scale.domain_to_range(scale, data_val)} + + {[{new_point, index} | points], data_val, last_neg_val} + else + new_point = + {Scale.domain_to_range(scale, data_val), + Scale.domain_to_range(scale, last_neg_val)} - Enum.reverse(results) + {[{new_point, index} | points], last_pos_val, data_val} + end + end) + |> elem(0) + |> Enum.sort_by(&elem(&1, 1)) + |> Enum.map(&elem(&1, 0)) end defp prepare_bar_values(series_values, scale, :grouped) do - {scale_min, _} = Scale.get_range(scale) + scale_zero = Scale.domain_to_range(scale, 0.0) results = Enum.reduce(series_values, [], fn data_val, points -> range_val = Scale.domain_to_range(scale, data_val) - [{scale_min, range_val} | points] + [{scale_zero, range_val} | points] end) Enum.reverse(results) @@ -561,7 +586,9 @@ defmodule Contex.BarChart do defp get_svg_bar_rects(_x, _y, _label, _plot, _fill, _event_handlers, _opacities), do: "" - defp adjust_cat_band(cat_band, _index, _count, :stacked, _), do: cat_band + defp adjust_cat_band(cat_band, _index, _count, type, _) + when type in [:stacked, :stacked_absolute], + do: cat_band defp adjust_cat_band({cat_band_start, cat_band_end}, index, count, :grouped, :vertical) do interval = (cat_band_end - cat_band_start) / count @@ -710,7 +737,8 @@ defmodule Contex.BarChart do {0, max} end - defp get_overall_value_domain(_plot, dataset, col_names, :grouped) do + defp get_overall_value_domain(_plot, dataset, col_names, type) + when type in [:stacked_absolute, :grouped] do combiner = fn {min1, max1}, {min2, max2} -> {Utils.safe_min(min1, min2), Utils.safe_max(max1, max2)} end From 8aad06e30418f879bb78d25ad6e61e1fc71f88d5 Mon Sep 17 00:00:00 2001 From: Joshua Bauer Date: Wed, 5 Jun 2024 22:21:35 +0200 Subject: [PATCH 2/2] add stacked_absolute to Gallery --- lib/chart/gallery/bar_charts.ex | 5 ++++ .../bar_charts_plain_stacked_absolute.sample | 29 +++++++++++++++++++ test/gallery/bar_charts_test.exs | 1 + 3 files changed, 35 insertions(+) create mode 100644 lib/chart/gallery/bar_charts_plain_stacked_absolute.sample diff --git a/lib/chart/gallery/bar_charts.ex b/lib/chart/gallery/bar_charts.ex index 405ff5a..65d3729 100644 --- a/lib/chart/gallery/bar_charts.ex +++ b/lib/chart/gallery/bar_charts.ex @@ -72,6 +72,11 @@ defmodule Contex.Gallery.BarCharts do """)} + #{graph(title: "A simple stacked bar chart with absolute input values", + file: "bar_charts_plain_stacked_absolute.sample", + info: "")} + + """ def plain(), do: 0 end diff --git a/lib/chart/gallery/bar_charts_plain_stacked_absolute.sample b/lib/chart/gallery/bar_charts_plain_stacked_absolute.sample new file mode 100644 index 0000000..dc04344 --- /dev/null +++ b/lib/chart/gallery/bar_charts_plain_stacked_absolute.sample @@ -0,0 +1,29 @@ +data = [ + ["January", 3500, 94, -3406], + ["February", 3500, 842, -2658], + ["March", 3500, 366, -3134], + ["April", 4000, 660, -3340], + ["May", 4000, 987, -3013], + ["June", 4000, 788, -3212], + ["July", 4000, 1630, -2370], + ["August", 4000, 569, -3431], + ["September", 4000, 855, -3145], + ["November", 4500, 1001, -3499], + ["December", 4000, 1562, -2438] +] + +category_col = "Month" +series_cols = ["Income", "Savings", "Expenditure"] +test_data = Contex.Dataset.new(data, [category_col | series_cols]) + +options = [ + mapping: %{category_col: category_col, value_cols: series_cols}, + type: :stacked_absolute, + data_labels: true, + orientation: :vertical, + colour_palette: ["009900", "003399", "990000"], + series_columns: series_cols +] + +Contex.Plot.new(test_data, Contex.BarChart, 500, 400, options) + |> Contex.Plot.titles("Finances for a year", "") diff --git a/test/gallery/bar_charts_test.exs b/test/gallery/bar_charts_test.exs index ea60c5e..691df6a 100644 --- a/test/gallery/bar_charts_test.exs +++ b/test/gallery/bar_charts_test.exs @@ -7,6 +7,7 @@ defmodule ContexGalleryBarChartTest do "bar_charts_plain.sample", "bar_charts_plain_horizontal.sample", "bar_charts_plain_stacked.sample", + "bar_charts_plain_stacked_absolute.sample", "bar_charts_log_stacked.sample", "bar_charts_log_stacked_auto_domain.sample" ]