From b4c27052d77eced941fcc2084ed660ebcbd3c906 Mon Sep 17 00:00:00 2001 From: Lars Heinrichs Date: Mon, 12 Aug 2024 19:22:15 +0200 Subject: [PATCH 1/6] feat: replace 'smooth' option with plot_style, add :direct, :smooth and :step options --- lib/chart/lineplot.ex | 8 ++++---- lib/chart/svg.ex | 23 +++++++++++++++++++++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/chart/lineplot.ex b/lib/chart/lineplot.ex index 48e6b3e..20dabc3 100644 --- a/lib/chart/lineplot.ex +++ b/lib/chart/lineplot.ex @@ -51,7 +51,7 @@ defmodule Contex.LinePlot do custom_y_formatter: nil, width: 100, height: 100, - smoothed: true, + plot_style: :smooth, stroke_width: "2", colour_palette: :default ] @@ -106,7 +106,7 @@ defmodule Contex.LinePlot do - `:stroke_width` : 2 (default) - stroke width of the line - - `:smoothed` : true (default) or false - draw the lines smoothed + - `:plot_style` : :direct (default), :smooth or :step Note that the smoothing algorithm is a cardinal spline with tension = 0.3. You may get strange effects (e.g. loops / backtracks) in certain circumstances, e.g. @@ -254,7 +254,7 @@ defmodule Contex.LinePlot do y_accessor, colour ) do - smooth = get_option(plot, :smoothed) + style = get_option(plot, :plot_style) stroke_width = get_option(plot, :stroke_width) options = [ @@ -282,7 +282,7 @@ defmodule Contex.LinePlot do |> Enum.chunk_by(fn {_x, y} -> is_nil(y) end) |> Enum.filter(fn [{_x, y} | _] -> not is_nil(y) end) - Enum.map(points_list, fn points -> line(points, smooth, options) end) + Enum.map(points_list, fn points -> line(points, style, options) end) end @doc false diff --git a/lib/chart/svg.ex b/lib/chart/svg.ex index 6547df3..831d754 100644 --- a/lib/chart/svg.ex +++ b/lib/chart/svg.ex @@ -85,7 +85,7 @@ defmodule Contex.SVG do defp path([], _), do: "" - defp path(points, false) do + defp path(points, :direct) do Enum.reduce(points, :first, fn {x, y}, acc -> coord = ~s|#{x} #{y}| @@ -94,9 +94,28 @@ defmodule Contex.SVG do _ -> [acc, [" L ", coord]] end end) + |> IO.inspect() end - defp path(points, true) do + defp path(points, :step) do + Enum.reduce(points, :first, fn {x, y}, acc -> + coord = ~s|#{x} #{y}| + + case acc do + :first -> + ["M ", coord] + + _ -> + previous_coord = acc |> List.last() + previous_y = previous_coord |> String.split(" ") |> List.last() + new_x = x + acc ++ [" L ", ~s|#{new_x} #{previous_y}|, " L ", coord] + end + end) + |> IO.inspect() + end + + defp path(points, :smooth) do # Use Catmull-Rom curve - see http://schepers.cc/getting-to-the-point # First point stays as-is. Subsequent points are draw using SVG cubic-spline # where control points are calculated as follows: From b62c8e0afc5556063db3b5aba41d8b2dee312707 Mon Sep 17 00:00:00 2001 From: Lars Heinrichs Date: Mon, 12 Aug 2024 19:25:37 +0200 Subject: [PATCH 2/6] chore: rename variable smoothed to plot_style --- lib/chart/svg.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/chart/svg.ex b/lib/chart/svg.ex index 831d754..a29cbf3 100644 --- a/lib/chart/svg.ex +++ b/lib/chart/svg.ex @@ -69,10 +69,10 @@ defmodule Contex.SVG do ] end - def line(points, smoothed, opts \\ []) do + def line(points, plot_style, opts \\ []) do attrs = opts_to_attrs(opts) - path = path(points, smoothed) + path = path(points, plot_style) [ " Date: Mon, 12 Aug 2024 19:26:13 +0200 Subject: [PATCH 3/6] fix(test): adjust test to new params for plotting a smooth graph --- test/contex_line_chart_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/contex_line_chart_test.exs b/test/contex_line_chart_test.exs index 6751848..a043d02 100644 --- a/test/contex_line_chart_test.exs +++ b/test/contex_line_chart_test.exs @@ -41,7 +41,7 @@ defmodule ContexLineChartTest do C330.6666666666667,237.5 388.3333333333333,60 404,20 " """ - output = Contex.SVG.line(points, true) |> IO.iodata_to_binary() + output = Contex.SVG.line(points, :smooth) |> IO.iodata_to_binary() IO.inspect(output) end end From 9a759312be3de9ce50036632774aabc3a90501d0 Mon Sep 17 00:00:00 2001 From: Lars Heinrichs Date: Mon, 12 Aug 2024 19:49:34 +0200 Subject: [PATCH 4/6] feat: add backwards compatibility with 'smoothed' option --- lib/chart/lineplot.ex | 10 +++++++--- lib/chart/svg.ex | 4 ++++ test/contex_line_chart_test.exs | 8 ++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/chart/lineplot.ex b/lib/chart/lineplot.ex index 20dabc3..be59be9 100644 --- a/lib/chart/lineplot.ex +++ b/lib/chart/lineplot.ex @@ -51,7 +51,6 @@ defmodule Contex.LinePlot do custom_y_formatter: nil, width: 100, height: 100, - plot_style: :smooth, stroke_width: "2", colour_palette: :default ] @@ -108,6 +107,8 @@ defmodule Contex.LinePlot do - `:plot_style` : :direct (default), :smooth or :step + For backwards compatibility, the option :smoothed was kept and is translated: true -> :smooth and false -> :direct. + Note that the smoothing algorithm is a cardinal spline with tension = 0.3. You may get strange effects (e.g. loops / backtracks) in certain circumstances, e.g. if the x-value spacing is very uneven. This alogorithm forces the smoothed line @@ -254,7 +255,10 @@ defmodule Contex.LinePlot do y_accessor, colour ) do - style = get_option(plot, :plot_style) + # keep backwards compatibility with old `:smoothed` option + style = get_option(plot, :plot_style) |> IO.inspect(label: "style") + smoothed = get_option(plot, :smoothed) |> IO.inspect(label: "smoothed") + plot_style = if smoothed != nil, do: smoothed, else: style stroke_width = get_option(plot, :stroke_width) options = [ @@ -282,7 +286,7 @@ defmodule Contex.LinePlot do |> Enum.chunk_by(fn {_x, y} -> is_nil(y) end) |> Enum.filter(fn [{_x, y} | _] -> not is_nil(y) end) - Enum.map(points_list, fn points -> line(points, style, options) end) + Enum.map(points_list, fn points -> line(points, plot_style, options) end) end @doc false diff --git a/lib/chart/svg.ex b/lib/chart/svg.ex index a29cbf3..8f53fc6 100644 --- a/lib/chart/svg.ex +++ b/lib/chart/svg.ex @@ -85,6 +85,10 @@ defmodule Contex.SVG do defp path([], _), do: "" + defp path(points, nil), do: path(points, :smooth) + defp path(points, true), do: path(points, IO.inspect(:smooth)) + defp path(points, false), do: path(points, IO.inspect(:direct)) + defp path(points, :direct) do Enum.reduce(points, :first, fn {x, y}, acc -> coord = ~s|#{x} #{y}| diff --git a/test/contex_line_chart_test.exs b/test/contex_line_chart_test.exs index a043d02..9812941 100644 --- a/test/contex_line_chart_test.exs +++ b/test/contex_line_chart_test.exs @@ -41,8 +41,12 @@ defmodule ContexLineChartTest do C330.6666666666667,237.5 388.3333333333333,60 404,20 " """ - output = Contex.SVG.line(points, :smooth) |> IO.iodata_to_binary() - IO.inspect(output) + output_new = Contex.SVG.line(points, :smooth) |> IO.iodata_to_binary() + output_old = Contex.SVG.line(points, true) |> IO.iodata_to_binary() + + assert output_new == output_old + + IO.inspect(output_new) end end end From a53f01fd048830befbf621364cf93c4430bbfe4e Mon Sep 17 00:00:00 2001 From: Lars Heinrichs Date: Wed, 14 Aug 2024 16:45:48 +0200 Subject: [PATCH 5/6] chore: remove calls to IO.inspect --- lib/chart/lineplot.ex | 4 ++-- lib/chart/svg.ex | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/chart/lineplot.ex b/lib/chart/lineplot.ex index be59be9..32e16b4 100644 --- a/lib/chart/lineplot.ex +++ b/lib/chart/lineplot.ex @@ -256,8 +256,8 @@ defmodule Contex.LinePlot do colour ) do # keep backwards compatibility with old `:smoothed` option - style = get_option(plot, :plot_style) |> IO.inspect(label: "style") - smoothed = get_option(plot, :smoothed) |> IO.inspect(label: "smoothed") + style = get_option(plot, :plot_style) + smoothed = get_option(plot, :smoothed) plot_style = if smoothed != nil, do: smoothed, else: style stroke_width = get_option(plot, :stroke_width) diff --git a/lib/chart/svg.ex b/lib/chart/svg.ex index 8f53fc6..56f2516 100644 --- a/lib/chart/svg.ex +++ b/lib/chart/svg.ex @@ -86,8 +86,8 @@ defmodule Contex.SVG do defp path([], _), do: "" defp path(points, nil), do: path(points, :smooth) - defp path(points, true), do: path(points, IO.inspect(:smooth)) - defp path(points, false), do: path(points, IO.inspect(:direct)) + defp path(points, true), do: path(points, :smooth) + defp path(points, false), do: path(points, :direct) defp path(points, :direct) do Enum.reduce(points, :first, fn {x, y}, acc -> @@ -98,7 +98,6 @@ defmodule Contex.SVG do _ -> [acc, [" L ", coord]] end end) - |> IO.inspect() end defp path(points, :step) do @@ -116,7 +115,6 @@ defmodule Contex.SVG do acc ++ [" L ", ~s|#{new_x} #{previous_y}|, " L ", coord] end end) - |> IO.inspect() end defp path(points, :smooth) do From 1eb2156f804db213701f1d05af658d45b2930133 Mon Sep 17 00:00:00 2001 From: Lars Heinrichs Date: Wed, 14 Aug 2024 16:52:45 +0200 Subject: [PATCH 6/6] chore: adjust selecton of :smoothed or :style input based on review --- lib/chart/lineplot.ex | 9 ++++++++- lib/chart/svg.ex | 4 ---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/chart/lineplot.ex b/lib/chart/lineplot.ex index 32e16b4..824fae3 100644 --- a/lib/chart/lineplot.ex +++ b/lib/chart/lineplot.ex @@ -258,7 +258,14 @@ defmodule Contex.LinePlot do # keep backwards compatibility with old `:smoothed` option style = get_option(plot, :plot_style) smoothed = get_option(plot, :smoothed) - plot_style = if smoothed != nil, do: smoothed, else: style + + plot_style = + case smoothed do + true -> :smooth + false -> :direct + _ -> style + end + stroke_width = get_option(plot, :stroke_width) options = [ diff --git a/lib/chart/svg.ex b/lib/chart/svg.ex index 56f2516..0db1427 100644 --- a/lib/chart/svg.ex +++ b/lib/chart/svg.ex @@ -85,10 +85,6 @@ defmodule Contex.SVG do defp path([], _), do: "" - defp path(points, nil), do: path(points, :smooth) - defp path(points, true), do: path(points, :smooth) - defp path(points, false), do: path(points, :direct) - defp path(points, :direct) do Enum.reduce(points, :first, fn {x, y}, acc -> coord = ~s|#{x} #{y}|