From 8e733efcb4b598f4bd1c09f4a0401400a522f519 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Wed, 18 Dec 2024 16:38:37 +0530 Subject: [PATCH 1/3] bar legend --- lib/matplotex/element/legend.ex | 60 +++++++++++++++++++++++++++++ lib/matplotex/element/rad_legend.ex | 19 ++++----- lib/matplotex/figure.ex | 3 +- lib/matplotex/figure/areal.ex | 26 +++++++------ lib/matplotex/figure/cast.ex | 48 ++++++++++++++++++++++- lib/matplotex/figure/lead.ex | 51 +++++++++++++++++------- lib/matplotex/figure/radial.ex | 49 ++++++++++++----------- lib/matplotex/figure/radial/pie.ex | 21 ++++++---- lib/matplotex/utils/algebra.ex | 4 +- test/matplotex/figure/cast_test.exs | 12 ++++++ test/matplotex/figure/lead_test.exs | 5 ++- 11 files changed, 226 insertions(+), 72 deletions(-) create mode 100644 lib/matplotex/element/legend.ex diff --git a/lib/matplotex/element/legend.ex b/lib/matplotex/element/legend.ex new file mode 100644 index 0000000..9b0e278 --- /dev/null +++ b/lib/matplotex/element/legend.ex @@ -0,0 +1,60 @@ +defmodule Matplotex.Element.Legend do + alias Matplotex.Element.Label + alias Matplotex.Element + alias Matplotex.Element.Rect + + use Element + + @stroke_width 1 + @stroke "rgba(0,0,0,0)" + @legend_size 20 / 96 + @label_type "legend.label" + defstruct [ + :type, + :x, + :y, + :color, + :label, + width: @legend_size, + height: @legend_size, + label_margin: @legend_size, + stroke: @stroke, + stroke_width: @stroke_width + ] + + @impl Element + def assemble(legend) do + """ + #{Rect.assemble(legend)} + #{Element.assemble(legend.label)} + """ + end + + @impl Element + def flipy(%__MODULE__{y: y} = legend, height) do + %__MODULE__{legend | y: height - y, label: Label.flipy(legend.label, height)} + end + + def with_label( + %__MODULE__{ + label: text, + x: x, + y: y, + width: width, + height: height + } = legend, + legend_font, padding + ) do + %{ + legend + | label: + %Label{ + x: x + width + padding, + y: y + height / 2, + text: text, + type: @label_type + } + |> Label.cast_label(legend_font) + } + end +end diff --git a/lib/matplotex/element/rad_legend.ex b/lib/matplotex/element/rad_legend.ex index 147b694..cf3c87b 100644 --- a/lib/matplotex/element/rad_legend.ex +++ b/lib/matplotex/element/rad_legend.ex @@ -42,18 +42,19 @@ defmodule Matplotex.Element.RadLegend do y: y, width: width, height: height - } = legend, legend_font + } = legend, + legend_font ) do - %{ legend - | label: %Label{ - x: x + width , - y: y + height / 2, - text: text, - type: @label_type - } - |> Label.cast_label(legend_font) + | label: + %Label{ + x: x + width, + y: y + height / 2, + text: text, + type: @label_type + } + |> Label.cast_label(legend_font) } end end diff --git a/lib/matplotex/figure.ex b/lib/matplotex/figure.ex index ad3d563..b8d7129 100644 --- a/lib/matplotex/figure.ex +++ b/lib/matplotex/figure.ex @@ -31,6 +31,7 @@ defmodule Matplotex.Figure do def new(opts) do struct(__MODULE__, opts) end + # TODO: put error message in error # def put_error(figure, opts) do @@ -114,6 +115,4 @@ defmodule Matplotex.Figure do defp update_rc_params(_, _) do raise Matplotex.InputError, keys: [:rc_params], message: "Invalid Input" end - - end diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 92e4407..1d613a5 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -154,6 +154,7 @@ defmodule Matplotex.Figure.Areal do |> Cast.cast_spines_by_region() |> Cast.cast_label_by_region() |> Cast.cast_title_by_region() + |> Cast.cast_legends() end defp update_tick(axes, tick) do @@ -255,7 +256,6 @@ defmodule Matplotex.Figure.Areal do def set_region_title(figure), do: figure - def set_region_legend( %Figure{ axes: @@ -272,9 +272,8 @@ defmodule Matplotex.Figure.Areal do ) do region_legend_width = f_width * legend_width region_x_width_after_legend = region_x_width - region_legend_width - {x_region_legend, y_region_legend} = - Algebra.transform_given_point(-region_legend_width, -region_title_height, rx, ty, 0) + Algebra.transform_given_point(-region_legend_width, -region_title_height, rx, ty) %Figure{ figure @@ -294,17 +293,19 @@ defmodule Matplotex.Figure.Areal do } } end + def set_region_legend(figure), do: figure + def set_region_content( - %Figure{ - axes: - %{ - region_x: %Region{x: x_region_x, width: region_x_width}, - region_y: %Region{y: y_region_y, height: region_y_height}, - region_content: region_content - } = axes - } = figure - ) do + %Figure{ + axes: + %{ + region_x: %Region{x: x_region_x, width: region_x_width}, + region_y: %Region{y: y_region_y, height: region_y_height}, + region_content: region_content + } = axes + } = figure + ) do %Figure{ figure | axes: %{ @@ -319,6 +320,7 @@ defmodule Matplotex.Figure.Areal do } } end + def set_region_content(figure), do: figure end end diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 2632cb9..1ae7d60 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -1,4 +1,6 @@ defmodule Matplotex.Figure.Cast do + alias Matplotex.Figure.Lead + alias Matplotex.Element.Legend alias Matplotex.Utils.Algebra alias Matplotex.Figure.Areal.XyRegion.Coords, as: XyCoords alias Matplotex.Figure.Areal.Region @@ -9,6 +11,7 @@ defmodule Matplotex.Figure.Cast do alias Matplotex.Element.Line alias Matplotex.Figure.Coords alias Matplotex.Figure + alias Matplotex.Figure.Dataset @tickline_offset 5 / 96 @xtick_type "figure.x_tick" @ytick_type "figure.y_tick" @@ -198,7 +201,6 @@ defmodule Matplotex.Figure.Cast do rc_params: %RcParams{title_font: title_font, label_padding: title_padding} } = figure ) do - {title_x, title_y} = region_title |> calculate_center(:x) |> Algebra.flip_y_coordinate() title = @@ -802,6 +804,50 @@ defmodule Matplotex.Figure.Cast do def cast_vgrids_by_region(figure), do: figure + def cast_legends( + %Figure{ + rc_params: %RcParams{legend_font: legend_font, x_padding: padding}, + axes: + %{ + dataset: datasets, + element: elements, + region_legend: %Region{x: x_region_legend, y: y_region_legend, width: width_region_legend}, + + } = axes + } = figure + ) do + + space_for_one_line = Lead.height_required_for_text(legend_font, "") + padding = padding * width_region_legend + + legend_elements = + datasets + |> Enum.with_index() + |> Enum.map(fn {%Dataset{color: color, label: label}, idx} -> + {x_region_legend, y_region_legend} = + Algebra.transform_given_point( + x_region_legend, + y_region_legend, + padding, + -idx * space_for_one_line-padding + ) + |>Algebra.flip_y_coordinate() + + %Legend{ + type: "figure.legend", + x: x_region_legend, + y: y_region_legend, + color: color, + label: label, + width: space_for_one_line, + height: space_for_one_line + } + |> Legend.with_label(legend_font, padding) + end) + + %Figure{figure | axes: %{axes | element: elements ++ legend_elements}} + end + defp plotify_tick(module, {label, value}, lim, axis_size, transition, data, axis) do {module.plotify(value, lim, axis_size, transition, data, axis), label} end diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index 0528aff..5fa0ec2 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -178,11 +178,32 @@ defmodule Matplotex.Figure.Lead do } end - def focus_to_origin(%Figure{rc_params: %RcParams{padding: padding}, axes: %{region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content, height: height_region_content}}=axes}=figure) do + def focus_to_origin( + %Figure{ + rc_params: %RcParams{padding: padding}, + axes: + %{ + region_content: %Region{ + x: x_region_content, + y: y_region_content, + width: width_region_content, + height: height_region_content + } + } = axes + } = figure + ) do width_padding_value = width_region_content * padding height_padding_value = height_region_content * padding radius = plotable_radius(width_region_content, height_region_content, padding) - {center_x, center_y} = x_region_content|>Algebra.transform_given_point(y_region_content, width_padding_value, height_padding_value)|>Algebra.transform_given_point({radius, radius}) + + {center_x, center_y} = + x_region_content + |> Algebra.transform_given_point( + y_region_content, + width_padding_value, + height_padding_value + ) + |> Algebra.transform_given_point({radius, radius}) %Figure{ figure @@ -193,13 +214,17 @@ defmodule Matplotex.Figure.Lead do } } end + def focus_to_origin(figure), do: figure + defp plotable_radius(width, height, padding) when height < width do (height - height * padding * 2) / 2 end - defp plotable_radius(width, height, padding) when width < height do - (width - width * padding * 2) / 2 - end + + defp plotable_radius(width, height, padding) when width < height do + (width - width * padding * 2) / 2 + end + # def focus_to_origin( # %Figure{ # figsize: {width, height}, @@ -300,14 +325,14 @@ defmodule Matplotex.Figure.Lead do end def height_required_for_text( - %Font{ - font_size: font_size, - pt_to_inch_ratio: pt_to_inch_ratio, - rotation: rotation, - flate: flate - }, - text - ) do + %Font{ + font_size: font_size, + pt_to_inch_ratio: pt_to_inch_ratio, + rotation: rotation, + flate: flate + }, + text + ) do text_height = to_number(font_size) * pt_to_inch_ratio text_length = tick_length(text) * pt_to_inch_ratio rotation = deg_to_rad(rotation) diff --git a/lib/matplotex/figure/radial.ex b/lib/matplotex/figure/radial.ex index 9cdc586..8fbc267 100644 --- a/lib/matplotex/figure/radial.ex +++ b/lib/matplotex/figure/radial.ex @@ -67,24 +67,27 @@ defmodule Matplotex.Figure.Radial do } } end + def set_region_title(figure), do: figure + def set_region_legend( - %Figure{ - figsize: {fwidth, _fheight}, - rc_params: %RcParams{legend_width: legend_width}, - axes: - %{ - border: {_lx, by, rx, ty}, - show_legend: true, - region_title: %Region{y: y_region_title, height: height_region_title} - } = axes - } = figure - ) do + %Figure{ + figsize: {fwidth, _fheight}, + rc_params: %RcParams{legend_width: legend_width}, + axes: + %{ + border: {_lx, by, rx, ty}, + show_legend: true, + region_title: %Region{y: y_region_title, height: height_region_title} + } = axes + } = figure + ) do width_region_legend = fwidth * legend_width height_region_legend = abs(by - y_region_title) {x_region_legend, y_region_legend} = - Algebra.transform_given_point(rx, abs(ty), -width_region_legend,height_region_title)|>Algebra.flip_y_coordinate() + Algebra.transform_given_point(rx, abs(ty), -width_region_legend, height_region_title) + |> Algebra.flip_y_coordinate() %Figure{ figure @@ -99,20 +102,20 @@ defmodule Matplotex.Figure.Radial do } } end + def set_region_legend(figure), do: figure def set_region_content( - %Figure{ - axes: - %{ - border: {lx, by, _rx, _ty}, - region_title: %Region{height: height_region_title}, - region_legend: %Region{width: width_region_legend}, - size: {width, height} - } = axes - } = figure - ) do - + %Figure{ + axes: + %{ + border: {lx, by, _rx, _ty}, + region_title: %Region{height: height_region_title}, + region_legend: %Region{width: width_region_legend}, + size: {width, height} + } = axes + } = figure + ) do width_region_content = width - width_region_legend height_region_content = height - height_region_title diff --git a/lib/matplotex/figure/radial/pie.ex b/lib/matplotex/figure/radial/pie.ex index e69a158..a9d6769 100644 --- a/lib/matplotex/figure/radial/pie.ex +++ b/lib/matplotex/figure/radial/pie.ex @@ -1,5 +1,4 @@ defmodule Matplotex.Figure.Radial.Pie do - alias Matplotex.Figure.RcParams alias Matplotex.Utils.Algebra alias Matplotex.Figure.Areal.Region @@ -27,11 +26,14 @@ defmodule Matplotex.Figure.Radial.Pie do @impl Radial def create(%Figure{axes: axes} = figure, sizes, opts) do - dataset = if sizes|>Enum.sum()|>abs() > 0 do - Dataset.cast(%Dataset{sizes: sizes}, opts) - else - raise Matplotex.InputError, "Invalid set of values for a pie chart, sum of sizes should be greater than 0" - end + dataset = + if sizes |> Enum.sum() |> abs() > 0 do + Dataset.cast(%Dataset{sizes: sizes}, opts) + else + raise Matplotex.InputError, + "Invalid set of values for a pie chart, sum of sizes should be greater than 0" + end + %Figure{figure | axes: %{axes | dataset: dataset}} end @@ -61,11 +63,13 @@ defmodule Matplotex.Figure.Radial.Pie do element: elements } = axes } = figure - ) when fwidth > 0 and fheight > 0 do + ) + when fwidth > 0 and fheight > 0 do %Region{x: legx, y: legy} = Algebra.flip_y_coordinate(region_legend) total_size = Enum.sum(sizes) legend_rect_side = height / length(sizes) / 2 center = Algebra.flip_y_coordinate(center) + slices = sizes |> Enum.zip(labels) @@ -137,7 +141,8 @@ defmodule Matplotex.Figure.Radial.Pie do cy: cy } - {x_legend, y_legend} = Algebra.transform_given_point(0, legend_unit_height, x_legend, y_legend) + {x_legend, y_legend} = + Algebra.transform_given_point(0, legend_unit_height, x_legend, y_legend) legend = %RadLegend{ diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index 182fcbd..46268b6 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -65,9 +65,11 @@ defmodule Matplotex.Utils.Algebra do |> List.to_tuple() |> then(fn {x, y, _} -> {x, y} end) end + def transform_given_point({x, y}, {ox, oy}, theta \\ 0) do transform_given_point(x, y, ox, oy, theta) end + def transform_given_point(x, y, ox, oy, theta \\ 0) do point_matrix = Nx.tensor([x, y, 1], type: {:f, @tensor_data_type_bits}) @@ -85,8 +87,6 @@ defmodule Matplotex.Utils.Algebra do |> then(fn {x, y, _} -> {x, y} end) end - - def flip_y_coordinate({x, y}) do {x, -y} end diff --git a/test/matplotex/figure/cast_test.exs b/test/matplotex/figure/cast_test.exs index fd58245..f5ce675 100644 --- a/test/matplotex/figure/cast_test.exs +++ b/test/matplotex/figure/cast_test.exs @@ -96,4 +96,16 @@ defmodule Matplotex.Figure.CastTest do length(x_ticks) end end + + describe "cast_legends/1" do + test "add legends for each datasets", %{figure: figure} do + assert %Figure{axes: %{element: elements, dataset: dataset}} = + figure + |> Lead.set_regions_areal() + |> Cast.cast_legends() + + assert Enum.filter(elements, fn x -> x.type == "figure.legend" end) |> length() == + length(dataset) + end + end end diff --git a/test/matplotex/figure/lead_test.exs b/test/matplotex/figure/lead_test.exs index 4f67f80..1bc99b3 100644 --- a/test/matplotex/figure/lead_test.exs +++ b/test/matplotex/figure/lead_test.exs @@ -240,10 +240,11 @@ defmodule Matplotex.Figure.LeadTest do assert 2 * lx + width_region_content + width_region_legend == fwidth assert 2 * abs(ty) + height_region_content + height_region_title == fheight end + @tag radial: true test "sets origin to center of the figure", %{figure: figure} do - assert %Figure{axes: %{center: %TwoD{x: cx, y: cy} }} = Lead.set_regions_radial(figure) - assert cx != 0 && cy != 0 + assert %Figure{axes: %{center: %TwoD{x: cx, y: cy}}} = Lead.set_regions_radial(figure) + assert cx != 0 && cy != 0 end end end From 379d1cde655aa9c43d39fb0d5bbb7caa67d254f6 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Wed, 18 Dec 2024 19:43:46 +0530 Subject: [PATCH 2/3] legend for all plots --- lib/matplotex.ex | 26 ++++++++++----------- lib/matplotex/element/circle.ex | 4 ++-- lib/matplotex/element/legend.ex | 11 ++++++--- lib/matplotex/element/polygon.ex | 1 + lib/matplotex/figure/areal.ex | 2 +- lib/matplotex/figure/areal/bar_chart.ex | 9 +++++++- lib/matplotex/figure/areal/line_plot.ex | 23 ++++++++++++++++++- lib/matplotex/figure/areal/scatter.ex | 13 +++++++++-- lib/matplotex/figure/cast.ex | 24 ++++++++++++-------- lib/matplotex/figure/marker.ex | 30 ++++++++++++++++++++++++- test/matplotex/figure/cast_test.exs | 1 + test/matplotex_test.exs | 10 ++++----- 12 files changed, 116 insertions(+), 38 deletions(-) diff --git a/lib/matplotex.ex b/lib/matplotex.ex index 7a25e91..7d48036 100644 --- a/lib/matplotex.ex +++ b/lib/matplotex.ex @@ -196,19 +196,19 @@ defmodule Matplotex do """ - @spec legend(Figure.t(), keyword() | map()) :: Figure.t() - def legend(figure, opts) when is_map(opts) do - Figure.add_legend(figure, opts) - end - - def legend(figure, [h | _t] = opts) when is_tuple(h) do - params = Enum.into(opts, %{}) - Figure.add_legend(figure, params) - end - - def legend(figure, labels) when is_list(labels) do - Figure.add_legend(figure, %{labels: labels}) - end + # @spec legend(Figure.t(), keyword() | map()) :: Figure.t() + # def legend(figure, opts) when is_map(opts) do + # Figure.add_legend(figure, opts) + # end + + # def legend(figure, [h | _t] = opts) when is_tuple(h) do + # params = Enum.into(opts, %{}) + # Figure.add_legend(figure, params) + # end + + # def legend(figure, labels) when is_list(labels) do + # Figure.add_legend(figure, %{labels: labels}) + # end @doc """ Function to update figure params diff --git a/lib/matplotex/element/circle.ex b/lib/matplotex/element/circle.ex index 1bb6a04..6ee9682 100644 --- a/lib/matplotex/element/circle.ex +++ b/lib/matplotex/element/circle.ex @@ -21,7 +21,7 @@ defmodule Matplotex.Element.Circle do type="#{circle.type}" cx="#{get_cx(circle)}px" cy="#{get_cy(circle)}px" - r="#{circle.r}" + r="#{get_r(circle)}" fill="#{circle.fill}" stroke="#{circle.stroke}" stroke_width="#{circle.stroke_width}" > @@ -31,7 +31,7 @@ defmodule Matplotex.Element.Circle do def get_cx(%{cx: x}), do: to_pixel(x) def get_cy(%{cy: y}), do: to_pixel(y) - + def get_r(%{r: r}), do: to_pixel(r) @impl Element def flipy(%__MODULE__{cy: y} = circle, height) do %__MODULE__{circle | cy: height - y} diff --git a/lib/matplotex/element/legend.ex b/lib/matplotex/element/legend.ex index 9b0e278..dcddf93 100644 --- a/lib/matplotex/element/legend.ex +++ b/lib/matplotex/element/legend.ex @@ -1,7 +1,6 @@ defmodule Matplotex.Element.Legend do alias Matplotex.Element.Label alias Matplotex.Element - alias Matplotex.Element.Rect use Element @@ -15,6 +14,7 @@ defmodule Matplotex.Element.Legend do :y, :color, :label, + :handle, width: @legend_size, height: @legend_size, label_margin: @legend_size, @@ -25,7 +25,7 @@ defmodule Matplotex.Element.Legend do @impl Element def assemble(legend) do """ - #{Rect.assemble(legend)} + #{handle(legend)} #{Element.assemble(legend.label)} """ end @@ -35,6 +35,10 @@ defmodule Matplotex.Element.Legend do %__MODULE__{legend | y: height - y, label: Label.flipy(legend.label, height)} end + defp handle(%__MODULE__{handle: %handle_element{} = handle}) do + handle_element.assemble(handle) + end + def with_label( %__MODULE__{ label: text, @@ -43,7 +47,8 @@ defmodule Matplotex.Element.Legend do width: width, height: height } = legend, - legend_font, padding + legend_font, + padding ) do %{ legend diff --git a/lib/matplotex/element/polygon.ex b/lib/matplotex/element/polygon.ex index 173e1d2..c415c94 100644 --- a/lib/matplotex/element/polygon.ex +++ b/lib/matplotex/element/polygon.ex @@ -10,6 +10,7 @@ defmodule Matplotex.Element.Polygon do ~s( ) end diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 1d613a5..1a03ca7 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -12,7 +12,6 @@ defmodule Matplotex.Figure.Areal do alias Matplotex.Figure.Dimension alias Matplotex.Figure.Coords alias Matplotex.Figure.Text - alias Matplotex.Figure.Legend import Matplotex.Figure.Areal, only: [transformation: 7, do_transform: 6] import Matplotex.Blueprint.Frame @@ -272,6 +271,7 @@ defmodule Matplotex.Figure.Areal do ) do region_legend_width = f_width * legend_width region_x_width_after_legend = region_x_width - region_legend_width + {x_region_legend, y_region_legend} = Algebra.transform_given_point(-region_legend_width, -region_title_height, rx, ty) diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index 414f22d..bea2892 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -1,6 +1,7 @@ defmodule Matplotex.Figure.Areal.BarChart do import Matplotex.Figure.Numer alias Matplotex.Figure.Areal.Region + alias Matplotex.Element.Legend alias Matplotex.Figure.Dataset alias Matplotex.Element.Rect alias Matplotex.Figure.RcParams @@ -12,7 +13,6 @@ defmodule Matplotex.Figure.Areal.BarChart do @xmin_value 0 frame( - legend: %Legend{}, coords: %Coords{}, dimension: %Dimension{}, tick: %TwoD{}, @@ -100,6 +100,13 @@ defmodule Matplotex.Figure.Areal.BarChart do value * s + transition - minl * s end + def with_legend_handle( + %Legend{x: x, y: y, color: color, width: width, height: height} = legend, + _dataset + ) do + %Legend{legend | handle: %Rect{x: x, y: y, color: color, width: width, height: height}} + end + def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} end diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index 4ea11e0..06eedd0 100644 --- a/lib/matplotex/figure/areal/line_plot.ex +++ b/lib/matplotex/figure/areal/line_plot.ex @@ -1,4 +1,5 @@ defmodule Matplotex.Figure.Areal.LinePlot do + alias Matplotex.Utils.Algebra alias Matplotex.Figure.Areal.Region alias Matplotex.Figure.Areal.Ticker alias Matplotex.Figure.Marker @@ -8,11 +9,11 @@ defmodule Matplotex.Figure.Areal.LinePlot do alias Matplotex.Element.Line alias Matplotex.Figure.Coords alias Matplotex.Figure + alias Matplotex.Element.Legend use Matplotex.Figure.Areal frame( - legend: %Legend{}, coords: %Coords{}, dimension: %Dimension{}, tick: %TwoD{}, @@ -95,6 +96,26 @@ defmodule Matplotex.Figure.Areal.LinePlot do value * s + transition - minl * s end + def with_legend_handle( + %Legend{x: x, y: y, color: color, width: marker_size} = legend, + %Dataset{linestyle: linestyle} + ) do + + {x2, y2} = Algebra.transform_given_point(x, y, marker_size, marker_size /2) + +%Legend{legend | handle: %Line{ + type: "plot.line", + x1: x, + y1: y + marker_size /2, + x2: x2, + y2: y2 , + stroke: color, + fill: color, + linestyle: linestyle +}} +end + + def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} end diff --git a/lib/matplotex/figure/areal/scatter.ex b/lib/matplotex/figure/areal/scatter.ex index 15846af..a9f787a 100644 --- a/lib/matplotex/figure/areal/scatter.ex +++ b/lib/matplotex/figure/areal/scatter.ex @@ -3,7 +3,7 @@ defmodule Matplotex.Figure.Areal.Scatter do alias Matplotex.Figure.Areal.Ticker alias Matplotex.Figure.Marker alias Matplotex.Figure.Dataset - + alias Matplotex.Element.Legend alias Matplotex.Figure.Areal alias Matplotex.Figure.RcParams @@ -13,7 +13,6 @@ defmodule Matplotex.Figure.Areal.Scatter do use Areal frame( - legend: %Legend{}, coords: %Coords{}, dimension: %Dimension{}, tick: %TwoD{}, @@ -106,6 +105,7 @@ defmodule Matplotex.Figure.Areal.Scatter do |> Stream.concat(element), figure} end + defp capture(%Dataset{transformed: transformed} = dataset) do capture(transformed, [], dataset) end @@ -136,6 +136,15 @@ defmodule Matplotex.Figure.Areal.Scatter do value * s + transition - minl * s end + def with_legend_handle( + %Legend{x: x, y: y, color: color, width: marker_size} = legend, + %Dataset{marker: marker} + ) do + + %Legend{legend | handle: Marker.marker_legend(marker, x, y, color, marker_size)} + end + + def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} end diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 1ae7d60..54f3eb7 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -808,30 +808,33 @@ defmodule Matplotex.Figure.Cast do %Figure{ rc_params: %RcParams{legend_font: legend_font, x_padding: padding}, axes: - %{ + %chart{ dataset: datasets, element: elements, - region_legend: %Region{x: x_region_legend, y: y_region_legend, width: width_region_legend}, - + show_legend: true, + region_legend: %Region{ + x: x_region_legend, + y: y_region_legend, + width: width_region_legend + } } = axes } = figure ) do - space_for_one_line = Lead.height_required_for_text(legend_font, "") padding = padding * width_region_legend legend_elements = datasets |> Enum.with_index() - |> Enum.map(fn {%Dataset{color: color, label: label}, idx} -> + |> Enum.map(fn {%Dataset{color: color, label: label} = dataset, idx} -> {x_region_legend, y_region_legend} = Algebra.transform_given_point( x_region_legend, y_region_legend, padding, - -idx * space_for_one_line-padding + -idx * space_for_one_line - padding ) - |>Algebra.flip_y_coordinate() + |> Algebra.flip_y_coordinate() %Legend{ type: "figure.legend", @@ -839,15 +842,18 @@ defmodule Matplotex.Figure.Cast do y: y_region_legend, color: color, label: label, - width: space_for_one_line, - height: space_for_one_line + width: space_for_one_line - padding, + height: space_for_one_line - padding } |> Legend.with_label(legend_font, padding) + |> chart.with_legend_handle(dataset) end) %Figure{figure | axes: %{axes | element: elements ++ legend_elements}} end + def cast_legends(figure), do: figure + defp plotify_tick(module, {label, value}, lim, axis_size, transition, data, axis) do {module.plotify(value, lim, axis_size, transition, data, axis), label} end diff --git a/lib/matplotex/figure/marker.ex b/lib/matplotex/figure/marker.ex index f849e4f..8319bdc 100644 --- a/lib/matplotex/figure/marker.ex +++ b/lib/matplotex/figure/marker.ex @@ -1,4 +1,6 @@ defmodule Matplotex.Figure.Marker do + + alias Matplotex.Utils.Algebra alias Matplotex.Element.Rect alias Matplotex.Element.Polygon alias Matplotex.Element.Circle @@ -7,7 +9,8 @@ defmodule Matplotex.Figure.Marker do def generate_marker("o", x, y, fill, marker_size), # TODO: type shoudl be dynamic plot.marker or scatter.marker - do: %Circle{type: "plot.marker", cx: x, cy: y, fill: fill, r: marker_size} + + do: %Circle{type: "plot.marker", cx: x, cy: y, fill: fill, r: marker_size / 96} def generate_marker("^", x, y, fill, marker_size), do: %Polygon{ @@ -25,4 +28,29 @@ defmodule Matplotex.Figure.Marker do height: marker_size / 96, color: fill } + + def marker_legend("o", x, y, fill, marker_size) do + {cx, cy} = Algebra.transform_given_point(x, y, marker_size / 2, marker_size /2) + %Circle{type: "legend.handle", cx: cx, cy: cy, fill: fill, r: marker_size / 2} + end + + def marker_legend("^", x, y, fill, marker_size) do + %Polygon{ + type: "legend.handle", + points: [{x , y}, {x + marker_size, y}, {x, y + marker_size}], + fill: fill + } + + end + + def marker_legend("s", x, y, fill, marker_size), + do: %Rect{ + type: "legend.handle", + x: x, + y: y, + width: marker_size, + height: marker_size, + color: fill + } + end diff --git a/test/matplotex/figure/cast_test.exs b/test/matplotex/figure/cast_test.exs index f5ce675..8489a08 100644 --- a/test/matplotex/figure/cast_test.exs +++ b/test/matplotex/figure/cast_test.exs @@ -102,6 +102,7 @@ defmodule Matplotex.Figure.CastTest do assert %Figure{axes: %{element: elements, dataset: dataset}} = figure |> Lead.set_regions_areal() + |> Matplotex.show_legend() |> Cast.cast_legends() assert Enum.filter(elements, fn x -> x.type == "figure.legend" end) |> length() == diff --git a/test/matplotex_test.exs b/test/matplotex_test.exs index 84e37fe..999671f 100644 --- a/test/matplotex_test.exs +++ b/test/matplotex_test.exs @@ -44,11 +44,11 @@ defmodule MatplotexTest do assert figure.axes.title == title end - test "adds legend to the figure ", %{figure: figure} do - labels = ["Data 1", "Data 2"] - figure = Matplotex.legend(figure, %{labels: labels}) - assert figure.axes.legend.labels == labels - end + # test "adds legend to the figure ", %{figure: figure} do + # labels = ["Data 1", "Data 2"] + # figure = Matplotex.legend(figure, %{labels: labels}) + # assert figure.axes.legend.labels == labels + # end test "adds xlim to the figure ", %{figure: figure} do xlim = {1, 10} From d3225cd68be6d2b1e6f16ba34ead8c97cc8c4489 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Wed, 18 Dec 2024 19:45:35 +0530 Subject: [PATCH 3/3] put on behaviour --- lib/matplotex/figure/areal.ex | 1 + lib/matplotex/figure/areal/bar_chart.ex | 1 + lib/matplotex/figure/areal/line_plot.ex | 38 +++++++++++++------------ lib/matplotex/figure/areal/scatter.ex | 4 +-- lib/matplotex/figure/marker.ex | 7 ++--- 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 1a03ca7..42bc022 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -5,6 +5,7 @@ defmodule Matplotex.Figure.Areal do @callback create(struct(), any(), keyword()) :: struct() @callback materialize(struct()) :: struct() @callback plotify(number(), tuple(), number(), number(), list(), atom()) :: number() + @callback with_legend_handle(struct(), struct()) :: struct() defmacro __using__(_) do quote do @behaviour Matplotex.Figure.Areal diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index bea2892..3932729 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -100,6 +100,7 @@ defmodule Matplotex.Figure.Areal.BarChart do value * s + transition - minl * s end + @impl Areal def with_legend_handle( %Legend{x: x, y: y, color: color, width: width, height: height} = legend, _dataset diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index 06eedd0..9412177 100644 --- a/lib/matplotex/figure/areal/line_plot.ex +++ b/lib/matplotex/figure/areal/line_plot.ex @@ -96,25 +96,27 @@ defmodule Matplotex.Figure.Areal.LinePlot do value * s + transition - minl * s end + @impl Areal def with_legend_handle( - %Legend{x: x, y: y, color: color, width: marker_size} = legend, - %Dataset{linestyle: linestyle} - ) do - - {x2, y2} = Algebra.transform_given_point(x, y, marker_size, marker_size /2) - -%Legend{legend | handle: %Line{ - type: "plot.line", - x1: x, - y1: y + marker_size /2, - x2: x2, - y2: y2 , - stroke: color, - fill: color, - linestyle: linestyle -}} -end - + %Legend{x: x, y: y, color: color, width: marker_size} = legend, + %Dataset{linestyle: linestyle} + ) do + {x2, y2} = Algebra.transform_given_point(x, y, marker_size, marker_size / 2) + + %Legend{ + legend + | handle: %Line{ + type: "plot.line", + x1: x, + y1: y + marker_size / 2, + x2: x2, + y2: y2, + stroke: color, + fill: color, + linestyle: linestyle + } + } + end def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} diff --git a/lib/matplotex/figure/areal/scatter.ex b/lib/matplotex/figure/areal/scatter.ex index a9f787a..c9d559d 100644 --- a/lib/matplotex/figure/areal/scatter.ex +++ b/lib/matplotex/figure/areal/scatter.ex @@ -105,7 +105,6 @@ defmodule Matplotex.Figure.Areal.Scatter do |> Stream.concat(element), figure} end - defp capture(%Dataset{transformed: transformed} = dataset) do capture(transformed, [], dataset) end @@ -136,15 +135,14 @@ defmodule Matplotex.Figure.Areal.Scatter do value * s + transition - minl * s end + @impl Areal def with_legend_handle( %Legend{x: x, y: y, color: color, width: marker_size} = legend, %Dataset{marker: marker} ) do - %Legend{legend | handle: Marker.marker_legend(marker, x, y, color, marker_size)} end - def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} end diff --git a/lib/matplotex/figure/marker.ex b/lib/matplotex/figure/marker.ex index 8319bdc..459485d 100644 --- a/lib/matplotex/figure/marker.ex +++ b/lib/matplotex/figure/marker.ex @@ -1,5 +1,4 @@ defmodule Matplotex.Figure.Marker do - alias Matplotex.Utils.Algebra alias Matplotex.Element.Rect alias Matplotex.Element.Polygon @@ -30,17 +29,16 @@ defmodule Matplotex.Figure.Marker do } def marker_legend("o", x, y, fill, marker_size) do - {cx, cy} = Algebra.transform_given_point(x, y, marker_size / 2, marker_size /2) + {cx, cy} = Algebra.transform_given_point(x, y, marker_size / 2, marker_size / 2) %Circle{type: "legend.handle", cx: cx, cy: cy, fill: fill, r: marker_size / 2} end def marker_legend("^", x, y, fill, marker_size) do %Polygon{ type: "legend.handle", - points: [{x , y}, {x + marker_size, y}, {x, y + marker_size}], + points: [{x, y}, {x + marker_size, y}, {x, y + marker_size}], fill: fill } - end def marker_legend("s", x, y, fill, marker_size), @@ -52,5 +50,4 @@ defmodule Matplotex.Figure.Marker do height: marker_size, color: fill } - end