From 89f8cebb1f0653f4bb019bb6a3ad279418a72d56 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 28 Nov 2024 19:19:42 +0530 Subject: [PATCH 01/14] casting labels and ticks by region --- lib/matplotex/element/label.ex | 5 +- lib/matplotex/figure/areal/line_plot.ex | 22 ++ lib/matplotex/figure/areal/region.ex | 10 + .../figure/areal/xy_region/coords.ex | 2 +- lib/matplotex/figure/cast.ex | 354 ++++++++++++++++-- lib/matplotex/figure/lead.ex | 11 +- lib/matplotex/utils/algebra.ex | 8 +- test/matplotex/element/label_test.exs | 15 + test/matplotex/figure/cast_test.exs | 54 +++ test/support/plot_case.ex | 2 - 10 files changed, 430 insertions(+), 53 deletions(-) create mode 100644 test/matplotex/element/label_test.exs diff --git a/lib/matplotex/element/label.ex b/lib/matplotex/element/label.ex index 361ae57..ff9694b 100644 --- a/lib/matplotex/element/label.ex +++ b/lib/matplotex/element/label.ex @@ -56,7 +56,10 @@ defmodule Matplotex.Element.Label do """ end - + def cast_label(label, font) do + font = Map.from_struct(font) + struct(label, font) + end defp rotate(%{rotate: nil}), do: nil defp rotate(%{rotate: rotate, x: x, y: y}), diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index 1bbc752..d84b116 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 @@ -84,6 +85,27 @@ defmodule Matplotex.Figure.Areal.LinePlot do value * s + transition - minl * s end + def tick_homogeneous_transformation(value, {x_min, x_max}, axis_size, transition, :x) do + sx = axis_size/ (x_max - x_min) + sy = 0 # no need y scale + x = value + y = 0 # Ignoring y here + tx = transition + ty = 0 + theta = 0 # not applying rotation + Algebra.transform_given_point(x, y, sx, sy, tx, ty, theta) + end + def tick_homogeneous_transformation(value, {y_min, y_max}, axis_size, transition, :y) do + sx = 0 + sy = axis_size/ (y_max - y_min) + x = 0 + y = value + tx = 0 + ty = transition + theta = 0 # not applying rotation + Algebra.transform_given_point(x, y, sx, sy, tx, ty, theta) + end + def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} end diff --git a/lib/matplotex/figure/areal/region.ex b/lib/matplotex/figure/areal/region.ex index 213eb39..64cb508 100644 --- a/lib/matplotex/figure/areal/region.ex +++ b/lib/matplotex/figure/areal/region.ex @@ -1,3 +1,13 @@ defmodule Matplotex.Figure.Areal.Region do + alias Matplotex.Figure.Areal.XyRegion.Coords defstruct [:x, :y, :width, :height, :name, :theta, :coords] + + def get_label_coords(%__MODULE__{x: x, y: y, coords: %Coords{label: {label_x, label_y}}}) do + {x + label_x, y + label_y} + end + + def get_tick_coords(%__MODULE__{x: x, y: y, coords: %Coords{ticks: {tick_x, tick_y}}}) do + {x + tick_x, y + tick_y} + end + end diff --git a/lib/matplotex/figure/areal/xy_region/coords.ex b/lib/matplotex/figure/areal/xy_region/coords.ex index db4383d..aef9d2b 100644 --- a/lib/matplotex/figure/areal/xy_region/coords.ex +++ b/lib/matplotex/figure/areal/xy_region/coords.ex @@ -1,3 +1,3 @@ defmodule Matplotex.Figure.Areal.XyRegion.Coords do - defstruct [:label, :ticks] + defstruct [:label, :ticks, :grids] end diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 8b06687..d9c097b 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -1,4 +1,7 @@ defmodule Matplotex.Figure.Cast do + alias Matplotex.Figure.TwoD + alias Matplotex.Figure.Areal.XyRegion.Coords, as: XyCoords + alias Matplotex.Figure.Areal.Region alias Matplotex.Figure.Dataset alias Matplotex.Element.Tick alias Matplotex.Figure.RcParams @@ -65,6 +68,64 @@ defmodule Matplotex.Figure.Cast do def cast_spines(_figure) do raise ArgumentError, message: "Figure does't contain enough data to proceed" end + def cast_spines_by_region( + %Figure{ + axes: + %{ + region_content: %Region{ + x: content_x, + y: content_y, + width: content_width, + height: content_height + }, + element: elements + } = axes + } = figure + ) do + + left_x = content_x + bottom_y = content_y + right_x = content_x + content_width + top_y = content_y + content_height + # Four Line struct representing each corners + + left = %Line{ + x1: left_x, + y1: bottom_y, + x2: left_x, + y2: top_y, + type: "spine.left" + } + + right = %Line{ + x1: right_x, + y1: bottom_y, + x2: right_x, + y2: top_y, + type: "spine.right" + } + + top = %Line{ + x1: left_x, + y1: top_y, + x2: right_x, + y2: top_y, + type: "spine.top" + } + + bottom = %Line{ + x1: left_x, + y1: bottom_y, + x2: right_x, + y2: bottom_y, + type: "spine.bottom" + } + + %Figure{figure | axes: %{axes | element: elements ++ [left, right, top, bottom]}} + end + + def cast_spines_by_region(figure), do: figure + def cast_border( %Figure{ @@ -119,37 +180,32 @@ defmodule Matplotex.Figure.Cast do raise ArgumentError, message: "Invalid figure to cast title" end - def cast_label(%Figure{axes: %{label: nil}} = figure), do: figure + def cast_title_by_region( + %Figure{ + axes: + %{ + region_title: region, + title: title, + element: elements + } = axes, + rc_params: %RcParams{title_font: title_font, label_padding: title_padding} + } = figure + ) do + {title_x, title_y} = calculate_center(region, :x) - # def cast_label( - # %Figure{ - # axes: %{label: labels, coords: coords, element: element} = axes, - # rc_params: rc_params - # } = figure - # ) do - # label_elements = - # Enum.map(labels, fn {axis, label} -> - # {x, y} = label_coords = Map.get(coords, :"#{axis}_label") - # ticks_coords = Map.get(coords, :"#{axis}_ticks") - # height = calculate_distance(label_coords, ticks_coords) - # width = calculate_width(coords, axis) - # label_font = rc_params |> RcParams.get_rc(:"get_#{axis}_label_font") |> Map.from_struct() - - # %Label{ - # type: "figure.#{axis}_label", - # x: x, - # y: y, - # text: label, - # height: height, - # width: width, - # rotate: rotate_label(axis) - # } - # |> merge_structs(label_font) - # end) - - # element = element ++ label_elements - # %Figure{figure | axes: %{axes | element: element}} - # end + title = + %Label{ + type: "figure.title", + x: title_x, + y: title_y+title_padding, + text: title + } + |> Label.cast_label(title_font) + + %Figure{figure | axes: %{axes | element: elements ++ [title]}} + end + + def cast_label(%Figure{axes: %{label: nil}} = figure), do: figure def cast_label(figure) do figure @@ -157,9 +213,12 @@ defmodule Matplotex.Figure.Cast do |> cast_ylabel() end - # def cast_label(_figure) do - # raise ArgumentError, message: "Invalid figure to cast label" - # end + def cast_label_by_region(figure) do + figure + |>cast_xlabel_by_region() + |>cast_ylabel_by_region() + end + def cast_xlabel( %Figure{ @@ -185,27 +244,51 @@ defmodule Matplotex.Figure.Cast do end def cast_xlabel(figure), do: figure + def cast_xlabel_by_region( + %Figure{ + axes: %{label: %{x: x_label}, region_x: region_x, element: element} = axes, + rc_params: %RcParams{x_label_font: x_label_font} + } = figure + ) + when not is_nil(x_label) do +{_, x_label_y} = Region.get_label_coords(region_x) +{x_label_x, _} = calculate_center(region_x, :x) + +x_label = + %Label{ + type: "figure.x_label", + x: x_label_x, + y: x_label_y, + text: x_label + } + |>Label.cast_label(x_label_font) + +element = element ++ [x_label] +%Figure{figure | axes: %{axes | element: element}} +end + + def cast_ylabel( %Figure{ - axes: %{label: %{y: y_label}, coords: coords, element: element} = axes, - rc_params: %RcParams{y_label_font: label_font} + axes: %{label: %{y: y_label}, region_y: region_y, element: element} = axes, + rc_params: %RcParams{y_label_font: y_label_font} } = figure ) when not is_nil(y_label) do - ylabel_coords = Map.get(coords, :y_label) + {y_label_x, _} = Region.get_label_coords(region_y) - {x, y} = calculate_center(coords, ylabel_coords, :y) + {_, y_label_y} = calculate_center(region_y, :y) y_label = %Label{ type: "figure.y_label", - x: x, - y: y, + x: y_label_x, + y: y_label_y, text: y_label, rotate: rotate_label(:y) } - |> merge_structs(label_font) + |> Label.cast_label(y_label_font) element = element ++ [y_label] %Figure{figure | axes: %{axes | element: element}} @@ -213,6 +296,31 @@ defmodule Matplotex.Figure.Cast do def cast_ylabel(figure), do: figure + def cast_ylabel_by_region( + %Figure{ + axes: %{label: %{y: y_label}, region_y: region_y, element: element} = axes, + rc_params: %RcParams{y_label_font: y_label_font} + } = figure + ) + when not is_nil(y_label) do + {y_label_x, _} = Region.get_label_coords(region_y) + + {_, y_label_y} = calculate_center(region_y, :y) + + y_label = + %Label{ + type: "figure.y_label", + x: y_label_x, + y: y_label_y, + text: y_label, + rotate: rotate_label(:y) + } + |> Label.cast_label(y_label_font) + + element = element ++ [y_label] + %Figure{figure | axes: %{axes | element: element}} + end + def cast_xticks( %Figure{ axes: @@ -292,7 +400,78 @@ defmodule Matplotex.Figure.Cast do end def cast_xticks(%Figure{} = figure), do: figure + def cast_xticks_by_region( + %Figure{ + axes: + %module{ + tick: %{x: x_ticks}, + limit: %{x: {_min, _max} = xlim}, + region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content} = region_content, + region_x: region_x, + data: {x_data, y_data}, + dataset: dataset, + element: elements, + show_x_ticks: true, + coords: coords + } = axes, + rc_params: %RcParams{x_tick_font: x_tick_font, x_padding: x_padding, tick_line_length: tick_line_length} + } = figure + ) + when is_list(x_ticks) do +x_ticks = confine_ticks(x_ticks, xlim) +x_data = confine_data(x_data, xlim) +dataset = confine_data(dataset, xlim, :x) + +{_, x_tick_y} = Region.get_tick_coords(region_x) + +{xtick_elements, vgridxs} = + Enum.map(x_ticks, fn tick -> + {tick_position, label} = + transform_tick( + module, + tick, + xlim, + width_region_content - width_region_content * x_padding * 2, + x_region_content + width_region_content * x_padding, + :x + ) + label = + %Label{ + type: @xtick_type, + x: tick_position, + y: x_tick_y, + text: label + } + |> Label.cast_label(x_tick_font) + + line = %Line{ + type: @xtick_type, + x1: tick_position, + y1: x_tick_y, + x2: tick_position, + y2: x_tick_y - tick_line_length + } + + {%Tick{type: @xtick_type, tick_line: line, label: label}, tick_position} + end) + |> Enum.unzip() + +elements = elements ++ xtick_elements +vgrids = Enum.map(vgridxs, fn g -> {g, x_tick_y} end) + +%Figure{ + figure + | axes: %{ + axes + | data: {x_data, y_data}, + dataset: dataset, + element: elements, + coords: %{coords | vgrids: vgrids} + } +} +end + @spec cast_yticks(Matplotex.Figure.t()) :: Matplotex.Figure.t() def cast_yticks( %Figure{ axes: @@ -372,7 +551,76 @@ defmodule Matplotex.Figure.Cast do end def cast_ytick(%Figure{} = figure), do: figure + def cast_yticks_by_region( + %Figure{ + axes: + %module{ + tick: %{y: y_ticks}, + region_content: %Region{x: x_region_content, y: y_region_content, height: height_region_content} = region_content, + region_y: region_y, + element: elements, + coords: coords, + limit: %{y: {_min, _max} = ylim}, + data: {x_data, y_data}, + dataset: dataset, + show_y_ticks: true + } = axes, + rc_params: %RcParams{y_tick_font: y_tick_font, y_padding: y_padding, tick_line_length: tick_line_length} + } = figure + ) do +y_ticks = confine_ticks(y_ticks, ylim) +y_data = confine_data(y_data, ylim) +dataset = confine_data(dataset, ylim, :y) +{y_tick_x, _} = Region.get_tick_coords(region_y) +{ytick_elements, hgridys} = + Enum.map(y_ticks, fn tick -> + {tick_position, label} = + transform_tick( + module, + tick, + ylim, + height_region_content - height_region_content * y_padding * 2, + y_region_content + height_region_content * y_padding, + :y + ) + + label = + %Label{ + type: @ytick_type, + y: tick_position, + x: y_tick_x, + text: label, + text_anchor: "end", + dominant_baseline: "middle" + } + |>Label.cast_label(y_tick_font) + + line = %Line{ + type: @ytick_type, + y1: tick_position, + x1: x_region_content, + x2: x_region_content - tick_line_length, + y2: tick_position + } + {%Tick{type: @ytick_type, tick_line: line, label: label}, tick_position} + end) + |> Enum.unzip() + +elements = elements ++ ytick_elements +hgrids = Enum.map(hgridys, fn g -> {x_region_content, g} end) + +%Figure{ + figure + | axes: %{ + axes + | data: {x_data, y_data}, + dataset: dataset, + element: elements, + coords: %{coords | hgrids: hgrids} + } +} +end def cast_hgrids(%Figure{axes: %{coords: %{hgrids: nil}}} = figure), do: figure def cast_hgrids(%Figure{axes: %{show_h_grid: false}} = figure), do: figure @@ -435,6 +683,17 @@ defmodule Matplotex.Figure.Cast do %Figure{figure | axes: %{axes | element: elements}} end + # def cast_region_x( + # %Figure{axes: %{ label: %TwoD{x: x_label},tick: %TwoD{x: x_ticks}, + # region_x: %Region{x: x_region_x, y: x_region_y, width: width, height: height, + # coords: %XyCoords{label: {x_label_x, x_label_y}, ticks: x_tick_coords}}, + # rc_params: %RcParams{x_label_font: x_label_font}} = axes} = figure) do + + # xlabel_element = %Label{x: x_label_x, y: x_label_y, text: x_label}|>Label.cast_label(x_label_font) + # x_tick_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 @@ -443,6 +702,13 @@ defmodule Matplotex.Figure.Cast do {module.plotify(value, lim, axis_size, transition, data, axis), value} end + defp transform_tick(module, {label, value}, lim, axis_size, transition, axis) do + {module.tick_homogeneous_transformation(value, lim, axis_size, transition, axis), label} + end + defp transform_tick(module, value, lim, axis_size, transition, axis) do + {module.tick_homogeneous_transformation(value, lim, axis_size, transition,axis), value} + end + defp min_max([{_pos, _label} | _] = ticks) do ticks |> Enum.min_max_by(fn {_labe, pos} -> pos end) @@ -461,6 +727,14 @@ defmodule Matplotex.Figure.Cast do {x, calculate_distance(bottom_left, top_left) / 2 + y} end + defp calculate_center(%Region{x: x, y: y, width: width}, :x) do + {calculate_distance({x, y}, {x + width, y}) / 2 + x, y} + end + + defp calculate_center(%Region{x: x, y: y, height: height}, :y) do + {x, calculate_distance({x, y}, {x , y + height}) / 2 + y} + end + defp merge_structs(%module{} = st, sst) do sst = Map.from_struct(sst) params = st |> Map.from_struct() |> Map.merge(sst) diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index 47709ec..2444b5d 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -52,7 +52,8 @@ defmodule Matplotex.Figure.Lead do region_y: region_y, label: %TwoD{y: y_label, x: x_label}, tick: %TwoD{y: y_ticks, x: x_ticks}, - size: {f_width, f_height} + size: {f_width, f_height}, + coords: %Coords{} = coords } = axes, rc_params: %RcParams{ x_label_font: x_label_font, @@ -91,7 +92,7 @@ defmodule Matplotex.Figure.Lead do y: 0, height: space_required_for_region_x, width: f_width - space_required_for_region_y, - coords: %XyCoords{label: {x_region_x, 0}, ticks: {x_region_x, space_for_x_label}} + coords: %XyCoords{label: {0, 0}, ticks: {0, space_for_x_label}} }, region_y: %Region{ region_y @@ -99,7 +100,7 @@ defmodule Matplotex.Figure.Lead do y: y_region_y, width: space_required_for_region_y, height: f_height - space_required_for_region_x, - coords: %XyCoords{label: {0, y_region_y}, ticks: {space_for_x_label, y_region_y}} + coords: %XyCoords{label: {0, 0}, ticks: {space_for_ylabel, 0}} } } } @@ -117,7 +118,7 @@ defmodule Matplotex.Figure.Lead do region_title: region_title, size: {_f_width, f_height} } = axes, - rc_params: %RcParams{title_font: title_font} + rc_params: %RcParams{title_font: title_font, label_padding: title_padding} } = figure ) do space_for_title = height_required_for_text(title_font, title) @@ -131,7 +132,7 @@ defmodule Matplotex.Figure.Lead do | x: region_y_width, y: f_height - space_for_title, width: region_x_width, - height: space_for_title + height: space_for_title + title_padding }, region_y: %Region{ region_y diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index ac8e3d2..ff45192 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -49,16 +49,16 @@ defmodule Matplotex.Utils.Algebra do def svgfy(y, height), do: height - y - def transform_given_point(ox, oy, theta, px, py) do + def transform_given_point(x, y,sx, sy,tx, ty, theta) do Nx.tensor( [ - [:math.cos(theta), -:math.sin(theta), ox], - [:math.sin(theta), :math.cos(theta), oy], + [sx * :math.cos(theta), sy * -:math.sin(theta), tx], + [sx * :math.sin(theta), sy * :math.cos(theta), ty], [0, 0, 1] ], type: {:f, @tensor_data_type_bits} ) - |> Nx.dot(Nx.tensor([px, py, 1], type: {:f, @tensor_data_type_bits})) + |> Nx.dot(Nx.tensor([x, y, 1], type: {:f, @tensor_data_type_bits})) |> Nx.to_flat_list() |> List.to_tuple() |> then(fn {x, y, _} -> {x, y} end) diff --git a/test/matplotex/element/label_test.exs b/test/matplotex/element/label_test.exs new file mode 100644 index 0000000..a7fcd8a --- /dev/null +++ b/test/matplotex/element/label_test.exs @@ -0,0 +1,15 @@ +defmodule Matplotex.Element.LabelTest do + + use Matplotex.PlotCase + + alias Matplotex.Element.Label + alias Matplotex.Figure.Font + describe "cast_label/2" do + test "merging label with font" do + font = %Font{font_size: 22} + label = %Label{x: 1, y: 1} + assert %Label{font_size: updated_font_size} = Label.cast_label(label, font) + assert updated_font_size == 22 + end + end +end diff --git a/test/matplotex/figure/cast_test.exs b/test/matplotex/figure/cast_test.exs index 31863f8..842a495 100644 --- a/test/matplotex/figure/cast_test.exs +++ b/test/matplotex/figure/cast_test.exs @@ -19,7 +19,18 @@ defmodule Matplotex.Figure.CastTest do assert Enum.filter(elements, fn x -> x.type == "spine.left" end) |> length() == 1 end end + describe "cast_spines_by_region/1" do + test "add elements for borders in axes", %{figure: figure} do + figure = Lead.set_regions(figure) + assert %Figure{axes: %{element: elements}} = Cast.cast_spines_by_region(figure) + + assert Enum.any?(elements, fn x -> x.type == "spine.top" end) + assert Enum.any?(elements, fn x -> x.type == "spine.bottom" end) + assert Enum.any?(elements, fn x -> x.type == "spine.right" end) + assert Enum.any?(elements, fn x -> x.type == "spine.left" end) + end + end describe "cast_title" do test "add element for title in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -31,7 +42,17 @@ defmodule Matplotex.Figure.CastTest do assert Enum.filter(elements, fn x -> x.type == "figure.title" end) |> length == 1 end end + describe "cast_title_by_region/1" do + test "add element for title in axes", %{figure: figure} do + assert %Figure{axes: %{element: elements}} = + figure + |> Lead.set_regions() + |> Cast.cast_spines_by_region() + |> Cast.cast_title_by_region() + assert Enum.any?(elements, fn x -> x.type == "figure.title" end) + end + end describe "cast_label/1" do test "add element for label in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -43,6 +64,17 @@ defmodule Matplotex.Figure.CastTest do assert Enum.filter(elements, fn x -> x.type == "figure.y_label" end) |> length() == 1 end end + describe "cast_label_by_region/1" do + test "add element for label in axes", %{figure: figure} do + assert %Figure{axes: %{element: elements}} = + figure + |> Lead.set_regions() + |> Cast.cast_label_by_region() + + assert Enum.any?(elements, fn x -> x.type == "figure.x_label" end) + assert Enum.any?(elements, fn x -> x.type == "figure.y_label" end) + end + end describe "cast_xticks/1" do test "add element for tick in axes", %{figure: figure} do @@ -80,7 +112,17 @@ defmodule Matplotex.Figure.CastTest do length(x_ticks) end end + describe "cast_xticks_by_region/1" do + test "add element for tick in axes", %{figure: figure} do + assert %Figure{axes: %{element: elements, tick: %{x: x_ticks, y: _y_ticks}}} = + figure + |> Lead.set_regions() + |> Cast.cast_xticks_by_region() + assert Enum.filter(elements, fn x -> x.type == "figure.x_tick" end) |> length() == + length(x_ticks) + end + end describe "cast_yticks/1" do test "add element for tick in axes", %{figure: figure} do assert %Figure{axes: %{element: elements, tick: %{y: y_ticks}}} = @@ -93,6 +135,18 @@ defmodule Matplotex.Figure.CastTest do end end + describe "cast_yticks_by_region/1" do + test "add element for tick in axes", %{figure: figure} do + assert %Figure{axes: %{element: elements, tick: %{y: y_ticks}}} = + figure + |> Lead.set_regions() + |> Cast.cast_yticks_by_region() + + assert Enum.filter(elements, fn x -> x.type == "figure.y_tick" end) |> length() == + length(y_ticks) + end + end + # TODO: Testcases for show and hide hgrid describe "cast_hgrids/1" do test "add elements for horizontal grids", %{figure: figure} do diff --git a/test/support/plot_case.ex b/test/support/plot_case.ex index 345150e..86f7d65 100644 --- a/test/support/plot_case.ex +++ b/test/support/plot_case.ex @@ -4,8 +4,6 @@ defmodule Matplotex.PlotCase do using do quote do - alias Matplotex.Blueprint.Label - alias Matplotex.Blueprint.Line import Matplotex.PlotCase, only: [set_bar: 0] end end From 20be7da77c355b805410e1957a0a6ac232215636 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Mon, 2 Dec 2024 19:59:23 +0530 Subject: [PATCH 02/14] hetero transformation --- lib/matplotex/blueprint/frame.ex | 3 +- lib/matplotex/element/label.ex | 2 + lib/matplotex/figure/areal.ex | 12 + lib/matplotex/figure/areal/line_plot.ex | 22 +- lib/matplotex/figure/areal/region.ex | 1 - .../figure/areal/xy_region/coords.ex | 1 + lib/matplotex/figure/cast.ex | 418 ++++++++++-------- lib/matplotex/figure/font.ex | 14 +- lib/matplotex/figure/lead.ex | 64 ++- lib/matplotex/figure/rc_params.ex | 109 +++-- lib/matplotex/figure/sketch.ex | 2 +- lib/matplotex/helpers.ex | 4 +- lib/matplotex/utils/algebra.ex | 18 +- test/matplotex/element/label_test.exs | 2 +- test/matplotex/figure/cast_test.exs | 34 +- test/matplotex/figure/rc_param_test.exs | 45 +- 16 files changed, 485 insertions(+), 266 deletions(-) diff --git a/lib/matplotex/blueprint/frame.ex b/lib/matplotex/blueprint/frame.ex index 5802db3..5b7e356 100644 --- a/lib/matplotex/blueprint/frame.ex +++ b/lib/matplotex/blueprint/frame.ex @@ -94,7 +94,8 @@ defmodule Matplotex.Blueprint.Frame do show_h_grid: @show_by_default, show_x_ticks: @show_by_default, show_y_ticks: @show_by_default, - show_ticks: @show_by_default + show_ticks: @show_by_default, + border: nil ] |> Keyword.merge(opts) ) diff --git a/lib/matplotex/element/label.ex b/lib/matplotex/element/label.ex index ff9694b..c2c1aef 100644 --- a/lib/matplotex/element/label.ex +++ b/lib/matplotex/element/label.ex @@ -56,10 +56,12 @@ defmodule Matplotex.Element.Label do """ end + def cast_label(label, font) do font = Map.from_struct(font) struct(label, font) end + defp rotate(%{rotate: nil}), do: nil defp rotate(%{rotate: rotate, x: x, y: y}), diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 72db769..0889128 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -149,6 +149,18 @@ defmodule Matplotex.Figure.Areal do |> Cast.cast_title() end + def materialized_by_region(figure) do + figure + |> Lead.set_regions() + |> Cast.cast_xticks_by_region() + |> Cast.cast_yticks_by_region() + |> Cast.cast_hgrids_by_region() + |> Cast.cast_vgrids_by_region() + |> Cast.cast_spines_by_region() + |> Cast.cast_label_by_region() + |> Cast.cast_title_by_region() + end + defp update_tick(axes, tick) do %{axes | tick: tick} end diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index d84b116..fa2a516 100644 --- a/lib/matplotex/figure/areal/line_plot.ex +++ b/lib/matplotex/figure/areal/line_plot.ex @@ -44,7 +44,7 @@ defmodule Matplotex.Figure.Areal.LinePlot do @impl Areal def materialize(figure) do figure - |> __MODULE__.materialized() + |> __MODULE__.materialized_by_region() |> materialize_lines() end @@ -54,8 +54,7 @@ defmodule Matplotex.Figure.Areal.LinePlot do %{ dataset: data, limit: %{x: xlim, y: ylim}, - size: {width, height}, - coords: %Coords{bottom_left: {blx, bly}}, + region_content: %Region{x: blx, y: bly, width: width, height: height}, element: elements } = axes, rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} @@ -86,23 +85,28 @@ defmodule Matplotex.Figure.Areal.LinePlot do end def tick_homogeneous_transformation(value, {x_min, x_max}, axis_size, transition, :x) do - sx = axis_size/ (x_max - x_min) - sy = 0 # no need y scale + sx = axis_size / (x_max - x_min) + # no need y scale + sy = 0 x = value - y = 0 # Ignoring y here + # Ignoring y here + y = 0 tx = transition ty = 0 - theta = 0 # not applying rotation + # not applying rotation + theta = 0 Algebra.transform_given_point(x, y, sx, sy, tx, ty, theta) end + def tick_homogeneous_transformation(value, {y_min, y_max}, axis_size, transition, :y) do sx = 0 - sy = axis_size/ (y_max - y_min) + sy = axis_size / (y_max - y_min) x = 0 y = value tx = 0 ty = transition - theta = 0 # not applying rotation + # not applying rotation + theta = 0 Algebra.transform_given_point(x, y, sx, sy, tx, ty, theta) end diff --git a/lib/matplotex/figure/areal/region.ex b/lib/matplotex/figure/areal/region.ex index 64cb508..52f63a6 100644 --- a/lib/matplotex/figure/areal/region.ex +++ b/lib/matplotex/figure/areal/region.ex @@ -9,5 +9,4 @@ defmodule Matplotex.Figure.Areal.Region do def get_tick_coords(%__MODULE__{x: x, y: y, coords: %Coords{ticks: {tick_x, tick_y}}}) do {x + tick_x, y + tick_y} end - end diff --git a/lib/matplotex/figure/areal/xy_region/coords.ex b/lib/matplotex/figure/areal/xy_region/coords.ex index aef9d2b..09e0287 100644 --- a/lib/matplotex/figure/areal/xy_region/coords.ex +++ b/lib/matplotex/figure/areal/xy_region/coords.ex @@ -1,3 +1,4 @@ defmodule Matplotex.Figure.Areal.XyRegion.Coords do defstruct [:label, :ticks, :grids] + end diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index d9c097b..1b240fe 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -1,5 +1,5 @@ defmodule Matplotex.Figure.Cast do - alias Matplotex.Figure.TwoD + alias Matplotex.Utils.Algebra alias Matplotex.Figure.Areal.XyRegion.Coords, as: XyCoords alias Matplotex.Figure.Areal.Region alias Matplotex.Figure.Dataset @@ -68,6 +68,7 @@ defmodule Matplotex.Figure.Cast do def cast_spines(_figure) do raise ArgumentError, message: "Figure does't contain enough data to proceed" end + def cast_spines_by_region( %Figure{ axes: @@ -77,16 +78,19 @@ defmodule Matplotex.Figure.Cast do y: content_y, width: content_width, height: content_height - }, + }, + border: {lx, ly, _, _}, element: elements } = axes } = figure ) do - left_x = content_x - bottom_y = content_y - right_x = content_x + content_width - top_y = content_y + content_height + # Convert to the svg plane + IO.inspect({content_x, content_width, content_y, content_height}, label: "The content dims") + {left_x, bottom_y} = Algebra.transform_given_point(content_x, content_y,lx, ly, 0 ) |>Algebra.flip_y_coordinate() + {right_x, top_y} = Algebra.transform_given_point(content_x + content_width, content_y + content_height, lx, ly, 0)|>Algebra.flip_y_coordinate() + IO.inspect({left_x, bottom_y, right_x, top_y}, label: "The spines coords") + # Four Line struct representing each corners left = %Line{ @@ -126,7 +130,6 @@ defmodule Matplotex.Figure.Cast do def cast_spines_by_region(figure), do: figure - def cast_border( %Figure{ axes: %{border: {lx, by, rx, ty}, element: element} = axes, @@ -197,7 +200,7 @@ defmodule Matplotex.Figure.Cast do %Label{ type: "figure.title", x: title_x, - y: title_y+title_padding, + y: title_y + title_padding, text: title } |> Label.cast_label(title_font) @@ -215,11 +218,10 @@ defmodule Matplotex.Figure.Cast do def cast_label_by_region(figure) do figure - |>cast_xlabel_by_region() - |>cast_ylabel_by_region() + |> cast_xlabel_by_region() + |> cast_ylabel_by_region() end - def cast_xlabel( %Figure{ axes: %{label: %{x: x_label}, coords: coords, element: element} = axes, @@ -244,51 +246,50 @@ defmodule Matplotex.Figure.Cast do end def cast_xlabel(figure), do: figure + def cast_xlabel_by_region( - %Figure{ - axes: %{label: %{x: x_label}, region_x: region_x, element: element} = axes, - rc_params: %RcParams{x_label_font: x_label_font} - } = figure - ) - when not is_nil(x_label) do -{_, x_label_y} = Region.get_label_coords(region_x) -{x_label_x, _} = calculate_center(region_x, :x) - -x_label = - %Label{ - type: "figure.x_label", - x: x_label_x, - y: x_label_y, - text: x_label - } - |>Label.cast_label(x_label_font) - -element = element ++ [x_label] -%Figure{figure | axes: %{axes | element: element}} -end + %Figure{ + axes: %{label: %{x: x_label}, region_x: region_x, element: element} = axes, + rc_params: %RcParams{x_label_font: x_label_font} + } = figure + ) + when not is_nil(x_label) do + {_, x_label_y} = Region.get_label_coords(region_x) + {x_label_x, _} = calculate_center(region_x, :x) + x_label = + %Label{ + type: "figure.x_label", + x: x_label_x, + y: x_label_y, + text: x_label + } + |> Label.cast_label(x_label_font) + element = element ++ [x_label] + %Figure{figure | axes: %{axes | element: element}} + end def cast_ylabel( %Figure{ - axes: %{label: %{y: y_label}, region_y: region_y, element: element} = axes, - rc_params: %RcParams{y_label_font: y_label_font} + axes: %{label: %{y: y_label}, coords: coords, element: element} = axes, + rc_params: %RcParams{y_label_font: label_font} } = figure ) when not is_nil(y_label) do - {y_label_x, _} = Region.get_label_coords(region_y) + ylabel_coords = Map.get(coords, :y_label) - {_, y_label_y} = calculate_center(region_y, :y) + {x, y} = calculate_center(coords, ylabel_coords, :y) y_label = %Label{ type: "figure.y_label", - x: y_label_x, - y: y_label_y, + x: x, + y: y, text: y_label, rotate: rotate_label(:y) } - |> Label.cast_label(y_label_font) + |> merge_structs(label_font) element = element ++ [y_label] %Figure{figure | axes: %{axes | element: element}} @@ -400,77 +401,82 @@ end end def cast_xticks(%Figure{} = figure), do: figure + def cast_xticks_by_region( - %Figure{ - axes: - %module{ - tick: %{x: x_ticks}, - limit: %{x: {_min, _max} = xlim}, - region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content} = region_content, - region_x: region_x, - data: {x_data, y_data}, - dataset: dataset, - element: elements, - show_x_ticks: true, - coords: coords - } = axes, - rc_params: %RcParams{x_tick_font: x_tick_font, x_padding: x_padding, tick_line_length: tick_line_length} - } = figure - ) - when is_list(x_ticks) do -x_ticks = confine_ticks(x_ticks, xlim) -x_data = confine_data(x_data, xlim) -dataset = confine_data(dataset, xlim, :x) - -{_, x_tick_y} = Region.get_tick_coords(region_x) - -{xtick_elements, vgridxs} = - Enum.map(x_ticks, fn tick -> - {tick_position, label} = - transform_tick( - module, - tick, - xlim, - width_region_content - width_region_content * x_padding * 2, - x_region_content + width_region_content * x_padding, - :x + %Figure{ + axes: + %module{ + tick: %{x: x_ticks}, + limit: %{x: {_min, _max} = xlim}, + region_content: %Region{x: x_region_content, width: width_region_content}, + region_x: %Region{coords: coords_region_x} = region_x, + data: {x_data, y_data}, + dataset: dataset, + element: elements, + show_x_ticks: true + } = axes, + rc_params: %RcParams{ + x_tick_font: x_tick_font, + x_padding: x_padding, + tick_line_length: tick_line_length + } + } = figure ) + when is_list(x_ticks) do + x_ticks = confine_ticks(x_ticks, xlim) + x_data = confine_data(x_data, xlim) + dataset = confine_data(dataset, xlim, :x) - label = - %Label{ - type: @xtick_type, - x: tick_position, - y: x_tick_y, - text: label - } - |> Label.cast_label(x_tick_font) - - line = %Line{ - type: @xtick_type, - x1: tick_position, - y1: x_tick_y, - x2: tick_position, - y2: x_tick_y - tick_line_length - } + {_, x_tick_y} = Region.get_tick_coords(region_x) + + {xtick_elements, vgridxs} = + Enum.map(x_ticks, fn tick -> + {tick_position, label} = + transform_tick( + module, + tick, + xlim, + width_region_content - width_region_content * x_padding * 2, + x_region_content + width_region_content * x_padding, + :x + ) + x_tick_x = elem(tick_position, 0) + label = + %Label{ + type: @xtick_type, + x: x_tick_x, + y: x_tick_y, + text: label + } + |> Label.cast_label(x_tick_font) + + line = %Line{ + type: @xtick_type, + x1: x_tick_x, + y1: x_tick_y, + x2: x_tick_x, + y2: x_tick_y - tick_line_length + } + + {%Tick{type: @xtick_type, tick_line: line, label: label}, x_tick_x} + end) + |> Enum.unzip() - {%Tick{type: @xtick_type, tick_line: line, label: label}, tick_position} - end) - |> Enum.unzip() - -elements = elements ++ xtick_elements -vgrids = Enum.map(vgridxs, fn g -> {g, x_tick_y} end) - -%Figure{ - figure - | axes: %{ - axes - | data: {x_data, y_data}, - dataset: dataset, - element: elements, - coords: %{coords | vgrids: vgrids} + elements = elements ++ xtick_elements + vgrids = Enum.map(vgridxs, fn g -> {g, x_tick_y} end) + + %Figure{ + figure + | axes: %{ + axes + | data: {x_data, y_data}, + dataset: dataset, + element: elements, + region_x: %Region{region_x | coords: %XyCoords{coords_region_x | grids: vgrids}} + } } -} -end + end + @spec cast_yticks(Matplotex.Figure.t()) :: Matplotex.Figure.t() def cast_yticks( %Figure{ @@ -551,76 +557,83 @@ end end def cast_ytick(%Figure{} = figure), do: figure + def cast_yticks_by_region( - %Figure{ - axes: - %module{ - tick: %{y: y_ticks}, - region_content: %Region{x: x_region_content, y: y_region_content, height: height_region_content} = region_content, - region_y: region_y, - element: elements, - coords: coords, - limit: %{y: {_min, _max} = ylim}, - data: {x_data, y_data}, - dataset: dataset, - show_y_ticks: true - } = axes, - rc_params: %RcParams{y_tick_font: y_tick_font, y_padding: y_padding, tick_line_length: tick_line_length} - } = figure - ) do -y_ticks = confine_ticks(y_ticks, ylim) -y_data = confine_data(y_data, ylim) -dataset = confine_data(dataset, ylim, :y) -{y_tick_x, _} = Region.get_tick_coords(region_y) -{ytick_elements, hgridys} = - Enum.map(y_ticks, fn tick -> - {tick_position, label} = - transform_tick( - module, - tick, - ylim, - height_region_content - height_region_content * y_padding * 2, - y_region_content + height_region_content * y_padding, - :y - ) + %Figure{ + axes: + %module{ + tick: %{y: y_ticks}, + region_content: + %Region{x: x_region_content, y: y_region_content, height: height_region_content}, + region_y: %Region{coords: coords_region_y} = region_y, + element: elements, + limit: %{y: {_min, _max} = ylim}, + data: {x_data, y_data}, + dataset: dataset, + show_y_ticks: true + } = axes, + rc_params: %RcParams{ + y_tick_font: y_tick_font, + y_padding: y_padding, + tick_line_length: tick_line_length + } + } = figure + ) do + y_ticks = confine_ticks(y_ticks, ylim) + y_data = confine_data(y_data, ylim) + dataset = confine_data(dataset, ylim, :y) + {y_tick_x, _} = Region.get_tick_coords(region_y) - label = - %Label{ - type: @ytick_type, - y: tick_position, - x: y_tick_x, - text: label, - text_anchor: "end", - dominant_baseline: "middle" - } - |>Label.cast_label(y_tick_font) - - line = %Line{ - type: @ytick_type, - y1: tick_position, - x1: x_region_content, - x2: x_region_content - tick_line_length, - y2: tick_position - } + {ytick_elements, hgridys} = + Enum.map(y_ticks, fn tick -> + {tick_position, label} = + transform_tick( + module, + tick, + ylim, + height_region_content - height_region_content * y_padding * 2, + y_region_content + height_region_content * y_padding, + :y + ) + y_tick_y = elem(tick_position, 1) + label = + %Label{ + type: @ytick_type, + y: y_tick_y, + x: y_tick_x, + text: label, + text_anchor: "end", + dominant_baseline: "middle" + } + |> Label.cast_label(y_tick_font) + + line = %Line{ + type: @ytick_type, + y1: y_tick_y, + x1: x_region_content, + x2: x_region_content - tick_line_length, + y2: y_tick_y + } + + {%Tick{type: @ytick_type, tick_line: line, label: label}, y_tick_y} + end) + |> Enum.unzip() + + elements = elements ++ ytick_elements + hgrids = Enum.map(hgridys, fn g -> {x_region_content, g} end) - {%Tick{type: @ytick_type, tick_line: line, label: label}, tick_position} - end) - |> Enum.unzip() - -elements = elements ++ ytick_elements -hgrids = Enum.map(hgridys, fn g -> {x_region_content, g} end) - -%Figure{ - figure - | axes: %{ - axes - | data: {x_data, y_data}, - dataset: dataset, - element: elements, - coords: %{coords | hgrids: hgrids} + %Figure{ + figure + | axes: %{ + axes + | data: {x_data, y_data}, + dataset: dataset, + element: elements, + region_y: %Region{region_y | coords: %XyCoords{coords_region_y | grids: hgrids}} + } } -} -end + end + def cast_hgrids(%Figure{axes: %{coords: %{hgrids: nil}}} = figure), do: figure def cast_hgrids(%Figure{axes: %{show_h_grid: false}} = figure), do: figure @@ -652,6 +665,35 @@ end %Figure{figure | axes: %{axes | element: elements}} end + def cast_hgrids_by_region( + %Figure{ + axes: + %{ + show_h_grid: true, + region_y: %Region{coords: %XyCoords{grids: hgrids}}, + region_content: %Region{x: x_region_content, width: width_region_content}, + element: elements + } = axes + } = figure + ) do + hgrid_elements = + Enum.map(hgrids, fn {_x, y} -> + %Line{ + x1: x_region_content, + x2: x_region_content + width_region_content, + y1: y, + y2: y, + type: "figure.h_grid", + stroke: @stroke_grid, + stroke_width: @stroke_width_grid + } + end) + + elements = elements ++ hgrid_elements + + %Figure{figure | axes: %{axes | element: elements}} + end + def cast_vgrids(%Figure{axes: %{coords: %{vgrids: nil}}} = figure), do: figure def cast_vgrids(%Figure{axes: %{show_v_grid: false}} = figure), do: figure @@ -683,6 +725,35 @@ end %Figure{figure | axes: %{axes | element: elements}} end + def cast_vgrids_by_region( + %Figure{ + axes: + %{ + show_v_grid: true, + region_x: %Region{coords: %XyCoords{grids: vgrids}}, + region_content: %Region{y: y_region_content, height: height_region_content}, + element: elements + } = axes + } = figure + ) do + vgrid_elements = + Enum.map(vgrids, fn {x, _y} -> + %Line{ + x1: x, + x2: x, + y1: y_region_content, + y2: y_region_content + height_region_content, + type: "figure.v_grid", + stroke: @stroke_grid, + stroke_width: @stroke_width_grid + } + end) + + elements = elements ++ vgrid_elements + + %Figure{figure | axes: %{axes | element: elements}} + end + # def cast_region_x( # %Figure{axes: %{ label: %TwoD{x: x_label},tick: %TwoD{x: x_ticks}, # region_x: %Region{x: x_region_x, y: x_region_y, width: width, height: height, @@ -705,8 +776,9 @@ end defp transform_tick(module, {label, value}, lim, axis_size, transition, axis) do {module.tick_homogeneous_transformation(value, lim, axis_size, transition, axis), label} end + defp transform_tick(module, value, lim, axis_size, transition, axis) do - {module.tick_homogeneous_transformation(value, lim, axis_size, transition,axis), value} + {module.tick_homogeneous_transformation(value, lim, axis_size, transition, axis), value} end defp min_max([{_pos, _label} | _] = ticks) do @@ -732,7 +804,7 @@ end end defp calculate_center(%Region{x: x, y: y, height: height}, :y) do - {x, calculate_distance({x, y}, {x , y + height}) / 2 + y} + {x, calculate_distance({x, y}, {x, y + height}) / 2 + y} end defp merge_structs(%module{} = st, sst) do diff --git a/lib/matplotex/figure/font.ex b/lib/matplotex/figure/font.ex index d764f33..8add45a 100644 --- a/lib/matplotex/figure/font.ex +++ b/lib/matplotex/figure/font.ex @@ -31,7 +31,17 @@ defmodule Matplotex.Figure.Font do struct(__MODULE__, params) end - def update(font, params) do - struct(font, params) + def update(font, params, element) do + update_font(font, params, element) + end + defp update_font(font, params, element) do + font + |> Map.from_struct() + |> Map.keys() + |> Enum.reduce(font, fn key, acc -> + existing_value = Map.get(font, key) + value = Map.get(params, :"#{element}_#{key}", existing_value) + Map.put(acc, key, value) + end) end end diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index 2444b5d..e148511 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -1,4 +1,5 @@ defmodule Matplotex.Figure.Lead do + alias Matplotex.Utils.Algebra alias Matplotex.Figure.Areal.XyRegion.Coords, as: XyCoords alias Matplotex.Figure.Font alias Matplotex.Figure.Areal.Region @@ -33,15 +34,19 @@ defmodule Matplotex.Figure.Lead do def set_border(%Figure{margin: margin, axes: axes, figsize: {fig_width, fig_height}} = figure) do margin = margin / 2 lx = fig_width * margin - by = fig_height * margin + by = fig_height * margin rx = fig_width - fig_width * margin - ty = fig_height - fig_height * margin + ty = fig_height - fig_height * margin %Figure{figure | axes: %{axes | border: {lx, by, rx, ty}}} end defp set_frame_size(%Figure{margin: margin, figsize: {fwidth, fheight}, axes: axes} = figure) do frame_size = {fwidth - fwidth * 2 * margin, fheight - fheight * 2 * margin} - %Figure{figure | axes: %{axes | size: frame_size}} + lx = fwidth * margin + ty = -fheight * margin + rx = fwidth - fwidth * margin + by = fheight * margin - fheight + %Figure{figure | axes: %{axes | size: frame_size, border: {lx, by, rx, ty}}} end defp set_region_xy( @@ -53,7 +58,7 @@ defmodule Matplotex.Figure.Lead do label: %TwoD{y: y_label, x: x_label}, tick: %TwoD{y: y_ticks, x: x_ticks}, size: {f_width, f_height}, - coords: %Coords{} = coords + border: {lx, by, _, _} } = axes, rc_params: %RcParams{ x_label_font: x_label_font, @@ -70,18 +75,18 @@ defmodule Matplotex.Figure.Lead do y_tick = Enum.max_by(y_ticks, &tick_length(&1)) space_for_yticks = length_required_for_text(y_tick_font, y_tick) - x_region_x = + space_required_for_region_y = - [space_for_ylabel, y_tick, space_for_yticks, label_padding, tick_line_length] |> Enum.sum() + [space_for_ylabel, space_for_yticks, label_padding, tick_line_length] |> Enum.sum() space_for_x_label = height_required_for_text(x_label_font, x_label) x_tick = Enum.max_by(x_ticks, &tick_length/1) space_for_x_tick = height_required_for_text(x_tick_font, x_tick) - y_region_y = space_required_for_region_x = - [space_for_x_label, x_tick, space_for_x_tick, label_padding, tick_line_length] |> Enum.sum() - + [space_for_x_label, space_for_x_tick, label_padding, tick_line_length] |> Enum.sum() + {x_region_x, y_region_x} = Algebra.transform_given_point(space_required_for_region_y, 0, lx, by, 0) + {x_region_y, y_region_y} = Algebra.transform_given_point(0,space_required_for_region_x, lx,by,0 ) %Figure{ figure | axes: %{ @@ -89,14 +94,14 @@ defmodule Matplotex.Figure.Lead do | region_x: %Region{ region_x | x: x_region_x, - y: 0, + y: y_region_x, height: space_required_for_region_x, width: f_width - space_required_for_region_y, coords: %XyCoords{label: {0, 0}, ticks: {0, space_for_x_label}} }, region_y: %Region{ region_y - | x: 0, + | x: x_region_y, y: y_region_y, width: space_required_for_region_y, height: f_height - space_required_for_region_x, @@ -116,23 +121,26 @@ defmodule Matplotex.Figure.Lead do region_x: %Region{width: region_x_width}, region_y: %Region{width: region_y_width, height: region_y_height} = region_y, region_title: region_title, - size: {_f_width, f_height} + size: {_f_width, f_height}, + border: {lx, by, _, _} } = axes, - rc_params: %RcParams{title_font: title_font, label_padding: title_padding} + rc_params: %RcParams{title_font: title_font} } = figure ) do + IO.inspect(region_y_height, label: "Region y height before peeling title space") space_for_title = height_required_for_text(title_font, title) - + IO.inspect(space_for_title, label: "Space for title: " ) + {x_region_title, y_region_title} = Algebra.transform_given_point(region_y_width, space_for_title, lx, by, 0) %Figure{ figure | axes: %{ axes | region_title: %Region{ region_title - | x: region_y_width, - y: f_height - space_for_title, + | x: x_region_title, + y: y_region_title, width: region_x_width, - height: space_for_title + title_padding + height: space_for_title }, region_y: %Region{ region_y @@ -195,6 +203,7 @@ defmodule Matplotex.Figure.Lead do } = axes } = figure ) do + IO.inspect(region_y_height, label: "Region y height for content") %Figure{ figure | axes: %{ @@ -488,8 +497,10 @@ defmodule Matplotex.Figure.Lead do rotation: 0 }, _text - ), - do: font_size * pt_to_inch_ratio + flate + ) do + to_number(font_size) * pt_to_inch_ratio + flate + end + defp height_required_for_text( %Font{ @@ -500,7 +511,7 @@ defmodule Matplotex.Figure.Lead do }, text ) do - text_height = font_size * pt_to_inch_ratio + text_height = to_number(font_size) * pt_to_inch_ratio text_length = tick_length(text) * pt_to_inch_ratio rotation = deg_to_rad(rotation) height_for_rotation = :math.sin(rotation) * text_length @@ -516,7 +527,7 @@ defmodule Matplotex.Figure.Lead do }, text ), - do: tick_length(text) * font_size * pt_to_inch_ratio + flate + do: tick_length(text) * to_number(font_size) * pt_to_inch_ratio + flate defp length_required_for_text( %Font{ @@ -527,11 +538,20 @@ defmodule Matplotex.Figure.Lead do }, text ) do - text_length = tick_length(text) * font_size * pt_to_inch_ratio + text_length = tick_length(text) * to_number(font_size) * pt_to_inch_ratio rotation = deg_to_rad(rotation) leng_for_rotation = :math.cos(rotation) * text_length leng_for_rotation + flate end defp deg_to_rad(deg), do: deg * :math.pi() / 180 + defp to_number(font_size) when is_number(font_size), do: font_size + defp to_number(font_size) when is_binary(font_size) do + font_size = String.trim(font_size, "pt") + if String.contains?(font_size, ".") do + String.to_float(font_size) + else + String.to_integer(font_size) + end + end end diff --git a/lib/matplotex/figure/rc_params.ex b/lib/matplotex/figure/rc_params.ex index 4c72e46..10f8ec7 100644 --- a/lib/matplotex/figure/rc_params.ex +++ b/lib/matplotex/figure/rc_params.ex @@ -128,61 +128,72 @@ defmodule Matplotex.Figure.RcParams do def update_with_font(rc_params, params) do rc_params - |> update_font(params, :x_label_font) - |> update_font(params, :y_label_font) - |> update_font(params, :x_tick_font) - |> update_font(params, :y_tick_font) - |> update_font(params, :title_font) + |> update_font(params, :x_label) + |> update_font(params, :y_label) + |> update_font(params, :x_tick) + |> update_font(params, :y_tick) + |> update_font(params, :title) end defp update_font( - %__MODULE__{x_label_font: x_label_font} = rc_params, - %{x_label_font_size: x_label_font_size}, - :x_label_font + rc_params, + params, + element ) do - %__MODULE__{ - rc_params - | x_label_font: Font.update(x_label_font, %{font_size: x_label_font_size}) - } + element_font_keys = font_associated_keys(element) + element_params = Map.take(params,element_font_keys) + + font = Map.get(rc_params, :"#{element}_font") + updated_font = Font.update(font, element_params, element) + Map.put(rc_params, :"#{element}_font", updated_font) + end + + # defp update_font( + # %__MODULE__{y_label_font: y_label_font} = rc_params, + # %{y_label_font_size: y_label_font_size}, + # :y_label_font + # ) do + # %__MODULE__{ + # rc_params + # | y_label_font: Font.update(y_label_font, %{font_size: y_label_font_size}) + # } + # end + + # defp update_font( + # %__MODULE__{x_tick_font: x_tick_font} = rc_params, + # %{x_tick_font_size: x_tick_font_size}, + # :x_tick_font + # ) do + # %__MODULE__{rc_params | x_tick_font: Font.update(x_tick_font, %{font_size: x_tick_font_size})} + # end + + # defp update_font( + # %__MODULE__{y_tick_font: y_tick_font} = rc_params, + # %{y_tick_font_size: y_tick_font_size}, + # :y_tick_font + # ) do + # %__MODULE__{rc_params | y_tick_font: Font.update(y_tick_font, %{font_size: y_tick_font_size})} + # end + + # defp update_font( + # %__MODULE__{title_font: title_font} = rc_params, + # %{title_font_size: title_font_size}, + # :title_font + # ) do + # %__MODULE__{rc_params | title_font: Font.update(title_font, %{font_size: title_font_size})} + # end + + # defp update_font(rc_params, _, _), do: rc_params + + def font_associated_keys(element) do + %Font{} + |> Map.from_struct() + |> Map.keys() + |> Enum.map(fn fkey -> + :"#{element}_#{fkey}" + end) end - defp update_font( - %__MODULE__{y_label_font: y_label_font} = rc_params, - %{y_label_font_size: y_label_font_size}, - :y_label_font - ) do - %__MODULE__{ - rc_params - | y_label_font: Font.update(y_label_font, %{font_size: y_label_font_size}) - } - end - - defp update_font( - %__MODULE__{x_tick_font: x_tick_font} = rc_params, - %{x_tick_font_size: x_tick_font_size}, - :x_tick_font - ) do - %__MODULE__{rc_params | x_tick_font: Font.update(x_tick_font, %{font_size: x_tick_font_size})} - end - - defp update_font( - %__MODULE__{y_tick_font: y_tick_font} = rc_params, - %{y_tick_font_size: y_tick_font_size}, - :y_tick_font - ) do - %__MODULE__{rc_params | y_tick_font: Font.update(y_tick_font, %{font_size: y_tick_font_size})} - end - - defp update_font( - %__MODULE__{title_font: title_font} = rc_params, - %{title_font_size: title_font_size}, - :title_font - ) do - %__MODULE__{rc_params | title_font: Font.update(title_font, %{font_size: title_font_size})} - end - - defp update_font(rc_params, _, _), do: rc_params - defp convert_font_size(<> <> "pt") do String.to_integer(font_size) end diff --git a/lib/matplotex/figure/sketch.ex b/lib/matplotex/figure/sketch.ex index 98b53a1..4d1bac3 100644 --- a/lib/matplotex/figure/sketch.ex +++ b/lib/matplotex/figure/sketch.ex @@ -13,7 +13,7 @@ defmodule Matplotex.Figure.Sketch do def call(%Figure{axes: %{element: elements}, figsize: {width, height}}) do elements - |> flipy(height) + # |> flipy(height) |> build_elements() |> wrap_with_tag(width * @dpi, height * @dpi) end diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index c587dc0..4030032 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -60,8 +60,8 @@ defmodule Matplotex.Helpers do x = [1, 2, 3, 4, 6, 6, 7] y = [1, 3, 4, 4, 5, 6, 7] - frame_width = 3 - frame_height = 3 + frame_width = 10 + frame_height = 10 size = {frame_width, frame_height} margin = 0.05 font_size = "16pt" diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index ff45192..b5c59e9 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -49,7 +49,7 @@ defmodule Matplotex.Utils.Algebra do def svgfy(y, height), do: height - y - def transform_given_point(x, y,sx, sy,tx, ty, theta) do + def transform_given_point(x, y, sx, sy, tx, ty, theta) do Nx.tensor( [ [sx * :math.cos(theta), sy * -:math.sin(theta), tx], @@ -63,8 +63,22 @@ 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) do + Nx.tensor( + [ + [:math.cos(theta), -:math.sin(theta), ox], + [:math.sin(theta), :math.cos(theta), oy], + [0, 0, 1] + ], + type: {:f, @tensor_data_type_bits} + ) + |> Nx.dot(Nx.tensor([x, y, 1], type: {:f, @tensor_data_type_bits})) + |> Nx.to_flat_list() + |> List.to_tuple() + |> then(fn {x, y, _} -> {x, y} end) + end - def flip_y_coordinate(x, y) do + def flip_y_coordinate({x, y}) do {x, -y} end end diff --git a/test/matplotex/element/label_test.exs b/test/matplotex/element/label_test.exs index a7fcd8a..be13d71 100644 --- a/test/matplotex/element/label_test.exs +++ b/test/matplotex/element/label_test.exs @@ -1,9 +1,9 @@ defmodule Matplotex.Element.LabelTest do - use Matplotex.PlotCase alias Matplotex.Element.Label alias Matplotex.Figure.Font + describe "cast_label/2" do test "merging label with font" do font = %Font{font_size: 22} diff --git a/test/matplotex/figure/cast_test.exs b/test/matplotex/figure/cast_test.exs index 842a495..bd710f7 100644 --- a/test/matplotex/figure/cast_test.exs +++ b/test/matplotex/figure/cast_test.exs @@ -19,6 +19,7 @@ defmodule Matplotex.Figure.CastTest do assert Enum.filter(elements, fn x -> x.type == "spine.left" end) |> length() == 1 end end + describe "cast_spines_by_region/1" do test "add elements for borders in axes", %{figure: figure} do figure = Lead.set_regions(figure) @@ -28,9 +29,9 @@ defmodule Matplotex.Figure.CastTest do assert Enum.any?(elements, fn x -> x.type == "spine.bottom" end) assert Enum.any?(elements, fn x -> x.type == "spine.right" end) assert Enum.any?(elements, fn x -> x.type == "spine.left" end) - end end + describe "cast_title" do test "add element for title in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -42,6 +43,7 @@ defmodule Matplotex.Figure.CastTest do assert Enum.filter(elements, fn x -> x.type == "figure.title" end) |> length == 1 end end + describe "cast_title_by_region/1" do test "add element for title in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -53,6 +55,7 @@ defmodule Matplotex.Figure.CastTest do assert Enum.any?(elements, fn x -> x.type == "figure.title" end) end end + describe "cast_label/1" do test "add element for label in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -64,6 +67,7 @@ defmodule Matplotex.Figure.CastTest do assert Enum.filter(elements, fn x -> x.type == "figure.y_label" end) |> length() == 1 end end + describe "cast_label_by_region/1" do test "add element for label in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -112,6 +116,7 @@ defmodule Matplotex.Figure.CastTest do length(x_ticks) end end + describe "cast_xticks_by_region/1" do test "add element for tick in axes", %{figure: figure} do assert %Figure{axes: %{element: elements, tick: %{x: x_ticks, y: _y_ticks}}} = @@ -123,6 +128,7 @@ defmodule Matplotex.Figure.CastTest do length(x_ticks) end end + describe "cast_yticks/1" do test "add element for tick in axes", %{figure: figure} do assert %Figure{axes: %{element: elements, tick: %{y: y_ticks}}} = @@ -163,6 +169,19 @@ defmodule Matplotex.Figure.CastTest do end end + describe "cast_hgrids_by_region/1" do + test "add elements for horizontal grids", %{figure: figure} do + assert %Figure{axes: %{element: elements, tick: %{y: y_ticks}}} = + figure + |> Lead.set_regions() + |> Cast.cast_yticks_by_region() + |> Cast.cast_hgrids_by_region() + + assert Enum.filter(elements, fn x -> x.type == "figure.h_grid" end) |> length() == + length(y_ticks) + end + end + describe "cast_vgrids/1" do test "add elements for vertical grids", %{figure: figure} do assert %Figure{axes: %{element: elements, tick: %{x: x_ticks}}} = @@ -178,4 +197,17 @@ defmodule Matplotex.Figure.CastTest do length(x_ticks) end end + + describe "cast_vgrids_by_region/1" do + test "add elements for vertical grids", %{figure: figure} do + assert %Figure{axes: %{element: elements, tick: %{x: x_ticks}}} = + figure + |> Lead.set_regions() + |> Cast.cast_xticks_by_region() + |> Cast.cast_vgrids_by_region() + + assert Enum.filter(elements, fn x -> x.type == "figure.v_grid" end) |> length() == + length(x_ticks) + end + end end diff --git a/test/matplotex/figure/rc_param_test.exs b/test/matplotex/figure/rc_param_test.exs index 21890ee..8724dfb 100644 --- a/test/matplotex/figure/rc_param_test.exs +++ b/test/matplotex/figure/rc_param_test.exs @@ -23,14 +23,36 @@ defmodule Matplotex.Figure.RcParamTest do describe "update_with_font/2" do test "updates associated fonts with font_size" do font_size = 10 + font_family = "Updated family" + font_weight = "Updated weight" + font_style = "Updated style" + + pt_to_inch_ratio = 1/60 rc_params = %RcParams{} - font = %Font{font_size: font_size} + + font = %Font{font_size: font_size, font_family: font_family, font_style: font_style, font_weight: font_weight, pt_to_inch_ratio: pt_to_inch_ratio} params = %{ x_label_font_size: font_size, + x_label_font_family: font_family, + x_label_font_style: font_style, + x_label_font_weight: font_weight, y_label_font_size: font_size, + y_label_font_family: font_family, + y_label_font_style: font_style, + y_label_font_weight: font_weight, x_tick_font_size: font_size, - y_tick_font_size: font_size + x_tick_font_family: font_family, + x_tick_font_style: font_style, + x_tick_font_weight: font_weight, + y_tick_font_size: font_size, + y_tick_font_family: font_family, + y_tick_font_style: font_style, + y_tick_font_weight: font_weight, + x_label_pt_to_inch_ratio: pt_to_inch_ratio, + y_label_pt_to_inch_ratio: pt_to_inch_ratio, + x_tick_pt_to_inch_ratio: pt_to_inch_ratio, + y_tick_pt_to_inch_ratio: pt_to_inch_ratio } assert %RcParams{ @@ -41,4 +63,23 @@ defmodule Matplotex.Figure.RcParamTest do } == RcParams.update_with_font(rc_params, params) end end + + test "font_associated_keys will make keys for an elemenent" do + element = :y_label + keys = RcParams.font_associated_keys(element) + + assert keys |> Enum.sort() == + [ + :y_label_font_size, + :y_label_font_family, + :y_label_font_style, + :y_label_font_weight, + :y_label_fill, + :y_label_unit_of_measurement, + :y_label_pt_to_inch_ratio, + :y_label_rotation, + :y_label_flate + ] + |> Enum.sort() + end end From eec33f313976ae9607c4c8481b947cc7e891b173 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Tue, 3 Dec 2024 19:57:43 +0530 Subject: [PATCH 03/14] region bias --- .../figure/areal/xy_region/coords.ex | 1 - lib/matplotex/figure/cast.ex | 33 +++++----- lib/matplotex/figure/font.ex | 1 + lib/matplotex/figure/lead.ex | 64 ++++++++++--------- lib/matplotex/figure/rc_params.ex | 10 +-- lib/matplotex/utils/algebra.ex | 1 + test/matplotex/figure/lead_test.exs | 53 +++++++++++++++ test/matplotex/figure/rc_param_test.exs | 10 ++- 8 files changed, 120 insertions(+), 53 deletions(-) diff --git a/lib/matplotex/figure/areal/xy_region/coords.ex b/lib/matplotex/figure/areal/xy_region/coords.ex index 09e0287..aef9d2b 100644 --- a/lib/matplotex/figure/areal/xy_region/coords.ex +++ b/lib/matplotex/figure/areal/xy_region/coords.ex @@ -1,4 +1,3 @@ defmodule Matplotex.Figure.Areal.XyRegion.Coords do defstruct [:label, :ticks, :grids] - end diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 1b240fe..e96464e 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -79,18 +79,16 @@ defmodule Matplotex.Figure.Cast do width: content_width, height: content_height }, - border: {lx, ly, _, _}, element: elements } = axes } = figure ) do - # Convert to the svg plane - IO.inspect({content_x, content_width, content_y, content_height}, label: "The content dims") - {left_x, bottom_y} = Algebra.transform_given_point(content_x, content_y,lx, ly, 0 ) |>Algebra.flip_y_coordinate() - {right_x, top_y} = Algebra.transform_given_point(content_x + content_width, content_y + content_height, lx, ly, 0)|>Algebra.flip_y_coordinate() - IO.inspect({left_x, bottom_y, right_x, top_y}, label: "The spines coords") + {left_x, bottom_y} = Algebra.flip_y_coordinate({content_x, content_y}) + + {right_x, top_y} = + Algebra.transform_given_point(content_width, content_height, content_x, content_y, 0)|>Algebra.flip_y_coordinate() # Four Line struct representing each corners left = %Line{ @@ -194,8 +192,7 @@ defmodule Matplotex.Figure.Cast do rc_params: %RcParams{title_font: title_font, label_padding: title_padding} } = figure ) do - {title_x, title_y} = calculate_center(region, :x) - + {title_x, title_y} = region|>calculate_center(:x)|>Algebra.flip_y_coordinate() title = %Label{ type: "figure.title", @@ -254,8 +251,8 @@ defmodule Matplotex.Figure.Cast do } = figure ) when not is_nil(x_label) do - {_, x_label_y} = Region.get_label_coords(region_x) - {x_label_x, _} = calculate_center(region_x, :x) + # {_, x_label_y} = Region.get_label_coords(region_x) + {x_label_x, x_label_y} = region_x|>calculate_center(:x)|>Algebra.flip_y_coordinate() x_label = %Label{ @@ -304,10 +301,9 @@ defmodule Matplotex.Figure.Cast do } = figure ) when not is_nil(y_label) do - {y_label_x, _} = Region.get_label_coords(region_y) - {_, y_label_y} = calculate_center(region_y, :y) + {y_label_x, y_label_y} = region_y|>calculate_center(:y)|>Algebra.flip_y_coordinate() y_label = %Label{ type: "figure.y_label", @@ -440,7 +436,9 @@ defmodule Matplotex.Figure.Cast do x_region_content + width_region_content * x_padding, :x ) - x_tick_x = elem(tick_position, 0) + + x_tick_x = elem(tick_position, 0) + label = %Label{ type: @xtick_type, @@ -563,8 +561,11 @@ defmodule Matplotex.Figure.Cast do axes: %module{ tick: %{y: y_ticks}, - region_content: - %Region{x: x_region_content, y: y_region_content, height: height_region_content}, + region_content: %Region{ + x: x_region_content, + y: y_region_content, + height: height_region_content + }, region_y: %Region{coords: coords_region_y} = region_y, element: elements, limit: %{y: {_min, _max} = ylim}, @@ -595,7 +596,9 @@ defmodule Matplotex.Figure.Cast do y_region_content + height_region_content * y_padding, :y ) + y_tick_y = elem(tick_position, 1) + label = %Label{ type: @ytick_type, diff --git a/lib/matplotex/figure/font.ex b/lib/matplotex/figure/font.ex index 8add45a..18f212f 100644 --- a/lib/matplotex/figure/font.ex +++ b/lib/matplotex/figure/font.ex @@ -34,6 +34,7 @@ defmodule Matplotex.Figure.Font do def update(font, params, element) do update_font(font, params, element) end + defp update_font(font, params, element) do font |> Map.from_struct() diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index e148511..3ca9e1b 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -34,16 +34,16 @@ defmodule Matplotex.Figure.Lead do def set_border(%Figure{margin: margin, axes: axes, figsize: {fig_width, fig_height}} = figure) do margin = margin / 2 lx = fig_width * margin - by = fig_height * margin + by = fig_height * margin rx = fig_width - fig_width * margin - ty = fig_height - fig_height * margin + ty = fig_height - fig_height * margin %Figure{figure | axes: %{axes | border: {lx, by, rx, ty}}} end defp set_frame_size(%Figure{margin: margin, figsize: {fwidth, fheight}, axes: axes} = figure) do frame_size = {fwidth - fwidth * 2 * margin, fheight - fheight * 2 * margin} lx = fwidth * margin - ty = -fheight * margin + ty = -fheight * margin rx = fwidth - fwidth * margin by = fheight * margin - fheight %Figure{figure | axes: %{axes | size: frame_size, border: {lx, by, rx, ty}}} @@ -58,7 +58,7 @@ defmodule Matplotex.Figure.Lead do label: %TwoD{y: y_label, x: x_label}, tick: %TwoD{y: y_ticks, x: x_ticks}, size: {f_width, f_height}, - border: {lx, by, _, _} + border: {lx, by, _rx, _ty} } = axes, rc_params: %RcParams{ x_label_font: x_label_font, @@ -75,18 +75,23 @@ defmodule Matplotex.Figure.Lead do y_tick = Enum.max_by(y_ticks, &tick_length(&1)) space_for_yticks = length_required_for_text(y_tick_font, y_tick) - - space_required_for_region_y = + space_required_for_region_y = [space_for_ylabel, space_for_yticks, label_padding, tick_line_length] |> Enum.sum() space_for_x_label = height_required_for_text(x_label_font, x_label) x_tick = Enum.max_by(x_ticks, &tick_length/1) space_for_x_tick = height_required_for_text(x_tick_font, x_tick) - space_required_for_region_x = + space_required_for_region_x = [space_for_x_label, space_for_x_tick, label_padding, tick_line_length] |> Enum.sum() - {x_region_x, y_region_x} = Algebra.transform_given_point(space_required_for_region_y, 0, lx, by, 0) - {x_region_y, y_region_y} = Algebra.transform_given_point(0,space_required_for_region_x, lx,by,0 ) + + {x_region_x, y_region_x} = + Algebra.transform_given_point(space_required_for_region_y, 0, lx, by, 0) + + {x_region_y, y_region_y} = + Algebra.transform_given_point(0, space_required_for_region_x, lx, by, 0) + + %Figure{ figure | axes: %{ @@ -122,15 +127,18 @@ defmodule Matplotex.Figure.Lead do region_y: %Region{width: region_y_width, height: region_y_height} = region_y, region_title: region_title, size: {_f_width, f_height}, - border: {lx, by, _, _} + border: {lx, by, _, ty} } = axes, rc_params: %RcParams{title_font: title_font} } = figure ) do - IO.inspect(region_y_height, label: "Region y height before peeling title space") + space_for_title = height_required_for_text(title_font, title) - IO.inspect(space_for_title, label: "Space for title: " ) - {x_region_title, y_region_title} = Algebra.transform_given_point(region_y_width, space_for_title, lx, by, 0) + + {x_region_title, y_region_title} = + Algebra.transform_given_point(0, -space_for_title, lx, ty, 0) + + %Figure{ figure | axes: %{ @@ -155,19 +163,18 @@ defmodule Matplotex.Figure.Lead do axes: %{ show_legend: true, - region_x: %Region{x: x_region_x, width: region_x_width} = region_x, - region_title: %Region{height: region_title_height} = region_title, + region_x: %Region{ width: region_x_width} = region_x, + region_title: %Region{height: region_title_height}, region_legend: region_legend, - size: {f_width, f_height} + size: {f_width, _f_height}, + border: {_lx, by, rx, ty} } = axes, rc_params: %RcParams{legend_width: legend_width} } = figure ) do region_legend_width = f_width * legend_width region_x_width_after_legend = region_x_width - region_legend_width - legend_region_x = x_region_x + region_x_width_after_legend - legend_region_y = f_height - region_title_height - + {x_region_legend, y_region_legend} = Algebra.transform_given_point(-region_legend_width, -region_title_height, rx, ty, 0) %Figure{ figure | axes: %{ @@ -176,16 +183,12 @@ defmodule Matplotex.Figure.Lead do region_x | width: region_x_width_after_legend }, - region_title: %Region{ - region_title - | width: region_x_width_after_legend - }, region_legend: %Region{ region_legend - | x: legend_region_x, - y: legend_region_y, + | x: x_region_legend, + y: y_region_legend, width: region_legend_width, - height: legend_region_y + height: y_region_legend - by } } } @@ -203,7 +206,7 @@ defmodule Matplotex.Figure.Lead do } = axes } = figure ) do - IO.inspect(region_y_height, label: "Region y height for content") + %Figure{ figure | axes: %{ @@ -498,9 +501,8 @@ defmodule Matplotex.Figure.Lead do }, _text ) do - to_number(font_size) * pt_to_inch_ratio + flate - end - + to_number(font_size) * pt_to_inch_ratio + flate + end defp height_required_for_text( %Font{ @@ -546,8 +548,10 @@ defmodule Matplotex.Figure.Lead do defp deg_to_rad(deg), do: deg * :math.pi() / 180 defp to_number(font_size) when is_number(font_size), do: font_size + defp to_number(font_size) when is_binary(font_size) do font_size = String.trim(font_size, "pt") + if String.contains?(font_size, ".") do String.to_float(font_size) else diff --git a/lib/matplotex/figure/rc_params.ex b/lib/matplotex/figure/rc_params.ex index 10f8ec7..1ba1614 100644 --- a/lib/matplotex/figure/rc_params.ex +++ b/lib/matplotex/figure/rc_params.ex @@ -140,12 +140,12 @@ defmodule Matplotex.Figure.RcParams do params, element ) do - element_font_keys = font_associated_keys(element) - element_params = Map.take(params,element_font_keys) + element_font_keys = font_associated_keys(element) + element_params = Map.take(params, element_font_keys) - font = Map.get(rc_params, :"#{element}_font") - updated_font = Font.update(font, element_params, element) - Map.put(rc_params, :"#{element}_font", updated_font) + font = Map.get(rc_params, :"#{element}_font") + updated_font = Font.update(font, element_params, element) + Map.put(rc_params, :"#{element}_font", updated_font) end # defp update_font( diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index b5c59e9..ef1493f 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -63,6 +63,7 @@ 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) do Nx.tensor( [ diff --git a/test/matplotex/figure/lead_test.exs b/test/matplotex/figure/lead_test.exs index d846a57..4c6bcf1 100644 --- a/test/matplotex/figure/lead_test.exs +++ b/test/matplotex/figure/lead_test.exs @@ -300,5 +300,58 @@ defmodule Matplotex.Figure.LeadTest do assert rcwidth == rxwidth assert ryheight == rcheight end + end + test "all coordinates will be zero if input is zero", %{figure2: figure} do + figure = Matplotex.figure(figure, %{figsize: {0, 0}}) + assert %Figure{ + axes: %{ + region_x: %Region{x: rxx, width: rxwidth}, + region_y: %Region{y: ryy, height: ryheight}, + region_title: %Region{x: rtx, y: rty, width: rtwidth, height: rtheight}, + region_content: %Region{x: rcx, y: rcy, width: rcwidth, height: rcheight} + } + } = Lead.set_regions(figure) + + assert rxx == 0 + assert ryy == 0 + assert rtx == 0 + assert rty == 0 + assert rtwidth == 0 + assert rtheight == 0 + assert rcx == 0 + assert rcy == 0 + assert rcwidth == 0 + assert rcheight == 0 + end + + test "height will tally with all vertical components", %{figure: figure} do + assert %Figure{ + figsize: {_width, height}, + margin: margin, + axes: %{ + region_x: %Region{height: rxheight}, + region_title: %Region{height: rtheight}, + region_content: %Region{height: rcheight} + } + } = Lead.set_regions(figure) + margin_two_side = height * margin * 2 + assert height == margin_two_side + rxheight + rtheight + rcheight + end + + test "width will tally with all horizontal components", %{figure: figure} do + assert %Figure{ + figsize: {width, _height}, + margin: margin, + axes: %{ + region_y: %Region{width: ry_width}, + region_content: %Region{width: rcwidth}, + region_legend: %Region{width: rlwidth} + } + } = Lead.set_regions(figure) + two_side_margin = width * margin * 2 + assert width == two_side_margin + ry_width + rcwidth + rlwidth + end + + end diff --git a/test/matplotex/figure/rc_param_test.exs b/test/matplotex/figure/rc_param_test.exs index 8724dfb..34a307b 100644 --- a/test/matplotex/figure/rc_param_test.exs +++ b/test/matplotex/figure/rc_param_test.exs @@ -27,10 +27,16 @@ defmodule Matplotex.Figure.RcParamTest do font_weight = "Updated weight" font_style = "Updated style" - pt_to_inch_ratio = 1/60 + pt_to_inch_ratio = 1 / 60 rc_params = %RcParams{} - font = %Font{font_size: font_size, font_family: font_family, font_style: font_style, font_weight: font_weight, pt_to_inch_ratio: pt_to_inch_ratio} + font = %Font{ + font_size: font_size, + font_family: font_family, + font_style: font_style, + font_weight: font_weight, + pt_to_inch_ratio: pt_to_inch_ratio + } params = %{ x_label_font_size: font_size, From fea2a430772f516ba6a88368bfd03885eb59333e Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Wed, 4 Dec 2024 19:21:00 +0530 Subject: [PATCH 04/14] x tick and y tick casting --- lib/matplotex/figure/areal.ex | 15 +-- lib/matplotex/figure/cast.ex | 166 ++++++++++++++++------------ lib/matplotex/figure/font.ex | 2 + lib/matplotex/figure/lead.ex | 36 +++--- lib/matplotex/figure/rc_params.ex | 2 +- lib/matplotex/utils/algebra.ex | 2 +- test/matplotex/figure/lead_test.exs | 101 ++++++++--------- 7 files changed, 173 insertions(+), 151 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 0889128..b245aa5 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -61,13 +61,6 @@ defmodule Matplotex.Figure.Areal do end def add_ticks(%__MODULE__{tick: tick} = axes, {key, ticks}) when is_list(ticks) do - ticks = - if number_based?(ticks) do - ticks - else - Enum.with_index(ticks, 1) - end - tick = Map.put(tick, key, ticks) update_tick(axes, tick) end @@ -82,14 +75,14 @@ defmodule Matplotex.Figure.Areal do |> update_tick(tick) end - def hide_v_grid(axes) do - %{axes | show_v_grid: false} - end - def add_ticks(_, _) do raise Matplotex.InputError, keys: [:tick], message: "Invalid Input" end + def hide_v_grid(axes) do + %{axes | show_v_grid: false} + end + def set_limit(%__MODULE__{limit: limit} = axes, {key, {_, _} = lim}) do limit = Map.put(limit, key, lim) update_limit(axes, limit) diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index e96464e..bd14778 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -87,8 +87,10 @@ defmodule Matplotex.Figure.Cast do {left_x, bottom_y} = Algebra.flip_y_coordinate({content_x, content_y}) - {right_x, top_y} = - Algebra.transform_given_point(content_width, content_height, content_x, content_y, 0)|>Algebra.flip_y_coordinate() + {right_x, top_y} = + Algebra.transform_given_point(content_width, content_height, content_x, content_y, 0) + |> Algebra.flip_y_coordinate() + # Four Line struct representing each corners left = %Line{ @@ -192,7 +194,8 @@ defmodule Matplotex.Figure.Cast do rc_params: %RcParams{title_font: title_font, label_padding: title_padding} } = figure ) do - {title_x, title_y} = region|>calculate_center(:x)|>Algebra.flip_y_coordinate() + {title_x, title_y} = region |> calculate_center(:x) |> Algebra.flip_y_coordinate() + title = %Label{ type: "figure.title", @@ -252,7 +255,7 @@ defmodule Matplotex.Figure.Cast do ) when not is_nil(x_label) do # {_, x_label_y} = Region.get_label_coords(region_x) - {x_label_x, x_label_y} = region_x|>calculate_center(:x)|>Algebra.flip_y_coordinate() + {x_label_x, x_label_y} = region_x |> calculate_center(:x) |> Algebra.flip_y_coordinate() x_label = %Label{ @@ -301,9 +304,8 @@ defmodule Matplotex.Figure.Cast do } = figure ) when not is_nil(y_label) do + {y_label_x, y_label_y} = region_y |> calculate_center(:y) |> Algebra.flip_y_coordinate() - - {y_label_x, y_label_y} = region_y|>calculate_center(:y)|>Algebra.flip_y_coordinate() y_label = %Label{ type: "figure.y_label", @@ -401,11 +403,17 @@ defmodule Matplotex.Figure.Cast do def cast_xticks_by_region( %Figure{ axes: - %module{ + %{ tick: %{x: x_ticks}, limit: %{x: {_min, _max} = xlim}, - region_content: %Region{x: x_region_content, width: width_region_content}, - region_x: %Region{coords: coords_region_x} = region_x, + region_x: + %Region{ + x: x_region_x, + y: y_region_x, + height: height_region_x, + width: width_region_x, + coords: %XyCoords{ticks: {_, y_x_tick}} = coords_region_x + } = region_x, data: {x_data, y_data}, dataset: dataset, element: elements, @@ -422,46 +430,54 @@ defmodule Matplotex.Figure.Cast do x_ticks = confine_ticks(x_ticks, xlim) x_data = confine_data(x_data, xlim) dataset = confine_data(dataset, xlim, :x) - - {_, x_tick_y} = Region.get_tick_coords(region_x) - - {xtick_elements, vgridxs} = - Enum.map(x_ticks, fn tick -> - {tick_position, label} = - transform_tick( - module, - tick, - xlim, - width_region_content - width_region_content * x_padding * 2, - x_region_content + width_region_content * x_padding, - :x + x_padding_value = width_region_x * x_padding + shrinked_width_region_x = width_region_x - x_padding_value * 2 + x_region_x_with_padding = x_region_x + x_padding_value + + ticks_width_position = + x_ticks + |> length() + |> content_linespace(shrinked_width_region_x) + |> Enum.zip(x_ticks) + + {x_tick_elements, vgrid_coords} = + Enum.map(ticks_width_position, fn {tick_position, label} -> + {_, y_x_tick_line} = + Algebra.transform_given_point(0, height_region_x, x_region_x, y_region_x) + |> Algebra.flip_y_coordinate() + + {x_x_tick, y_x_tick} = + Algebra.transform_given_point( + tick_position, + 0, + x_region_x_with_padding, + y_x_tick, + 0 ) - - x_tick_x = elem(tick_position, 0) + |> Algebra.flip_y_coordinate() label = %Label{ type: @xtick_type, - x: x_tick_x, - y: x_tick_y, + x: x_x_tick, + y: y_x_tick, text: label } |> Label.cast_label(x_tick_font) line = %Line{ type: @xtick_type, - x1: x_tick_x, - y1: x_tick_y, - x2: x_tick_x, - y2: x_tick_y - tick_line_length + x1: x_x_tick, + y1: y_x_tick_line, + x2: x_x_tick, + y2: y_x_tick_line + tick_line_length } - {%Tick{type: @xtick_type, tick_line: line, label: label}, x_tick_x} + {%Tick{type: @xtick_type, tick_line: line, label: label}, {x_x_tick, y_x_tick_line}} end) |> Enum.unzip() - elements = elements ++ xtick_elements - vgrids = Enum.map(vgridxs, fn g -> {g, x_tick_y} end) + elements = elements ++ x_tick_elements %Figure{ figure @@ -470,11 +486,15 @@ defmodule Matplotex.Figure.Cast do | data: {x_data, y_data}, dataset: dataset, element: elements, - region_x: %Region{region_x | coords: %XyCoords{coords_region_x | grids: vgrids}} + region_x: %Region{region_x | coords: %XyCoords{coords_region_x | grids: vgrid_coords}} } } end + defp content_linespace(number_of_ticks_required, axis_size) do + Nx.linspace(0, axis_size, n: number_of_ticks_required) |> Nx.to_list() + end + @spec cast_yticks(Matplotex.Figure.t()) :: Matplotex.Figure.t() def cast_yticks( %Figure{ @@ -559,14 +579,10 @@ defmodule Matplotex.Figure.Cast do def cast_yticks_by_region( %Figure{ axes: - %module{ + %{ tick: %{y: y_ticks}, - region_content: %Region{ - x: x_region_content, - y: y_region_content, - height: height_region_content - }, - region_y: %Region{coords: coords_region_y} = region_y, + region_y: + %Region{x: x_region_y, y: y_region_y, width: width_region_y, height: height_region_y,coords: %XyCoords{ticks: {x_y_tick, _}} = coords_region_y} = region_y, element: elements, limit: %{y: {_min, _max} = ylim}, data: {x_data, y_data}, @@ -583,47 +599,52 @@ defmodule Matplotex.Figure.Cast do y_ticks = confine_ticks(y_ticks, ylim) y_data = confine_data(y_data, ylim) dataset = confine_data(dataset, ylim, :y) - {y_tick_x, _} = Region.get_tick_coords(region_y) - {ytick_elements, hgridys} = - Enum.map(y_ticks, fn tick -> - {tick_position, label} = - transform_tick( - module, - tick, - ylim, - height_region_content - height_region_content * y_padding * 2, - y_region_content + height_region_content * y_padding, - :y + y_padding_value = height_region_y * y_padding + shrinked_height_region_y = height_region_y - y_padding_value * 2 + y_region_y_with_padding = y_region_y + y_padding_value + + ticks_width_position = + y_ticks + |> length() + |> content_linespace(shrinked_height_region_y) + |> Enum.zip(y_ticks) + + {ytick_elements, hgrid_coords} = + Enum.map(ticks_width_position, fn {tick_position, label} -> + {x_y_tick_line, _} = Algebra.transform_given_point(width_region_y, 0, x_region_y, y_region_y) + + {x_y_tick, y_y_tick} = + Algebra.transform_given_point( + 0, + tick_position, + x_y_tick, + y_region_y_with_padding ) - - y_tick_y = elem(tick_position, 1) + |> Algebra.flip_y_coordinate() label = %Label{ type: @ytick_type, - y: y_tick_y, - x: y_tick_x, - text: label, - text_anchor: "end", - dominant_baseline: "middle" + y: y_y_tick, + x: x_y_tick, + text: label } |> Label.cast_label(y_tick_font) line = %Line{ type: @ytick_type, - y1: y_tick_y, - x1: x_region_content, - x2: x_region_content - tick_line_length, - y2: y_tick_y + y1: y_y_tick, + x1: x_y_tick_line, + x2: x_y_tick_line - tick_line_length, + y2: y_y_tick } - {%Tick{type: @ytick_type, tick_line: line, label: label}, y_tick_y} + {%Tick{type: @ytick_type, tick_line: line, label: label}, {x_y_tick_line, y_y_tick}} end) |> Enum.unzip() elements = elements ++ ytick_elements - hgrids = Enum.map(hgridys, fn g -> {x_region_content, g} end) %Figure{ figure @@ -632,7 +653,7 @@ defmodule Matplotex.Figure.Cast do | data: {x_data, y_data}, dataset: dataset, element: elements, - region_y: %Region{region_y | coords: %XyCoords{coords_region_y | grids: hgrids}} + region_y: %Region{region_y | coords: %XyCoords{coords_region_y | grids: hgrid_coords}} } } end @@ -680,10 +701,10 @@ defmodule Matplotex.Figure.Cast do } = figure ) do hgrid_elements = - Enum.map(hgrids, fn {_x, y} -> + Enum.map(hgrids, fn {x, y} -> %Line{ - x1: x_region_content, - x2: x_region_content + width_region_content, + x1: x, + x2: x + width_region_content, y1: y, y2: y, type: "figure.h_grid", @@ -740,12 +761,12 @@ defmodule Matplotex.Figure.Cast do } = figure ) do vgrid_elements = - Enum.map(vgrids, fn {x, _y} -> + Enum.map(vgrids, fn {x, y} -> %Line{ x1: x, x2: x, - y1: y_region_content, - y2: y_region_content + height_region_content, + y1: y, + y2: y - height_region_content, type: "figure.v_grid", stroke: @stroke_grid, stroke_width: @stroke_width_grid @@ -767,7 +788,6 @@ defmodule Matplotex.Figure.Cast do # x_tick_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/font.ex b/lib/matplotex/figure/font.ex index 18f212f..ec8d779 100644 --- a/lib/matplotex/figure/font.ex +++ b/lib/matplotex/figure/font.ex @@ -8,6 +8,7 @@ defmodule Matplotex.Figure.Font do @pt_to_inch_ratio 1 / 72 @text_rotation 0 @flate 0 + @dominant_baseline "middle" defstruct font_size: @default_font_size, font_style: @default_font_style, @@ -17,6 +18,7 @@ defmodule Matplotex.Figure.Font do unit_of_measurement: @font_unit, pt_to_inch_ratio: @pt_to_inch_ratio, rotation: @text_rotation, + dominant_baseline: @dominant_baseline, flate: @flate def font_keys() do diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index 3ca9e1b..3041fbc 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -12,6 +12,8 @@ defmodule Matplotex.Figure.Lead do @padding 10 / 96 @tick_line_offset 5 / 96 + @theta 0 + def set_spines(%Figure{} = figure) do figure |> set_xlabel_coords() @@ -76,21 +78,25 @@ defmodule Matplotex.Figure.Lead do space_for_yticks = length_required_for_text(y_tick_font, y_tick) space_required_for_region_y = - [space_for_ylabel, space_for_yticks, label_padding, tick_line_length] |> Enum.sum() + [space_for_ylabel, space_for_yticks, tick_line_length] |> Enum.sum() space_for_x_label = height_required_for_text(x_label_font, x_label) x_tick = Enum.max_by(x_ticks, &tick_length/1) space_for_x_tick = height_required_for_text(x_tick_font, x_tick) space_required_for_region_x = - [space_for_x_label, space_for_x_tick, label_padding, tick_line_length] |> Enum.sum() + [space_for_x_label, space_for_x_tick, tick_line_length] |> Enum.sum() {x_region_x, y_region_x} = - Algebra.transform_given_point(space_required_for_region_y, 0, lx, by, 0) + Algebra.transform_given_point(space_required_for_region_y, 0, lx, by) {x_region_y, y_region_y} = - Algebra.transform_given_point(0, space_required_for_region_x, lx, by, 0) + Algebra.transform_given_point(0, space_required_for_region_x, lx, by) + x_label_coords = Algebra.transform_given_point(0, 0, x_region_x, y_region_x) + x_tick_coords = Algebra.transform_given_point(0, space_for_x_label, x_region_x, y_region_x) + y_label_coords = Algebra.transform_given_point(0, 0, x_region_y, y_region_y) + y_tick_coords = Algebra.transform_given_point(space_for_ylabel, 0, x_region_y, y_region_y) %Figure{ figure @@ -102,7 +108,7 @@ defmodule Matplotex.Figure.Lead do y: y_region_x, height: space_required_for_region_x, width: f_width - space_required_for_region_y, - coords: %XyCoords{label: {0, 0}, ticks: {0, space_for_x_label}} + coords: %XyCoords{label: x_label_coords, ticks: x_tick_coords} }, region_y: %Region{ region_y @@ -110,7 +116,7 @@ defmodule Matplotex.Figure.Lead do y: y_region_y, width: space_required_for_region_y, height: f_height - space_required_for_region_x, - coords: %XyCoords{label: {0, 0}, ticks: {space_for_ylabel, 0}} + coords: %XyCoords{label: y_label_coords, ticks: y_tick_coords} } } } @@ -124,21 +130,19 @@ defmodule Matplotex.Figure.Lead do %{ title: title, region_x: %Region{width: region_x_width}, - region_y: %Region{width: region_y_width, height: region_y_height} = region_y, + region_y: %Region{height: region_y_height} = region_y, region_title: region_title, - size: {_f_width, f_height}, - border: {lx, by, _, ty} + size: {_f_width, _f_height}, + border: {lx, _by, _, ty} } = axes, rc_params: %RcParams{title_font: title_font} } = figure ) do - space_for_title = height_required_for_text(title_font, title) {x_region_title, y_region_title} = Algebra.transform_given_point(0, -space_for_title, lx, ty, 0) - %Figure{ figure | axes: %{ @@ -146,7 +150,7 @@ defmodule Matplotex.Figure.Lead do | region_title: %Region{ region_title | x: x_region_title, - y: y_region_title, + y: y_region_title + space_for_title, width: region_x_width, height: space_for_title }, @@ -163,7 +167,7 @@ defmodule Matplotex.Figure.Lead do axes: %{ show_legend: true, - region_x: %Region{ width: region_x_width} = region_x, + region_x: %Region{width: region_x_width} = region_x, region_title: %Region{height: region_title_height}, region_legend: region_legend, size: {f_width, _f_height}, @@ -174,7 +178,10 @@ defmodule Matplotex.Figure.Lead 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) + + {x_region_legend, y_region_legend} = + Algebra.transform_given_point(-region_legend_width, -region_title_height, rx, ty, 0) + %Figure{ figure | axes: %{ @@ -206,7 +213,6 @@ defmodule Matplotex.Figure.Lead do } = axes } = figure ) do - %Figure{ figure | axes: %{ diff --git a/lib/matplotex/figure/rc_params.ex b/lib/matplotex/figure/rc_params.ex index 1ba1614..1a4deec 100644 --- a/lib/matplotex/figure/rc_params.ex +++ b/lib/matplotex/figure/rc_params.ex @@ -19,7 +19,7 @@ defmodule Matplotex.Figure.RcParams do @label_padding 5 / 96 @default_legend_width_percentage 0.2 @default_legend_items_orientation :horizontal - defstruct x_tick_font: @font, + defstruct x_tick_font: %Font{dominant_baseline: "middle"}, y_tick_font: @font, x_label_font: @font, y_label_font: @font, diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index ef1493f..c32f764 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -64,7 +64,7 @@ defmodule Matplotex.Utils.Algebra do |> then(fn {x, y, _} -> {x, y} end) end - def transform_given_point(x, y, ox, oy, theta) do + def transform_given_point(x, y, ox, oy, theta \\ 0) do Nx.tensor( [ [:math.cos(theta), -:math.sin(theta), ox], diff --git a/test/matplotex/figure/lead_test.exs b/test/matplotex/figure/lead_test.exs index 4c6bcf1..49cb49d 100644 --- a/test/matplotex/figure/lead_test.exs +++ b/test/matplotex/figure/lead_test.exs @@ -300,58 +300,59 @@ defmodule Matplotex.Figure.LeadTest do assert rcwidth == rxwidth assert ryheight == rcheight end - end - test "all coordinates will be zero if input is zero", %{figure2: figure} do - figure = Matplotex.figure(figure, %{figsize: {0, 0}}) - assert %Figure{ - axes: %{ - region_x: %Region{x: rxx, width: rxwidth}, - region_y: %Region{y: ryy, height: ryheight}, - region_title: %Region{x: rtx, y: rty, width: rtwidth, height: rtheight}, - region_content: %Region{x: rcx, y: rcy, width: rcwidth, height: rcheight} - } - } = Lead.set_regions(figure) - - assert rxx == 0 - assert ryy == 0 - assert rtx == 0 - assert rty == 0 - assert rtwidth == 0 - assert rtheight == 0 - assert rcx == 0 - assert rcy == 0 - assert rcwidth == 0 - assert rcheight == 0 - end - - test "height will tally with all vertical components", %{figure: figure} do - assert %Figure{ - figsize: {_width, height}, - margin: margin, - axes: %{ - region_x: %Region{height: rxheight}, - region_title: %Region{height: rtheight}, - region_content: %Region{height: rcheight} - } - } = Lead.set_regions(figure) - margin_two_side = height * margin * 2 - assert height == margin_two_side + rxheight + rtheight + rcheight - end - test "width will tally with all horizontal components", %{figure: figure} do - assert %Figure{ - figsize: {width, _height}, - margin: margin, - axes: %{ - region_y: %Region{width: ry_width}, - region_content: %Region{width: rcwidth}, - region_legend: %Region{width: rlwidth} - } - } = Lead.set_regions(figure) - two_side_margin = width * margin * 2 - assert width == two_side_margin + ry_width + rcwidth + rlwidth - end + test "all coordinates will be zero if input is zero", %{figure2: figure} do + figure = Matplotex.figure(figure, %{figsize: {0, 0}}) + + assert %Figure{ + axes: %{ + region_x: %Region{x: rxx, width: rxwidth}, + region_y: %Region{y: ryy, height: ryheight}, + region_title: %Region{x: rtx, y: rty, width: rtwidth, height: rtheight}, + region_content: %Region{x: rcx, y: rcy, width: rcwidth, height: rcheight} + } + } = Lead.set_regions(figure) + + assert rxx == 0 + assert ryy == 0 + assert rtx == 0 + assert rty == 0 + assert rtwidth == 0 + assert rtheight == 0 + assert rcx == 0 + assert rcy == 0 + assert rcwidth == 0 + assert rcheight == 0 + end + test "height will tally with all vertical components", %{figure: figure} do + assert %Figure{ + figsize: {_width, height}, + margin: margin, + axes: %{ + region_x: %Region{height: rxheight}, + region_title: %Region{height: rtheight}, + region_content: %Region{height: rcheight} + } + } = Lead.set_regions(figure) + + margin_two_side = height * margin * 2 + assert height == margin_two_side + rxheight + rtheight + rcheight + end + test "width will tally with all horizontal components", %{figure: figure} do + assert %Figure{ + figsize: {width, _height}, + margin: margin, + axes: %{ + region_y: %Region{width: ry_width}, + region_content: %Region{width: rcwidth}, + region_legend: %Region{width: rlwidth} + } + } = Lead.set_regions(figure) + + two_side_margin = width * margin * 2 + assert width == two_side_margin + ry_width + rcwidth + rlwidth + end end From 43d90ba2f8605049883aa71a06cb8d168a14d653 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 5 Dec 2024 10:48:56 +0530 Subject: [PATCH 05/14] lineplot by region based transformation --- lib/matplotex/figure/areal.ex | 23 +++++----------------- lib/matplotex/figure/areal/line_plot.ex | 15 +++++++------- lib/matplotex/figure/cast.ex | 26 ++++--------------------- lib/matplotex/helpers.ex | 4 ++-- lib/matplotex/utils/algebra.ex | 2 +- 5 files changed, 20 insertions(+), 50 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index b245aa5..2922065 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -1,4 +1,5 @@ defmodule Matplotex.Figure.Areal do + alias Matplotex.Utils.Algebra alias Matplotex.Figure.Dataset alias Matplotex.Figure.TwoD @callback create(struct(), any(), keyword()) :: struct() @@ -215,9 +216,6 @@ defmodule Matplotex.Figure.Areal do end end - @tensor_data_type_bits 64 - - alias Nx def transformation({_label, value}, y, xminmax, yminmax, width, height, transition) do transformation(value, y, xminmax, yminmax, width, height, transition) @@ -242,20 +240,7 @@ defmodule Matplotex.Figure.Areal do tx = transition_x - xmin * sx ty = transition_y - ymin * sy - # TODO: work for the datasets which has values in a range way far from zero in both directi - point_matrix = Nx.tensor([x, y, 1], type: {:f, @tensor_data_type_bits}) - - Nx.tensor( - [ - [sx, 0, tx], - [0, sy, ty], - [0, 0, 1] - ], - type: {:f, @tensor_data_type_bits} - ) - |> Nx.dot(point_matrix) - |> Nx.to_flat_list() - |> then(fn [x_trans, y_trans, _] -> {x_trans, y_trans} end) + Algebra.transform_given_point(x, y, sx, sy, tx, ty) end def do_transform(%Dataset{x: x, y: y} = dataset, xlim, ylim, width, height, transition) do @@ -263,7 +248,9 @@ defmodule Matplotex.Figure.Areal do x |> Enum.zip(y) |> Enum.map(fn {x, y} -> - transformation(x, y, xlim, ylim, width, height, transition) + x + |>transformation(y, xlim, ylim, width, height, transition) + |>Algebra.flip_y_coordinate() end) %Dataset{dataset | transformed: transformed} diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index fa2a516..6b44985 100644 --- a/lib/matplotex/figure/areal/line_plot.ex +++ b/lib/matplotex/figure/areal/line_plot.ex @@ -54,22 +54,23 @@ defmodule Matplotex.Figure.Areal.LinePlot do %{ dataset: data, limit: %{x: xlim, y: ylim}, - region_content: %Region{x: blx, y: bly, width: width, height: height}, + region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content, height: height_region_content}, element: elements } = axes, rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} } = figure ) do - px = width * x_padding - py = height * y_padding - width = width - px * 2 - height = height - py * 2 - + # {x_region_content, y_region_content} = Algebra.flip_y_coordinate({x_region_content, y_region_content}) + x_padding_value = width_region_content * x_padding + y_padding_value = height_region_content * y_padding + shrinked_width_region_content = width_region_content - x_padding_value * 2 + shrinked_height_region_content = height_region_content - y_padding_value * 2 + IO.inspect({shrinked_height_region_content, shrinked_height_region_content, y_region_content}, label: "Shrinked size") line_elements = data |> Enum.map(fn dataset -> dataset - |> do_transform(xlim, ylim, width, height, {blx + px, bly + py}) + |> do_transform(xlim, ylim, shrinked_width_region_content, shrinked_height_region_content, {x_region_content+x_padding_value, y_region_content+y_padding_value}) |> capture() end) |> List.flatten() diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index bd14778..8317a89 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -695,7 +695,7 @@ defmodule Matplotex.Figure.Cast do %{ show_h_grid: true, region_y: %Region{coords: %XyCoords{grids: hgrids}}, - region_content: %Region{x: x_region_content, width: width_region_content}, + region_x: %Region{width: width_region_x}, element: elements } = axes } = figure @@ -704,7 +704,7 @@ defmodule Matplotex.Figure.Cast do Enum.map(hgrids, fn {x, y} -> %Line{ x1: x, - x2: x + width_region_content, + x2: x + width_region_x, y1: y, y2: y, type: "figure.h_grid", @@ -755,7 +755,7 @@ defmodule Matplotex.Figure.Cast do %{ show_v_grid: true, region_x: %Region{coords: %XyCoords{grids: vgrids}}, - region_content: %Region{y: y_region_content, height: height_region_content}, + region_y: %Region{height: height_region_y}, element: elements } = axes } = figure @@ -766,7 +766,7 @@ defmodule Matplotex.Figure.Cast do x1: x, x2: x, y1: y, - y2: y - height_region_content, + y2: y - height_region_y, type: "figure.v_grid", stroke: @stroke_grid, stroke_width: @stroke_width_grid @@ -777,17 +777,6 @@ defmodule Matplotex.Figure.Cast do %Figure{figure | axes: %{axes | element: elements}} end - - # def cast_region_x( - # %Figure{axes: %{ label: %TwoD{x: x_label},tick: %TwoD{x: x_ticks}, - # region_x: %Region{x: x_region_x, y: x_region_y, width: width, height: height, - # coords: %XyCoords{label: {x_label_x, x_label_y}, ticks: x_tick_coords}}, - # rc_params: %RcParams{x_label_font: x_label_font}} = axes} = figure) do - - # xlabel_element = %Label{x: x_label_x, y: x_label_y, text: x_label}|>Label.cast_label(x_label_font) - # x_tick_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 @@ -796,13 +785,6 @@ defmodule Matplotex.Figure.Cast do {module.plotify(value, lim, axis_size, transition, data, axis), value} end - defp transform_tick(module, {label, value}, lim, axis_size, transition, axis) do - {module.tick_homogeneous_transformation(value, lim, axis_size, transition, axis), label} - end - - defp transform_tick(module, value, lim, axis_size, transition, axis) do - {module.tick_homogeneous_transformation(value, lim, axis_size, transition, axis), value} - end defp min_max([{_pos, _label} | _] = ticks) do ticks diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index 4030032..d82a3a5 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -166,8 +166,8 @@ defmodule Matplotex.Helpers do x = [1, 2, 3, 4, 6, 6, 7] y = [1, 3, 4, 4, 5, 6, 7] - frame_width = 3 - frame_height = 3 + frame_width = 10 + frame_height = 10 size = {frame_width, frame_height} margin = 0.05 font_size = "16pt" diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index c32f764..ef1f695 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -49,7 +49,7 @@ defmodule Matplotex.Utils.Algebra do def svgfy(y, height), do: height - y - def transform_given_point(x, y, sx, sy, tx, ty, theta) do + def transform_given_point(x, y, sx, sy, tx, ty, theta\\0) do Nx.tensor( [ [sx * :math.cos(theta), sy * -:math.sin(theta), tx], From b0911a862a9f2fc87129f18a62df1d20f22ed0e9 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 5 Dec 2024 11:01:30 +0530 Subject: [PATCH 06/14] scatter plot by region based transformation --- lib/matplotex/figure/areal.ex | 5 ++--- lib/matplotex/figure/areal/line_plot.ex | 21 +++++++++++++++++--- lib/matplotex/figure/areal/scatter.ex | 26 +++++++++++++++++-------- lib/matplotex/figure/cast.ex | 13 ++++++++++--- lib/matplotex/utils/algebra.ex | 2 +- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 2922065..757324d 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -216,7 +216,6 @@ defmodule Matplotex.Figure.Areal do end end - def transformation({_label, value}, y, xminmax, yminmax, width, height, transition) do transformation(value, y, xminmax, yminmax, width, height, transition) end @@ -249,8 +248,8 @@ defmodule Matplotex.Figure.Areal do |> Enum.zip(y) |> Enum.map(fn {x, y} -> x - |>transformation(y, xlim, ylim, width, height, transition) - |>Algebra.flip_y_coordinate() + |> transformation(y, xlim, ylim, width, height, transition) + |> Algebra.flip_y_coordinate() end) %Dataset{dataset | transformed: transformed} diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index 6b44985..ce7f248 100644 --- a/lib/matplotex/figure/areal/line_plot.ex +++ b/lib/matplotex/figure/areal/line_plot.ex @@ -54,7 +54,12 @@ defmodule Matplotex.Figure.Areal.LinePlot do %{ dataset: data, limit: %{x: xlim, y: ylim}, - region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content, height: height_region_content}, + region_content: %Region{ + x: x_region_content, + y: y_region_content, + width: width_region_content, + height: height_region_content + }, element: elements } = axes, rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} @@ -65,12 +70,22 @@ defmodule Matplotex.Figure.Areal.LinePlot do y_padding_value = height_region_content * y_padding shrinked_width_region_content = width_region_content - x_padding_value * 2 shrinked_height_region_content = height_region_content - y_padding_value * 2 - IO.inspect({shrinked_height_region_content, shrinked_height_region_content, y_region_content}, label: "Shrinked size") + + IO.inspect({shrinked_height_region_content, shrinked_height_region_content, y_region_content}, + label: "Shrinked size" + ) + line_elements = data |> Enum.map(fn dataset -> dataset - |> do_transform(xlim, ylim, shrinked_width_region_content, shrinked_height_region_content, {x_region_content+x_padding_value, y_region_content+y_padding_value}) + |> do_transform( + xlim, + ylim, + shrinked_width_region_content, + shrinked_height_region_content, + {x_region_content + x_padding_value, y_region_content + y_padding_value} + ) |> capture() end) |> List.flatten() diff --git a/lib/matplotex/figure/areal/scatter.ex b/lib/matplotex/figure/areal/scatter.ex index 3bddbfd..adf2071 100644 --- a/lib/matplotex/figure/areal/scatter.ex +++ b/lib/matplotex/figure/areal/scatter.ex @@ -38,7 +38,7 @@ defmodule Matplotex.Figure.Areal.Scatter do @impl Areal def materialize(figure) do - __MODULE__.materialized(figure) + __MODULE__.materialized_by_region(figure) |> materialize_elements() end @@ -48,23 +48,33 @@ defmodule Matplotex.Figure.Areal.Scatter do %{ dataset: data, limit: %{x: xlim, y: ylim}, - size: {width, height}, - coords: %Coords{bottom_left: {blx, bly}}, + region_content: %Region{ + x: x_region_content, + y: y_region_content, + width: width_region_content, + height: height_region_content + }, element: elements } = axes, rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} } = figure ) do - px = width * x_padding - py = height * y_padding - width = width - px * 2 - height = height - py * 2 + x_padding_value = width_region_content * x_padding + y_padding_value = height_region_content * y_padding + shrinked_width_region_content = width_region_content - x_padding_value * 2 + shrinked_height_region_content = height_region_content - y_padding_value * 2 line_elements = data |> Enum.map(fn dataset -> dataset - |> do_transform(xlim, ylim, width, height, {blx + px, bly + py}) + |> do_transform( + xlim, + ylim, + shrinked_width_region_content, + shrinked_height_region_content, + {x_region_content + x_padding_value, y_region_content + y_padding_value} + ) |> capture() end) |> List.flatten() diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 8317a89..08573b6 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -582,7 +582,13 @@ defmodule Matplotex.Figure.Cast do %{ tick: %{y: y_ticks}, region_y: - %Region{x: x_region_y, y: y_region_y, width: width_region_y, height: height_region_y,coords: %XyCoords{ticks: {x_y_tick, _}} = coords_region_y} = region_y, + %Region{ + x: x_region_y, + y: y_region_y, + width: width_region_y, + height: height_region_y, + coords: %XyCoords{ticks: {x_y_tick, _}} = coords_region_y + } = region_y, element: elements, limit: %{y: {_min, _max} = ylim}, data: {x_data, y_data}, @@ -612,7 +618,8 @@ defmodule Matplotex.Figure.Cast do {ytick_elements, hgrid_coords} = Enum.map(ticks_width_position, fn {tick_position, label} -> - {x_y_tick_line, _} = Algebra.transform_given_point(width_region_y, 0, x_region_y, y_region_y) + {x_y_tick_line, _} = + Algebra.transform_given_point(width_region_y, 0, x_region_y, y_region_y) {x_y_tick, y_y_tick} = Algebra.transform_given_point( @@ -777,6 +784,7 @@ defmodule Matplotex.Figure.Cast do %Figure{figure | axes: %{axes | element: 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 @@ -785,7 +793,6 @@ defmodule Matplotex.Figure.Cast do {module.plotify(value, lim, axis_size, transition, data, axis), value} end - defp min_max([{_pos, _label} | _] = ticks) do ticks |> Enum.min_max_by(fn {_labe, pos} -> pos end) diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index ef1f695..6d9af55 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -49,7 +49,7 @@ defmodule Matplotex.Utils.Algebra do def svgfy(y, height), do: height - y - def transform_given_point(x, y, sx, sy, tx, ty, theta\\0) do + def transform_given_point(x, y, sx, sy, tx, ty, theta \\ 0) do Nx.tensor( [ [sx * :math.cos(theta), sy * -:math.sin(theta), tx], From e2158bf5992144af2eb0941a7342b9a97cd0f9be Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Tue, 10 Dec 2024 16:01:26 +0530 Subject: [PATCH 07/14] on no ticks --- lib/matplotex/figure/areal.ex | 13 +++++-- lib/matplotex/figure/areal/bar_chart.ex | 20 +++++------ lib/matplotex/figure/areal/line_plot.ex | 30 ---------------- lib/matplotex/figure/areal/region.ex | 2 +- lib/matplotex/figure/cast.ex | 40 ++++++++++++--------- lib/matplotex/figure/lead.ex | 48 ++++++++++++++++++++++--- lib/matplotex/helpers.ex | 13 +++---- test/matplotex/figure/lead_test.exs | 30 +++++++++++++--- test/matplotex/figure/rc_param_test.exs | 1 + 9 files changed, 121 insertions(+), 76 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 757324d..5dab8ee 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -62,8 +62,17 @@ defmodule Matplotex.Figure.Areal do end def add_ticks(%__MODULE__{tick: tick} = axes, {key, ticks}) when is_list(ticks) do + + {ticks, lim} = if number_based?(ticks) do + {ticks, Enum.min_max(ticks)} + else + {Enum.with_index(ticks) , {0, length(ticks)}} + end tick = Map.put(tick, key, ticks) - update_tick(axes, tick) + + axes + |>set_limit({key,lim}) + |>update_tick(tick) end def add_ticks(%__MODULE__{tick: tick, size: size} = axes, {key, {_min, _max} = lim}) do @@ -72,7 +81,7 @@ defmodule Matplotex.Figure.Areal do tick = Map.put(tick, key, ticks) axes - |> set_limit(lim) + |> set_limit({key, lim}) |> update_tick(tick) end diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index 753684c..df3b911 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -38,7 +38,7 @@ defmodule Matplotex.Figure.Areal.BarChart do @impl Areal def materialize(figure) do - __MODULE__.materialized(figure) + __MODULE__.materialized_by_region(figure) |> materialize_bars() end @@ -48,24 +48,22 @@ defmodule Matplotex.Figure.Areal.BarChart do %{ dataset: data, limit: %{x: xlim, y: ylim}, - size: {width, height}, - coords: %Coords{bottom_left: {blx, bly}}, + region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content, height: height_region_content}, element: elements } = axes, - rc_params: %RcParams{x_padding: padding} + rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} } = figure ) do - px = width * padding - width = width - px * 2 - py = height * padding - height = height - py * 2 - + x_padding_value = width_region_content * x_padding + y_padding_value = height_region_content * y_padding + shrinked_width_region_content = width_region_content - x_padding_value * 2 + shrinked_height_region_content = height_region_content - y_padding_value * 2 bar_elements = data |> Enum.map(fn dataset -> dataset - |> do_transform(xlim, ylim, width, height, {blx + px, bly + py}) - |> capture(bly) + |> do_transform(xlim, ylim, shrinked_width_region_content, shrinked_height_region_content, {x_region_content+x_padding_value, y_region_content + y_padding_value}) + |> capture(y_region_content) end) |> List.flatten() diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index ce7f248..595bba1 100644 --- a/lib/matplotex/figure/areal/line_plot.ex +++ b/lib/matplotex/figure/areal/line_plot.ex @@ -1,5 +1,4 @@ defmodule Matplotex.Figure.Areal.LinePlot do - alias Matplotex.Utils.Algebra alias Matplotex.Figure.Areal.Region alias Matplotex.Figure.Areal.Ticker alias Matplotex.Figure.Marker @@ -71,10 +70,6 @@ defmodule Matplotex.Figure.Areal.LinePlot do shrinked_width_region_content = width_region_content - x_padding_value * 2 shrinked_height_region_content = height_region_content - y_padding_value * 2 - IO.inspect({shrinked_height_region_content, shrinked_height_region_content, y_region_content}, - label: "Shrinked size" - ) - line_elements = data |> Enum.map(fn dataset -> @@ -100,31 +95,6 @@ defmodule Matplotex.Figure.Areal.LinePlot do value * s + transition - minl * s end - def tick_homogeneous_transformation(value, {x_min, x_max}, axis_size, transition, :x) do - sx = axis_size / (x_max - x_min) - # no need y scale - sy = 0 - x = value - # Ignoring y here - y = 0 - tx = transition - ty = 0 - # not applying rotation - theta = 0 - Algebra.transform_given_point(x, y, sx, sy, tx, ty, theta) - end - - def tick_homogeneous_transformation(value, {y_min, y_max}, axis_size, transition, :y) do - sx = 0 - sy = axis_size / (y_max - y_min) - x = 0 - y = value - tx = 0 - ty = transition - # not applying rotation - theta = 0 - Algebra.transform_given_point(x, y, sx, sy, tx, ty, theta) - end def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} diff --git a/lib/matplotex/figure/areal/region.ex b/lib/matplotex/figure/areal/region.ex index 52f63a6..e5e7791 100644 --- a/lib/matplotex/figure/areal/region.ex +++ b/lib/matplotex/figure/areal/region.ex @@ -1,6 +1,6 @@ defmodule Matplotex.Figure.Areal.Region do alias Matplotex.Figure.Areal.XyRegion.Coords - defstruct [:x, :y, :width, :height, :name, :theta, :coords] + defstruct [x: 0, y: 0, width: 0, height: 0, name: nil, theta: 0, coords: nil] def get_label_coords(%__MODULE__{x: x, y: y, coords: %Coords{label: {label_x, label_y}}}) do {x + label_x, y + label_y} diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 08573b6..be37647 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -433,7 +433,6 @@ defmodule Matplotex.Figure.Cast do x_padding_value = width_region_x * x_padding shrinked_width_region_x = width_region_x - x_padding_value * 2 x_region_x_with_padding = x_region_x + x_padding_value - ticks_width_position = x_ticks |> length() @@ -491,6 +490,8 @@ defmodule Matplotex.Figure.Cast do } end + def cast_xticks_by_region(figure), do: figure + defp content_linespace(number_of_ticks_required, axis_size) do Nx.linspace(0, axis_size, n: number_of_ticks_required) |> Nx.to_list() end @@ -665,6 +666,8 @@ defmodule Matplotex.Figure.Cast do } end + def cast_yticks_by_region(figure), do: figure + def cast_hgrids(%Figure{axes: %{coords: %{hgrids: nil}}} = figure), do: figure def cast_hgrids(%Figure{axes: %{show_h_grid: false}} = figure), do: figure @@ -725,6 +728,8 @@ defmodule Matplotex.Figure.Cast do %Figure{figure | axes: %{axes | element: elements}} end + def cast_hgrids_by_region(figure), do: figure + def cast_vgrids(%Figure{axes: %{coords: %{vgrids: nil}}} = figure), do: figure def cast_vgrids(%Figure{axes: %{show_v_grid: false}} = figure), do: figure @@ -785,6 +790,8 @@ defmodule Matplotex.Figure.Cast do %Figure{figure | axes: %{axes | element: elements}} end + def cast_vgrids_by_region(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 @@ -857,9 +864,8 @@ defmodule Matplotex.Figure.Cast do end) end - defp confine_ticks(ticks, {min, max} = lim) do + defp confine_ticks(ticks, {min, max}) do ticks - |> append_lim(lim) |> Enum.filter(fn tick -> tick >= min && tick <= max end) @@ -883,18 +889,18 @@ defmodule Matplotex.Figure.Cast do end) end - defp append_lim([first | [second | _]] = ticks, {min, max}) do - with_min = - if Enum.min(ticks) > min + (second - first) do - [min] ++ ticks - else - ticks - end - - if Enum.max(with_min) < max - (second - first) do - with_min ++ [max] - else - with_min - end - end + # defp append_lim([first | [second | _]] = ticks, {min, max}) do + # with_min = + # if Enum.min(ticks) > min + (second - first) do + # [min] ++ ticks + # else + # ticks + # end + + # if Enum.max(with_min) < max - (second - first) do + # with_min ++ [max] + # else + # with_min + # end + # end end diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index 3041fbc..a98acc2 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -12,8 +12,6 @@ defmodule Matplotex.Figure.Lead do @padding 10 / 96 @tick_line_offset 5 / 96 - @theta 0 - def set_spines(%Figure{} = figure) do figure |> set_xlabel_coords() @@ -23,16 +21,20 @@ defmodule Matplotex.Figure.Lead do |> set_yticks_coords() |> set_border_coords() end + @spec set_regions(Matplotex.Figure.t()) :: Matplotex.Figure.t() - def set_regions(%Figure{} = figure) do + def set_regions(%Figure{figsize: {width, height}} = figure) when width > 0 and height > 0 do figure |> set_frame_size() + |> ensure_ticks_are_valid() |> set_region_xy() |> set_region_title() |> set_region_legend() |> set_region_content() end + def set_regions(figure), do: figure + def set_border(%Figure{margin: margin, axes: axes, figsize: {fig_width, fig_height}} = figure) do margin = margin / 2 lx = fig_width * margin @@ -51,6 +53,44 @@ defmodule Matplotex.Figure.Lead do %Figure{figure | axes: %{axes | size: frame_size, border: {lx, by, rx, ty}}} end + defp ensure_ticks_are_valid( + %Figure{ + figsize: {width, height}, + axes: + %{ + data: {x_data, y_data}, + tick: %TwoD{x: x_ticks, y: y_ticks} = ticks,limit: %TwoD{x: x_lim, y: y_lim} = limit}= axes} = figure) do + + {x_ticks, x_lim} = maybe_generate_ticks(x_ticks, x_lim, x_data, width) + {y_ticks, y_lim} = maybe_generate_ticks(y_ticks, y_lim, y_data, height) + + %Figure{figure | axes: %{axes | tick: %TwoD{ticks | x: x_ticks, y: y_ticks},limit: %TwoD{limit | x: x_lim, y: y_lim}}} + end + defp maybe_generate_ticks(ticks, limit, data, number_of_ticks) do + if is_nil(ticks) || length(ticks) < 3 do + generate_ticks(limit, data, ceil(number_of_ticks)) + else + {ticks, limit} + end + end + + defp generate_ticks(nil, data, number_of_ticks) do + {min, upper_limit} = Enum.min_max(data) + + lower_limit = if min < 0 do + min + else + 0 + end + + generate_ticks({lower_limit, upper_limit}, data, number_of_ticks) + end + defp generate_ticks({lower_limit, upper_limit} = lim, _data, number_of_ticks) do + {lower_limit|> Nx.linspace(upper_limit, n: number_of_ticks)|> Nx.to_list(), lim} + end + + + defp set_region_xy( %Figure{ axes: @@ -67,12 +107,10 @@ defmodule Matplotex.Figure.Lead do x_tick_font: x_tick_font, y_label_font: y_label_font, y_tick_font: y_tick_font, - label_padding: label_padding, tick_line_length: tick_line_length } } = figure ) do - # region_x = %Region{x: total space required for ylabel plus yticks plus ytickline plus ypadding y: 0} space_for_ylabel = height_required_for_text(y_label_font, y_label) y_tick = Enum.max_by(y_ticks, &tick_length(&1)) space_for_yticks = length_required_for_text(y_tick_font, y_tick) diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index d82a3a5..3b98248 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -163,16 +163,17 @@ defmodule Matplotex.Helpers do # end def scatter() do - x = [1, 2, 3, 4, 6, 6, 7] - y = [1, 3, 4, 4, 5, 6, 7] - + # x = [10, 20, 3, 4, 6, 6, 7] + # y = [1, 3, 4, 4, 5, 6, 7] + x = [0,10,20] + y = [0,10,20] frame_width = 10 frame_height = 10 size = {frame_width, frame_height} margin = 0.05 font_size = "16pt" title_font_size = "18pt" - ticks = [1, 2, 3, 4, 5, 6, 7] + ticks = [0, 10, 20] x |> Matplotex.scatter(y) @@ -182,8 +183,8 @@ defmodule Matplotex.Helpers do |> Matplotex.set_ylabel("Y Axis") |> Matplotex.set_xticks(ticks) |> Matplotex.set_yticks(ticks) - |> Matplotex.set_xlim({1, 7}) - |> Matplotex.set_ylim({1, 7}) + |> Matplotex.set_xlim({0, 20}) + |> Matplotex.set_ylim({0, 20}) # TODO: Setting limits are not taking the proper xy values |> Matplotex.set_rc_params( x_tick_font_size: font_size, diff --git a/test/matplotex/figure/lead_test.exs b/test/matplotex/figure/lead_test.exs index 49cb49d..51be8a4 100644 --- a/test/matplotex/figure/lead_test.exs +++ b/test/matplotex/figure/lead_test.exs @@ -1,4 +1,5 @@ defmodule Matplotex.Figure.LeadTest do + alias Matplotex.Figure.TwoD alias Matplotex.Figure.Areal.Region alias Matplotex.Figure.Coords alias Matplotex.Figure.LinePlot @@ -260,8 +261,8 @@ defmodule Matplotex.Figure.LeadTest do } } = Lead.set_regions(figure) - assert Enum.all?([rxx, rxwidth, rxheight, ryy, rywidth, ryheight], &(&1 > 0)) - assert Enum.all?([rxy, ryx], &(&1 == 0)) + assert Enum.all?([rxx,rxy, rxwidth, rxheight,ryx, ryy, rywidth, ryheight], &(&1 != 0)) + end test "set region title updates the values for titles space", %{figure2: figure} do @@ -271,7 +272,7 @@ defmodule Matplotex.Figure.LeadTest do } } = Lead.set_regions(figure) - assert Enum.all?([rtx, rty, rtwidth, rtheight], &(&1 > 0)) + assert Enum.all?([rtx, rty, rtwidth, rtheight], &(&1 != 0)) end test "setting region legend", %{figure2: figure} do @@ -283,7 +284,7 @@ defmodule Matplotex.Figure.LeadTest do } } = Lead.set_regions(figure) - assert Enum.all?([rlx, rly, rlwidth, rlheight], &(&1 > 0)) + assert Enum.all?([rlx, rly, rlwidth, rlheight], &(&1 != 0)) end test "setting content takes the same width of x region and y region", %{figure2: figure} do @@ -300,6 +301,23 @@ defmodule Matplotex.Figure.LeadTest do assert rcwidth == rxwidth assert ryheight == rcheight end + + test "generates ticks from dataset if does't exist for show_ticks true", %{figure2: %Figure{axes: %{tick: tick}= axes} = figure} do + figure = %Figure{figure | axes: %{axes | tick: %TwoD{tick |x: nil, y: nil}, limit: %TwoD{x: nil, y: nil}}} + assert %Figure{ + figsize: {width, height}, + axes: %{ + tick: %TwoD{x: xticks, y: yticks}, + data: {x, y} + } + } = Lead.set_regions(figure) + assert Enum.min(xticks) == 0 + assert Enum.min(yticks) == 0 + assert Enum.max(xticks) == Enum.max(x) + assert Enum.max(yticks) == Enum.max(y) + assert length(xticks) == ceil(width) + assert length(yticks) == ceil(height) + end end test "all coordinates will be zero if input is zero", %{figure2: figure} do @@ -324,6 +342,8 @@ defmodule Matplotex.Figure.LeadTest do assert rcy == 0 assert rcwidth == 0 assert rcheight == 0 + assert ryheight == 0 + assert rxwidth == 0 end test "height will tally with all vertical components", %{figure: figure} do @@ -355,4 +375,6 @@ defmodule Matplotex.Figure.LeadTest do two_side_margin = width * margin * 2 assert width == two_side_margin + ry_width + rcwidth + rlwidth end + + end diff --git a/test/matplotex/figure/rc_param_test.exs b/test/matplotex/figure/rc_param_test.exs index 34a307b..f2b01fe 100644 --- a/test/matplotex/figure/rc_param_test.exs +++ b/test/matplotex/figure/rc_param_test.exs @@ -76,6 +76,7 @@ defmodule Matplotex.Figure.RcParamTest do assert keys |> Enum.sort() == [ + :y_label_dominant_baseline, :y_label_font_size, :y_label_font_family, :y_label_font_style, From 6a998a35007730956c401c8772679261a0854034 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Tue, 10 Dec 2024 16:05:38 +0530 Subject: [PATCH 08/14] format --- lib/matplotex/figure/areal.ex | 15 +++++----- lib/matplotex/figure/areal/bar_chart.ex | 16 ++++++++-- lib/matplotex/figure/areal/line_plot.ex | 1 - lib/matplotex/figure/areal/region.ex | 2 +- lib/matplotex/figure/cast.ex | 1 + lib/matplotex/figure/lead.ex | 40 ++++++++++++++++--------- lib/matplotex/helpers.ex | 4 +-- test/matplotex/figure/lead_test.exs | 28 +++++++++-------- 8 files changed, 68 insertions(+), 39 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 5dab8ee..3f91d78 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -62,17 +62,18 @@ defmodule Matplotex.Figure.Areal do end def add_ticks(%__MODULE__{tick: tick} = axes, {key, ticks}) when is_list(ticks) do + {ticks, lim} = + if number_based?(ticks) do + {ticks, Enum.min_max(ticks)} + else + {Enum.with_index(ticks), {0, length(ticks)}} + end - {ticks, lim} = if number_based?(ticks) do - {ticks, Enum.min_max(ticks)} - else - {Enum.with_index(ticks) , {0, length(ticks)}} - end tick = Map.put(tick, key, ticks) axes - |>set_limit({key,lim}) - |>update_tick(tick) + |> set_limit({key, lim}) + |> update_tick(tick) end def add_ticks(%__MODULE__{tick: tick, size: size} = axes, {key, {_min, _max} = lim}) do diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index df3b911..1fa6501 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -48,7 +48,12 @@ defmodule Matplotex.Figure.Areal.BarChart do %{ dataset: data, limit: %{x: xlim, y: ylim}, - region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content, height: height_region_content}, + region_content: %Region{ + x: x_region_content, + y: y_region_content, + width: width_region_content, + height: height_region_content + }, element: elements } = axes, rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} @@ -58,11 +63,18 @@ defmodule Matplotex.Figure.Areal.BarChart do y_padding_value = height_region_content * y_padding shrinked_width_region_content = width_region_content - x_padding_value * 2 shrinked_height_region_content = height_region_content - y_padding_value * 2 + bar_elements = data |> Enum.map(fn dataset -> dataset - |> do_transform(xlim, ylim, shrinked_width_region_content, shrinked_height_region_content, {x_region_content+x_padding_value, y_region_content + y_padding_value}) + |> do_transform( + xlim, + ylim, + shrinked_width_region_content, + shrinked_height_region_content, + {x_region_content + x_padding_value, y_region_content + y_padding_value} + ) |> capture(y_region_content) end) |> List.flatten() diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index 595bba1..4ea11e0 100644 --- a/lib/matplotex/figure/areal/line_plot.ex +++ b/lib/matplotex/figure/areal/line_plot.ex @@ -95,7 +95,6 @@ defmodule Matplotex.Figure.Areal.LinePlot do value * s + transition - minl * s end - def generate_ticks([{_l, _v} | _] = data) do {data, min_max(data)} end diff --git a/lib/matplotex/figure/areal/region.ex b/lib/matplotex/figure/areal/region.ex index e5e7791..ef11027 100644 --- a/lib/matplotex/figure/areal/region.ex +++ b/lib/matplotex/figure/areal/region.ex @@ -1,6 +1,6 @@ defmodule Matplotex.Figure.Areal.Region do alias Matplotex.Figure.Areal.XyRegion.Coords - defstruct [x: 0, y: 0, width: 0, height: 0, name: nil, theta: 0, coords: nil] + defstruct x: 0, y: 0, width: 0, height: 0, name: nil, theta: 0, coords: nil def get_label_coords(%__MODULE__{x: x, y: y, coords: %Coords{label: {label_x, label_y}}}) do {x + label_x, y + label_y} diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index be37647..847a042 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -433,6 +433,7 @@ defmodule Matplotex.Figure.Cast do x_padding_value = width_region_x * x_padding shrinked_width_region_x = width_region_x - x_padding_value * 2 x_region_x_with_padding = x_region_x + x_padding_value + ticks_width_position = x_ticks |> length() diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index a98acc2..e914e71 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -21,6 +21,7 @@ defmodule Matplotex.Figure.Lead do |> set_yticks_coords() |> set_border_coords() end + @spec set_regions(Matplotex.Figure.t()) :: Matplotex.Figure.t() def set_regions(%Figure{figsize: {width, height}} = figure) when width > 0 and height > 0 do @@ -55,17 +56,28 @@ defmodule Matplotex.Figure.Lead do defp ensure_ticks_are_valid( %Figure{ - figsize: {width, height}, + figsize: {width, height}, axes: %{ data: {x_data, y_data}, - tick: %TwoD{x: x_ticks, y: y_ticks} = ticks,limit: %TwoD{x: x_lim, y: y_lim} = limit}= axes} = figure) do + tick: %TwoD{x: x_ticks, y: y_ticks} = ticks, + limit: %TwoD{x: x_lim, y: y_lim} = limit + } = axes + } = figure + ) do + {x_ticks, x_lim} = maybe_generate_ticks(x_ticks, x_lim, x_data, width) + {y_ticks, y_lim} = maybe_generate_ticks(y_ticks, y_lim, y_data, height) - {x_ticks, x_lim} = maybe_generate_ticks(x_ticks, x_lim, x_data, width) - {y_ticks, y_lim} = maybe_generate_ticks(y_ticks, y_lim, y_data, height) + %Figure{ + figure + | axes: %{ + axes + | tick: %TwoD{ticks | x: x_ticks, y: y_ticks}, + limit: %TwoD{limit | x: x_lim, y: y_lim} + } + } + end - %Figure{figure | axes: %{axes | tick: %TwoD{ticks | x: x_ticks, y: y_ticks},limit: %TwoD{limit | x: x_lim, y: y_lim}}} - end defp maybe_generate_ticks(ticks, limit, data, number_of_ticks) do if is_nil(ticks) || length(ticks) < 3 do generate_ticks(limit, data, ceil(number_of_ticks)) @@ -77,20 +89,20 @@ defmodule Matplotex.Figure.Lead do defp generate_ticks(nil, data, number_of_ticks) do {min, upper_limit} = Enum.min_max(data) - lower_limit = if min < 0 do - min - else - 0 - end + lower_limit = + if min < 0 do + min + else + 0 + end generate_ticks({lower_limit, upper_limit}, data, number_of_ticks) end + defp generate_ticks({lower_limit, upper_limit} = lim, _data, number_of_ticks) do - {lower_limit|> Nx.linspace(upper_limit, n: number_of_ticks)|> Nx.to_list(), lim} + {lower_limit |> Nx.linspace(upper_limit, n: number_of_ticks) |> Nx.to_list(), lim} end - - defp set_region_xy( %Figure{ axes: diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index 3b98248..e7b771c 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -165,8 +165,8 @@ defmodule Matplotex.Helpers do def scatter() do # x = [10, 20, 3, 4, 6, 6, 7] # y = [1, 3, 4, 4, 5, 6, 7] - x = [0,10,20] - y = [0,10,20] + x = [0, 10, 20] + y = [0, 10, 20] frame_width = 10 frame_height = 10 size = {frame_width, frame_height} diff --git a/test/matplotex/figure/lead_test.exs b/test/matplotex/figure/lead_test.exs index 51be8a4..717a7ce 100644 --- a/test/matplotex/figure/lead_test.exs +++ b/test/matplotex/figure/lead_test.exs @@ -261,8 +261,7 @@ defmodule Matplotex.Figure.LeadTest do } } = Lead.set_regions(figure) - assert Enum.all?([rxx,rxy, rxwidth, rxheight,ryx, ryy, rywidth, ryheight], &(&1 != 0)) - + assert Enum.all?([rxx, rxy, rxwidth, rxheight, ryx, ryy, rywidth, ryheight], &(&1 != 0)) end test "set region title updates the values for titles space", %{figure2: figure} do @@ -302,15 +301,22 @@ defmodule Matplotex.Figure.LeadTest do assert ryheight == rcheight end - test "generates ticks from dataset if does't exist for show_ticks true", %{figure2: %Figure{axes: %{tick: tick}= axes} = figure} do - figure = %Figure{figure | axes: %{axes | tick: %TwoD{tick |x: nil, y: nil}, limit: %TwoD{x: nil, y: nil}}} + test "generates ticks from dataset if does't exist for show_ticks true", %{ + figure2: %Figure{axes: %{tick: tick} = axes} = figure + } do + figure = %Figure{ + figure + | axes: %{axes | tick: %TwoD{tick | x: nil, y: nil}, limit: %TwoD{x: nil, y: nil}} + } + assert %Figure{ - figsize: {width, height}, - axes: %{ - tick: %TwoD{x: xticks, y: yticks}, - data: {x, y} - } - } = Lead.set_regions(figure) + figsize: {width, height}, + axes: %{ + tick: %TwoD{x: xticks, y: yticks}, + data: {x, y} + } + } = Lead.set_regions(figure) + assert Enum.min(xticks) == 0 assert Enum.min(yticks) == 0 assert Enum.max(xticks) == Enum.max(x) @@ -375,6 +381,4 @@ defmodule Matplotex.Figure.LeadTest do two_side_margin = width * margin * 2 assert width == two_side_margin + ry_width + rcwidth + rlwidth end - - end From e4f91f88075989ad7e4ea45a5088ef74cff74d84 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Tue, 10 Dec 2024 18:20:21 +0530 Subject: [PATCH 09/14] bar chart errors --- lib/matplotex/figure/areal/bar_chart.ex | 6 ++++-- lib/matplotex/figure/cast.ex | 9 ++++----- lib/matplotex/figure/sketch.ex | 7 ------- lib/matplotex/helpers.ex | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index 1fa6501..8353ef0 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -75,7 +75,7 @@ defmodule Matplotex.Figure.Areal.BarChart do shrinked_height_region_content, {x_region_content + x_padding_value, y_region_content + y_padding_value} ) - |> capture(y_region_content) + |> capture(-y_region_content) end) |> List.flatten() @@ -124,6 +124,8 @@ defmodule Matplotex.Figure.Areal.BarChart do } = dataset, bly ) do + + capture( to_capture, captured ++ @@ -133,7 +135,7 @@ defmodule Matplotex.Figure.Areal.BarChart do x: bar_position(x, pos_factor), y: y, width: width, - height: y - bly, + height: bly - y, color: color } ], diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 847a042..3698a04 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -451,8 +451,7 @@ defmodule Matplotex.Figure.Cast do tick_position, 0, x_region_x_with_padding, - y_x_tick, - 0 + y_x_tick ) |> Algebra.flip_y_coordinate() @@ -461,7 +460,7 @@ defmodule Matplotex.Figure.Cast do type: @xtick_type, x: x_x_tick, y: y_x_tick, - text: label + text: format_tick_label(label) } |> Label.cast_label(x_tick_font) @@ -476,7 +475,6 @@ defmodule Matplotex.Figure.Cast do {%Tick{type: @xtick_type, tick_line: line, label: label}, {x_x_tick, y_x_tick_line}} end) |> Enum.unzip() - elements = elements ++ x_tick_elements %Figure{ @@ -492,7 +490,8 @@ defmodule Matplotex.Figure.Cast do end def cast_xticks_by_region(figure), do: figure - + defp format_tick_label({label, _index}), do: label + defp format_tick_label(label), do: label defp content_linespace(number_of_ticks_required, axis_size) do Nx.linspace(0, axis_size, n: number_of_ticks_required) |> Nx.to_list() end diff --git a/lib/matplotex/figure/sketch.ex b/lib/matplotex/figure/sketch.ex index 4d1bac3..9f6d71b 100644 --- a/lib/matplotex/figure/sketch.ex +++ b/lib/matplotex/figure/sketch.ex @@ -13,7 +13,6 @@ defmodule Matplotex.Figure.Sketch do def call(%Figure{axes: %{element: elements}, figsize: {width, height}}) do elements - # |> flipy(height) |> build_elements() |> wrap_with_tag(width * @dpi, height * @dpi) end @@ -45,10 +44,4 @@ defmodule Matplotex.Figure.Sketch do ) end - - defp flipy(elements, height) do - Enum.map(elements, fn %module{} = elem -> - module.flipy(elem, height) - end) - end end diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index e7b771c..394aada 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -227,7 +227,7 @@ defmodule Matplotex.Helpers do Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") |> Matplotex.bar(-width, values2, width, label: "Dataset2", color: "orange") |> Matplotex.set_xticks(categories) - |> Matplotex.figure(%{figsize: {3, 3}, margin: 0.05}) + |> Matplotex.figure(%{figsize: {10, 10}, margin: 0.05}) |> Matplotex.set_title("Bar chart") |> Matplotex.set_xlabel("X-axis") |> Matplotex.set_ylabel("Y-Axis") From ab14ae81ff8351ce94a2a9c80d00e10d72d9e195 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Wed, 11 Dec 2024 19:44:06 +0530 Subject: [PATCH 10/14] fix bar chart position --- lib/matplotex/figure/areal.ex | 22 ++++++++++++---------- lib/matplotex/figure/areal/bar_chart.ex | 17 +++++++---------- lib/matplotex/figure/cast.ex | 8 +++++--- lib/matplotex/figure/lead.ex | 2 +- lib/matplotex/figure/rc_params.ex | 2 +- lib/matplotex/helpers.ex | 8 +++++--- 6 files changed, 31 insertions(+), 28 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 3f91d78..8c4bec7 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -225,15 +225,18 @@ defmodule Matplotex.Figure.Areal do end end end - - def transformation({_label, value}, y, xminmax, yminmax, width, height, transition) do - transformation(value, y, xminmax, yminmax, width, height, transition) + def transformation({_labelx, x}, {_labely, y}, xminmax, yminmax, width, height, transition) do + transformation(x, y, xminmax, yminmax, width, height, transition) + end + def transformation({_label, x}, y, xminmax, yminmax, width, height, transition) do + transformation(x, y, xminmax, yminmax, width, height, transition) end - def transformation(x, {_label, value}, y, xminmax, yminmax, width, transition) do - transformation(x, value, y, xminmax, yminmax, width, transition) + def transformation(x, {_label, y}, xminmax, yminmax, width, height, transition) do + transformation(x, y, xminmax, yminmax, width, height, transition) end + def transformation( x, y, @@ -245,10 +248,9 @@ defmodule Matplotex.Figure.Areal do ) do sx = svg_width / (xmax - xmin) sy = svg_height / (ymax - ymin) - - tx = transition_x - xmin * sx - ty = transition_y - ymin * sy - + IO.inspect({x, y, transition_x, transition_y, xmin, xmax, ymin, ymax, svg_height, svg_width}, label: "Transformation") + tx = transition_x #- xmin * sx + ty = transition_y #- ymin * sy Algebra.transform_given_point(x, y, sx, sy, tx, ty) end @@ -257,11 +259,11 @@ defmodule Matplotex.Figure.Areal do x |> Enum.zip(y) |> Enum.map(fn {x, y} -> + x |> transformation(y, xlim, ylim, width, height, transition) |> Algebra.flip_y_coordinate() end) - %Dataset{dataset | transformed: transformed} end end diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index 8353ef0..db5991e 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -33,7 +33,7 @@ defmodule Matplotex.Figure.Areal.BarChart do dataset = Dataset.cast(%Dataset{x: x, y: values, pos: pos, width: width}, opts) datasets = data ++ [dataset] xydata = flatten_for_data(datasets) - %Figure{figure | axes: %{axes | data: xydata, dataset: datasets}} + %Figure{figure | rc_params: %RcParams{white_space: width, y_padding: 0}, axes: %{axes | data: xydata, dataset: datasets}} end @impl Areal @@ -56,13 +56,11 @@ defmodule Matplotex.Figure.Areal.BarChart do }, element: elements } = axes, - rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} + rc_params: %RcParams{x_padding: x_padding, white_space: white_space} } = figure ) do - x_padding_value = width_region_content * x_padding - y_padding_value = height_region_content * y_padding + x_padding_value = width_region_content * x_padding + white_space shrinked_width_region_content = width_region_content - x_padding_value * 2 - shrinked_height_region_content = height_region_content - y_padding_value * 2 bar_elements = data @@ -72,8 +70,8 @@ defmodule Matplotex.Figure.Areal.BarChart do xlim, ylim, shrinked_width_region_content, - shrinked_height_region_content, - {x_region_content + x_padding_value, y_region_content + y_padding_value} + height_region_content, + {x_region_content + x_padding_value, y_region_content} ) |> capture(-y_region_content) end) @@ -124,8 +122,6 @@ defmodule Matplotex.Figure.Areal.BarChart do } = dataset, bly ) do - - capture( to_capture, captured ++ @@ -147,7 +143,8 @@ defmodule Matplotex.Figure.Areal.BarChart do defp capture([], captured, _dataset, _bly), do: captured defp hypox(y) do - 1..length(y) |> Enum.into([]) + nof_x = length(y) + 0|>Nx.linspace(nof_x, n: nof_x)|>Nx.to_list() end defp bar_position(x, pos_factor) when pos_factor < 0 do diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 3698a04..cd69188 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -422,7 +422,8 @@ defmodule Matplotex.Figure.Cast do rc_params: %RcParams{ x_tick_font: x_tick_font, x_padding: x_padding, - tick_line_length: tick_line_length + tick_line_length: tick_line_length, + white_space: white_space } } = figure ) @@ -430,7 +431,7 @@ defmodule Matplotex.Figure.Cast do x_ticks = confine_ticks(x_ticks, xlim) x_data = confine_data(x_data, xlim) dataset = confine_data(dataset, xlim, :x) - x_padding_value = width_region_x * x_padding + x_padding_value = width_region_x * x_padding + white_space shrinked_width_region_x = width_region_x - x_padding_value * 2 x_region_x_with_padding = x_region_x + x_padding_value @@ -491,6 +492,7 @@ defmodule Matplotex.Figure.Cast do def cast_xticks_by_region(figure), do: figure defp format_tick_label({label, _index}), do: label + defp format_tick_label(label) when is_float(label), do: Float.round(label, 2) defp format_tick_label(label), do: label defp content_linespace(number_of_ticks_required, axis_size) do Nx.linspace(0, axis_size, n: number_of_ticks_required) |> Nx.to_list() @@ -636,7 +638,7 @@ defmodule Matplotex.Figure.Cast do type: @ytick_type, y: y_y_tick, x: x_y_tick, - text: label + text: format_tick_label(label) } |> Label.cast_label(y_tick_font) diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index e914e71..f578268 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -533,7 +533,7 @@ defmodule Matplotex.Figure.Lead do end defp tick_length(tick) when is_float(tick) do - tick |> Float.to_string() |> String.length() + tick|>Float.round(2) |> Float.to_string() |> String.length() end defp tick_length({label, _v}) when is_binary(label) do diff --git a/lib/matplotex/figure/rc_params.ex b/lib/matplotex/figure/rc_params.ex index 1a4deec..897af08 100644 --- a/lib/matplotex/figure/rc_params.ex +++ b/lib/matplotex/figure/rc_params.ex @@ -44,6 +44,7 @@ defmodule Matplotex.Figure.RcParams do tick_line_length: @tick_line_length, x_padding: @chart_padding, y_padding: @chart_padding, + white_space: 0, label_padding: @label_padding, legend_width: @default_legend_width_percentage, legend_items_orientation: @default_legend_items_orientation @@ -142,7 +143,6 @@ defmodule Matplotex.Figure.RcParams do ) do element_font_keys = font_associated_keys(element) element_params = Map.take(params, element_font_keys) - font = Map.get(rc_params, :"#{element}_font") updated_font = Font.update(font, element_params, element) Map.put(rc_params, :"#{element}_font", updated_font) diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index 394aada..93f7be8 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -219,9 +219,9 @@ defmodule Matplotex.Helpers do end def multi_bar() do - categories = ["apple", "banana", "fig"] - values1 = [22, 33, 28] - values2 = [53, 63, 59] + categories = ["apple", "banana", "fig", "avocado"] + values1 = [22, 33, 28, 34] + values2 = [53, 63, 59, 60] width = 0.22 Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") @@ -232,6 +232,8 @@ defmodule Matplotex.Helpers do |> Matplotex.set_xlabel("X-axis") |> Matplotex.set_ylabel("Y-Axis") |> Matplotex.hide_v_grid() + |> Matplotex.set_rc_params(y_tick_flate: -0.6) + |> Matplotex.set_ylim({0, 70}) |> Matplotex.show() |> copy() end From 9362ffe5039e7854c4d61eba86ead138e07d43ce Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 12 Dec 2024 12:40:38 +0530 Subject: [PATCH 11/14] fixing some attributes --- lib/matplotex/figure/areal.ex | 9 ++++----- lib/matplotex/figure/areal/bar_chart.ex | 6 ++++-- lib/matplotex/figure/areal/region.ex | 3 ++- lib/matplotex/figure/cast.ex | 19 +++++++++++-------- lib/matplotex/figure/lead.ex | 15 ++++++++------- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 8c4bec7..8bd4d7f 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -30,7 +30,7 @@ defmodule Matplotex.Figure.Areal do alias Matplotex.Figure.Dataset alias Matplotex.Figure.Text - + @default_tick_minimum 0 def add_label(%__MODULE__{label: nil} = axes, {key, label}, opts) when is_binary(label) do label = Map.new() @@ -66,7 +66,7 @@ defmodule Matplotex.Figure.Areal do if number_based?(ticks) do {ticks, Enum.min_max(ticks)} else - {Enum.with_index(ticks), {0, length(ticks)}} + {Enum.with_index(ticks), {@default_tick_minimum, length(ticks)}} end tick = Map.put(tick, key, ticks) @@ -248,9 +248,8 @@ defmodule Matplotex.Figure.Areal do ) do sx = svg_width / (xmax - xmin) sy = svg_height / (ymax - ymin) - IO.inspect({x, y, transition_x, transition_y, xmin, xmax, ymin, ymax, svg_height, svg_width}, label: "Transformation") - tx = transition_x #- xmin * sx - ty = transition_y #- ymin * sy + tx = transition_x - xmin * sx + ty = transition_y - ymin * sy Algebra.transform_given_point(x, y, sx, sy, tx, ty) end diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index db5991e..c9ac286 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -9,6 +9,8 @@ defmodule Matplotex.Figure.Areal.BarChart do alias Matplotex.Figure.Areal use Areal + @xmin_value 0 + frame( legend: %Legend{}, coords: %Coords{}, @@ -100,7 +102,7 @@ defmodule Matplotex.Figure.Areal.BarChart do def generate_ticks(data) do max = Enum.max(data) step = max |> round_to_best() |> div(5) |> round_to_best() - {list_of_ticks(data, step), {0, max}} + {list_of_ticks(data, step), {@xmin_value, max}} end def generate_ticks(side, {min, max} = lim) do @@ -144,7 +146,7 @@ defmodule Matplotex.Figure.Areal.BarChart do defp hypox(y) do nof_x = length(y) - 0|>Nx.linspace(nof_x, n: nof_x)|>Nx.to_list() + @xmin_value|>Nx.linspace(nof_x, n: nof_x)|>Nx.to_list() end defp bar_position(x, pos_factor) when pos_factor < 0 do diff --git a/lib/matplotex/figure/areal/region.ex b/lib/matplotex/figure/areal/region.ex index ef11027..e4ad9f6 100644 --- a/lib/matplotex/figure/areal/region.ex +++ b/lib/matplotex/figure/areal/region.ex @@ -1,6 +1,7 @@ defmodule Matplotex.Figure.Areal.Region do alias Matplotex.Figure.Areal.XyRegion.Coords - defstruct x: 0, y: 0, width: 0, height: 0, name: nil, theta: 0, coords: nil + @zero_by_default 0 + defstruct x: @zero_by_default, y: @zero_by_default, width: @zero_by_default, height: @zero_by_default, name: nil, theta: @zero_by_default, coords: nil def get_label_coords(%__MODULE__{x: x, y: y, coords: %Coords{label: {label_x, label_y}}}) do {x + label_x, y + label_y} diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index cd69188..16324d6 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -14,6 +14,8 @@ defmodule Matplotex.Figure.Cast do @ytick_type "figure.y_tick" @stroke_grid "#ddd" @stroke_width_grid 1 + @lowest_tick 0 + @zero_to_move 0 def cast_spines( %Figure{ @@ -444,13 +446,14 @@ defmodule Matplotex.Figure.Cast do {x_tick_elements, vgrid_coords} = Enum.map(ticks_width_position, fn {tick_position, label} -> {_, y_x_tick_line} = - Algebra.transform_given_point(0, height_region_x, x_region_x, y_region_x) + @zero_to_move + |>Algebra.transform_given_point(height_region_x, x_region_x, y_region_x) |> Algebra.flip_y_coordinate() {x_x_tick, y_x_tick} = - Algebra.transform_given_point( - tick_position, - 0, + tick_position + |>Algebra.transform_given_point( + @zero_to_move, x_region_x_with_padding, y_x_tick ) @@ -495,7 +498,7 @@ defmodule Matplotex.Figure.Cast do defp format_tick_label(label) when is_float(label), do: Float.round(label, 2) defp format_tick_label(label), do: label defp content_linespace(number_of_ticks_required, axis_size) do - Nx.linspace(0, axis_size, n: number_of_ticks_required) |> Nx.to_list() + @lowest_tick |> Nx.linspace(axis_size, n: number_of_ticks_required) |> Nx.to_list() end @spec cast_yticks(Matplotex.Figure.t()) :: Matplotex.Figure.t() @@ -622,11 +625,11 @@ defmodule Matplotex.Figure.Cast do {ytick_elements, hgrid_coords} = Enum.map(ticks_width_position, fn {tick_position, label} -> {x_y_tick_line, _} = - Algebra.transform_given_point(width_region_y, 0, x_region_y, y_region_y) + Algebra.transform_given_point(width_region_y, @zero_to_move, x_region_y, y_region_y) {x_y_tick, y_y_tick} = - Algebra.transform_given_point( - 0, + @zero_to_move + |>Algebra.transform_given_point( tick_position, x_y_tick, y_region_y_with_padding diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index f578268..567002d 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -11,6 +11,7 @@ defmodule Matplotex.Figure.Lead do @pt_to_inch 1 / 150 @padding 10 / 96 @tick_line_offset 5 / 96 + @zero_to_move 0 def set_spines(%Figure{} = figure) do figure @@ -138,15 +139,15 @@ defmodule Matplotex.Figure.Lead do [space_for_x_label, space_for_x_tick, tick_line_length] |> Enum.sum() {x_region_x, y_region_x} = - Algebra.transform_given_point(space_required_for_region_y, 0, lx, by) + Algebra.transform_given_point(space_required_for_region_y, @zero_to_move, lx, by) {x_region_y, y_region_y} = - Algebra.transform_given_point(0, space_required_for_region_x, lx, by) + Algebra.transform_given_point(@zero_to_move, space_required_for_region_x, lx, by) - x_label_coords = Algebra.transform_given_point(0, 0, x_region_x, y_region_x) - x_tick_coords = Algebra.transform_given_point(0, space_for_x_label, x_region_x, y_region_x) - y_label_coords = Algebra.transform_given_point(0, 0, x_region_y, y_region_y) - y_tick_coords = Algebra.transform_given_point(space_for_ylabel, 0, x_region_y, y_region_y) + x_label_coords = Algebra.transform_given_point(@zero_to_move, @zero_to_move, x_region_x, y_region_x) + x_tick_coords = Algebra.transform_given_point(@zero_to_move, space_for_x_label, x_region_x, y_region_x) + y_label_coords = Algebra.transform_given_point(@zero_to_move, @zero_to_move, x_region_y, y_region_y) + y_tick_coords = Algebra.transform_given_point(space_for_ylabel, @zero_to_move, x_region_y, y_region_y) %Figure{ figure @@ -191,7 +192,7 @@ defmodule Matplotex.Figure.Lead do space_for_title = height_required_for_text(title_font, title) {x_region_title, y_region_title} = - Algebra.transform_given_point(0, -space_for_title, lx, ty, 0) + Algebra.transform_given_point(@zero_to_move, -space_for_title, lx, ty, 0) %Figure{ figure From ec8f0658ecd1952333082f40dd826e645f4d4b80 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 12 Dec 2024 17:27:27 +0530 Subject: [PATCH 12/14] final cleanup --- lib/matplotex/figure/areal.ex | 12 -- lib/matplotex/figure/areal/scatter.ex | 2 +- lib/matplotex/figure/cast.ex | 1 + lib/matplotex/figure/lead.ex | 202 +---------------------- lib/matplotex/helpers.ex | 8 +- lib/matplotex/utils/algebra.ex | 6 +- test/matplotex/figure/cast_test.exs | 115 +------------ test/matplotex/figure/lead_test.exs | 224 -------------------------- 8 files changed, 14 insertions(+), 556 deletions(-) diff --git a/lib/matplotex/figure/areal.ex b/lib/matplotex/figure/areal.ex index 8bd4d7f..1aadc7b 100644 --- a/lib/matplotex/figure/areal.ex +++ b/lib/matplotex/figure/areal.ex @@ -141,18 +141,6 @@ defmodule Matplotex.Figure.Areal do defp update_limit(limit, _, _), do: limit - def materialized(figure) do - figure - |> Lead.set_spines() - |> Cast.cast_xticks() - |> Cast.cast_yticks() - |> Cast.cast_hgrids() - |> Cast.cast_vgrids() - |> Cast.cast_spines() - |> Cast.cast_label() - |> Cast.cast_title() - end - def materialized_by_region(figure) do figure |> Lead.set_regions() diff --git a/lib/matplotex/figure/areal/scatter.ex b/lib/matplotex/figure/areal/scatter.ex index adf2071..15846af 100644 --- a/lib/matplotex/figure/areal/scatter.ex +++ b/lib/matplotex/figure/areal/scatter.ex @@ -84,7 +84,7 @@ defmodule Matplotex.Figure.Areal.Scatter do end def materialize(xystream, figure) do - __MODULE__.materialized(figure) + __MODULE__.materialized_by_region(figure) |> material_stream(xystream) end diff --git a/lib/matplotex/figure/cast.ex b/lib/matplotex/figure/cast.ex index 16324d6..73b8dcb 100644 --- a/lib/matplotex/figure/cast.ex +++ b/lib/matplotex/figure/cast.ex @@ -430,6 +430,7 @@ defmodule Matplotex.Figure.Cast do } = figure ) when is_list(x_ticks) do + x_ticks = confine_ticks(x_ticks, xlim) x_data = confine_data(x_data, xlim) dataset = confine_data(dataset, xlim, :x) diff --git a/lib/matplotex/figure/lead.ex b/lib/matplotex/figure/lead.ex index 567002d..28ff82b 100644 --- a/lib/matplotex/figure/lead.ex +++ b/lib/matplotex/figure/lead.ex @@ -4,25 +4,13 @@ defmodule Matplotex.Figure.Lead do alias Matplotex.Figure.Font alias Matplotex.Figure.Areal.Region alias Matplotex.Figure.TwoD - alias Matplotex.Figure.Dimension alias Matplotex.Figure.Coords alias Matplotex.Figure.RcParams alias Matplotex.Figure @pt_to_inch 1 / 150 @padding 10 / 96 - @tick_line_offset 5 / 96 @zero_to_move 0 - def set_spines(%Figure{} = figure) do - figure - |> set_xlabel_coords() - |> set_ylabel_coords() - |> set_title_coords() - |> set_xticks_coords() - |> set_yticks_coords() - |> set_border_coords() - end - @spec set_regions(Matplotex.Figure.t()) :: Matplotex.Figure.t() def set_regions(%Figure{figsize: {width, height}} = figure) when width > 0 and height > 0 do @@ -68,7 +56,6 @@ defmodule Matplotex.Figure.Lead do ) do {x_ticks, x_lim} = maybe_generate_ticks(x_ticks, x_lim, x_data, width) {y_ticks, y_lim} = maybe_generate_ticks(y_ticks, y_lim, y_data, height) - %Figure{ figure | axes: %{ @@ -281,171 +268,6 @@ defmodule Matplotex.Figure.Lead do defp set_region_content(figure), do: figure - defp set_xlabel_coords(%Figure{axes: %{tick: %{y: nil}, show_y_ticks: true}} = figure) do - figure - |> generate_yticks() - |> set_xlabel_coords() - end - - defp set_xlabel_coords( - %Figure{ - rc_params: rc_params, - margin: margin, - figsize: {f_width, f_height}, - axes: - %{coords: coords, label: %{y: ylabel}, tick: %{y: y_ticks}, dimension: dimension} = - axes - } = figure - ) do - y_label_font_size = RcParams.get_rc(rc_params, :get_y_label_font_size) - y_tick_font_size = RcParams.get_rc(rc_params, :get_y_tick_font_size) - label_offset = label_offset(ylabel, y_label_font_size) - y_tick_offset = ytick_offset(y_ticks, y_tick_font_size) - y_tick_height = label_offset(y_ticks, y_tick_font_size) - - x_labelx = - f_width * margin + label_offset + y_tick_offset - - %Figure{ - figure - | axes: %{ - axes - | coords: %Coords{coords | x_label: {x_labelx, f_height * margin}}, - dimension: %Dimension{ - dimension - | y_label: {0, label_offset}, - y_tick: {y_tick_offset, y_tick_height} - } - } - } - end - - defp set_xlabel_coords(%Figure{} = figure), do: figure - - defp set_ylabel_coords(%Figure{axes: %{tick: %{x: nil}, show_x_ticks: true}} = figure) do - figure - |> generate_xticks() - |> set_ylabel_coords() - end - - defp set_ylabel_coords( - %Figure{ - rc_params: rc_params, - margin: margin, - figsize: {f_width, f_height}, - axes: - %{coords: coords, dimension: dimension, label: %{x: xlabel}, tick: %{x: x_ticks}} = - axes - } = figure - ) do - x_label_font_size = RcParams.get_rc(rc_params, :get_x_label_font_size) - x_tick_font_size = RcParams.get_rc(rc_params, :get_x_tick_font_size) - xlabel_offset = label_offset(xlabel, x_label_font_size) - x_tick_offset = xtick_offset(x_ticks, x_tick_font_size) - x_tick_width = ytick_offset(x_ticks, x_tick_font_size) - - y_labely = - f_height * margin + xlabel_offset + x_tick_offset - - %Figure{ - figure - | axes: %{ - axes - | coords: %Coords{coords | y_label: {f_width * margin, y_labely}}, - dimension: %Dimension{ - dimension - | x_label: {0, xlabel_offset}, - x_tick: {x_tick_width, x_tick_offset} - } - } - } - end - - defp set_ylabel_coords(%Figure{} = figure), do: figure - - defp set_title_coords( - %Figure{ - margin: margin, - figsize: {_f_width, f_height}, - axes: %{coords: %Coords{x_label: {xlx, _xly}} = coords} = axes - } = figure - ) do - %Figure{ - figure - | axes: %{axes | coords: %Coords{coords | title: {xlx, f_height - f_height * margin}}} - } - end - - defp set_xticks_coords( - %Figure{ - rc_params: rc_params, - axes: %{coords: %Coords{x_label: {xlx, xly}} = coords, label: %{x: xlabel}} = axes - } = figure - ) do - x_label_font_size = RcParams.get_rc(rc_params, :get_x_label_font_size) - xlable_offset = label_offset(xlabel, x_label_font_size) - - %Figure{ - figure - | axes: %{axes | coords: %Coords{coords | x_ticks: {xlx + @padding, xly + xlable_offset}}} - } - end - - defp set_yticks_coords( - %Figure{ - rc_params: rc_params, - axes: %{coords: %Coords{y_label: {ylx, yly}} = coords, label: %{y: ylabel}} = axes - } = figure - ) do - y_label_font_size = RcParams.get_rc(rc_params, :get_y_label_font_size) - ylabel_offset = label_offset(ylabel, y_label_font_size) - - %Figure{ - figure - | axes: %{axes | coords: %Coords{coords | y_ticks: {ylx + ylabel_offset + @padding, yly}}} - } - end - - defp set_border_coords( - %Figure{ - margin: margin, - figsize: {f_width, _}, - rc_params: rc_params, - axes: - %{ - coords: %Coords{x_label: {xlx, _}, y_label: {_ylx, yly}, title: {_, ytt}} = coords, - title: title - } = - axes - } = figure - ) do - bottom_left = {xlx, yly} - title_font_size = rc_params |> RcParams.get_rc(:get_title_font_size) - title_offset = label_offset(title, title_font_size) - topy = ytt - title_offset - top_left = {xlx, topy} - rightx = f_width - f_width * margin - top_right = {rightx, topy} - bottom_right = {rightx, yly} - width = rightx - xlx - height = topy - yly - - %Figure{ - figure - | axes: %{ - axes - | coords: %Coords{ - coords - | bottom_left: bottom_left, - top_right: top_right, - bottom_right: bottom_right, - top_left: top_left - }, - size: {width, height} - } - } - end - def focus_to_origin( %Figure{ figsize: {width, height}, @@ -503,7 +325,7 @@ defmodule Matplotex.Figure.Lead do {{centerx, centery}, radius} end - # TODO: Sort out how the user gets the control on font of the all texts + defp label_offset(nil, _font_size), do: 0 defp label_offset("", _font_size), do: 0 @@ -515,16 +337,6 @@ defmodule Matplotex.Figure.Lead do defp label_offset(_label, font_size) do font_size * @pt_to_inch + @padding end - - defp ytick_offset(y_ticks, font_size) do - tick_size = y_ticks |> Enum.max_by(fn tick -> tick_length(tick) end) |> tick_length() - font_size * @pt_to_inch * tick_size + @tick_line_offset + @padding - end - - defp xtick_offset(xticks, font_size) do - label_offset(xticks, font_size) + @tick_line_offset - end - defp tick_length(tick) when is_integer(tick) do tick |> Integer.to_string() |> String.length() end @@ -541,14 +353,6 @@ defmodule Matplotex.Figure.Lead do String.length(label) end - defp generate_yticks(%Figure{axes: %module{} = axes} = figure) do - %Figure{figure | axes: module.generate_yticks(axes)} - end - - defp generate_xticks(%Figure{axes: %module{} = axes} = figure) do - %Figure{figure | axes: module.generate_xticks(axes)} - end - defp height_required_for_text( %Font{ font_size: font_size, @@ -586,7 +390,7 @@ defmodule Matplotex.Figure.Lead do }, text ), - do: tick_length(text) * to_number(font_size) * pt_to_inch_ratio + flate + do: tick_length(text) * to_number(font_size) * (pt_to_inch_ratio/2) + flate defp length_required_for_text( %Font{ @@ -597,7 +401,7 @@ defmodule Matplotex.Figure.Lead do }, text ) do - text_length = tick_length(text) * to_number(font_size) * pt_to_inch_ratio + text_length = tick_length(text) * to_number(font_size) * (pt_to_inch_ratio/2) rotation = deg_to_rad(rotation) leng_for_rotation = :math.cos(rotation) * text_length leng_for_rotation + flate diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index 93f7be8..cbe8d5a 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -60,8 +60,8 @@ defmodule Matplotex.Helpers do x = [1, 2, 3, 4, 6, 6, 7] y = [1, 3, 4, 4, 5, 6, 7] - frame_width = 10 - frame_height = 10 + frame_width = 6 + frame_height = 6 size = {frame_width, frame_height} margin = 0.05 font_size = "16pt" @@ -212,6 +212,7 @@ defmodule Matplotex.Helpers do |> Matplotex.plot(x, y2, color: "red", linestyle: "--", marker: "^", label: "Dataset 2") |> Matplotex.plot(x, y3, color: "green", linestyle: "-.", marker: "s", label: "Dataset 3") |> Matplotex.set_title("Title") + |> Matplotex.set_xticks([1,2,3,4,5]) |> Matplotex.set_xlabel("X-Axis") |> Matplotex.set_ylabel("Y-Axis") |> Matplotex.show() @@ -232,7 +233,6 @@ defmodule Matplotex.Helpers do |> Matplotex.set_xlabel("X-axis") |> Matplotex.set_ylabel("Y-Axis") |> Matplotex.hide_v_grid() - |> Matplotex.set_rc_params(y_tick_flate: -0.6) |> Matplotex.set_ylim({0, 70}) |> Matplotex.show() |> copy() @@ -251,7 +251,7 @@ defmodule Matplotex.Helpers do |> Matplotex.scatter(y1, color: "blue", linestyle: "_", marker: "o", label: "Dataset 1") |> Matplotex.scatter(x, y2, color: "red", linestyle: "--", marker: "^", label: "Dataset 2") |> Matplotex.scatter(x, y3, color: "green", linestyle: "-.", marker: "s", label: "Dataset 3") - |> Matplotex.figure(%{figsize: {3, 3}, margin: 0.05}) + |> Matplotex.figure(%{figsize: {8, 8}, margin: 0.05}) |> Matplotex.set_title("Title") |> Matplotex.set_xlabel("X-Axis") |> Matplotex.set_ylabel("Y-Axis") diff --git a/lib/matplotex/utils/algebra.ex b/lib/matplotex/utils/algebra.ex index 6d9af55..1fde07f 100644 --- a/lib/matplotex/utils/algebra.ex +++ b/lib/matplotex/utils/algebra.ex @@ -50,6 +50,7 @@ defmodule Matplotex.Utils.Algebra do def svgfy(y, height), do: height - y def transform_given_point(x, y, sx, sy, tx, ty, theta \\ 0) do + point_matrix = Nx.tensor([x, y, 1], type: {:f, @tensor_data_type_bits}) Nx.tensor( [ [sx * :math.cos(theta), sy * -:math.sin(theta), tx], @@ -58,13 +59,14 @@ defmodule Matplotex.Utils.Algebra do ], type: {:f, @tensor_data_type_bits} ) - |> Nx.dot(Nx.tensor([x, y, 1], type: {:f, @tensor_data_type_bits})) + |> Nx.dot(point_matrix) |> Nx.to_flat_list() |> List.to_tuple() |> then(fn {x, y, _} -> {x, y} end) 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}) Nx.tensor( [ [:math.cos(theta), -:math.sin(theta), ox], @@ -73,7 +75,7 @@ defmodule Matplotex.Utils.Algebra do ], type: {:f, @tensor_data_type_bits} ) - |> Nx.dot(Nx.tensor([x, y, 1], type: {:f, @tensor_data_type_bits})) + |> Nx.dot(point_matrix) |> Nx.to_flat_list() |> List.to_tuple() |> then(fn {x, y, _} -> {x, y} end) diff --git a/test/matplotex/figure/cast_test.exs b/test/matplotex/figure/cast_test.exs index bd710f7..f78d994 100644 --- a/test/matplotex/figure/cast_test.exs +++ b/test/matplotex/figure/cast_test.exs @@ -5,20 +5,10 @@ defmodule Matplotex.Figure.CastTest do use Matplotex.PlotCase setup do - figure = Matplotex.FrameHelpers.sample_figure() |> Lead.set_spines() + figure = Matplotex.FrameHelpers.sample_figure() {:ok, %{figure: figure}} end - describe "cast_spines" do - test "add elements for borders in axes", %{figure: figure} do - assert %Figure{axes: %{element: elements}} = Cast.cast_spines(figure) - - assert Enum.filter(elements, fn x -> x.type == "spine.top" end) |> length() == 1 - assert Enum.filter(elements, fn x -> x.type == "spine.bottom" end) |> length() == 1 - assert Enum.filter(elements, fn x -> x.type == "spine.right" end) |> length() == 1 - assert Enum.filter(elements, fn x -> x.type == "spine.left" end) |> length() == 1 - end - end describe "cast_spines_by_region/1" do test "add elements for borders in axes", %{figure: figure} do @@ -32,18 +22,6 @@ defmodule Matplotex.Figure.CastTest do end end - describe "cast_title" do - test "add element for title in axes", %{figure: figure} do - assert %Figure{axes: %{element: elements}} = - figure - |> Lead.set_spines() - |> Cast.cast_spines() - |> Cast.cast_title() - - assert Enum.filter(elements, fn x -> x.type == "figure.title" end) |> length == 1 - end - end - describe "cast_title_by_region/1" do test "add element for title in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -56,18 +34,6 @@ defmodule Matplotex.Figure.CastTest do end end - describe "cast_label/1" do - test "add element for label in axes", %{figure: figure} do - assert %Figure{axes: %{element: elements}} = - figure - |> Lead.set_spines() - |> Cast.cast_label() - - assert Enum.filter(elements, fn x -> x.type == "figure.x_label" end) |> length() == 1 - assert Enum.filter(elements, fn x -> x.type == "figure.y_label" end) |> length() == 1 - end - end - describe "cast_label_by_region/1" do test "add element for label in axes", %{figure: figure} do assert %Figure{axes: %{element: elements}} = @@ -80,43 +46,6 @@ defmodule Matplotex.Figure.CastTest do end end - describe "cast_xticks/1" do - test "add element for tick in axes", %{figure: figure} do - assert %Figure{axes: %{element: elements, tick: %{x: x_ticks, y: _y_ticks}}} = - figure - |> Lead.set_spines() - |> Cast.cast_xticks() - - assert Enum.filter(elements, fn x -> x.type == "figure.x_tick" end) |> length() == - length(x_ticks) - end - - test "generates the ticks are getting confined within the limit", %{figure: figure} do - figure = Matplotex.set_xlim(figure, {2, 6}) - - %Figure{axes: %{element: elements}} = - figure - |> Lead.set_spines() - |> Cast.cast_xticks() - - assert Enum.filter(elements, fn x -> x.type == "figure.x_tick" end) |> length() == 5 - end - - test "ticks will get generated if no ticks added" do - x = [1, 3, 7, 4, 2, 5, 6] - y = [1, 3, 7, 4, 2, 5, 6] - figure = Matplotex.plot(x, y) - - %Figure{axes: %{data: {_x, _y}, tick: %{x: x_ticks}, element: elements}} = - figure - |> Lead.set_spines() - |> Cast.cast_xticks() - - assert Enum.filter(elements, fn x -> x.type == "figure.x_tick" end) |> length() == - length(x_ticks) - end - end - describe "cast_xticks_by_region/1" do test "add element for tick in axes", %{figure: figure} do assert %Figure{axes: %{element: elements, tick: %{x: x_ticks, y: _y_ticks}}} = @@ -129,18 +58,6 @@ defmodule Matplotex.Figure.CastTest do end end - describe "cast_yticks/1" do - test "add element for tick in axes", %{figure: figure} do - assert %Figure{axes: %{element: elements, tick: %{y: y_ticks}}} = - figure - |> Lead.set_spines() - |> Cast.cast_yticks() - - assert Enum.filter(elements, fn x -> x.type == "figure.y_tick" end) |> length() == - length(y_ticks) - end - end - describe "cast_yticks_by_region/1" do test "add element for tick in axes", %{figure: figure} do assert %Figure{axes: %{element: elements, tick: %{y: y_ticks}}} = @@ -154,20 +71,6 @@ defmodule Matplotex.Figure.CastTest do end # TODO: Testcases for show and hide hgrid - describe "cast_hgrids/1" do - test "add elements for horizontal grids", %{figure: figure} do - assert %Figure{axes: %{element: elements, tick: %{y: y_ticks}}} = - figure - |> Lead.set_spines() - |> Cast.cast_label() - |> Cast.cast_xticks() - |> Cast.cast_yticks() - |> Cast.cast_hgrids() - - assert Enum.filter(elements, fn x -> x.type == "figure.h_grid" end) |> length() == - length(y_ticks) - end - end describe "cast_hgrids_by_region/1" do test "add elements for horizontal grids", %{figure: figure} do @@ -182,22 +85,6 @@ defmodule Matplotex.Figure.CastTest do end end - describe "cast_vgrids/1" do - test "add elements for vertical grids", %{figure: figure} do - assert %Figure{axes: %{element: elements, tick: %{x: x_ticks}}} = - figure - |> Lead.set_spines() - |> Cast.cast_label() - |> Cast.cast_xticks() - |> Cast.cast_yticks() - |> Cast.cast_hgrids() - |> Cast.cast_vgrids() - - assert Enum.filter(elements, fn x -> x.type == "figure.v_grid" end) |> length() == - length(x_ticks) - end - end - describe "cast_vgrids_by_region/1" do test "add elements for vertical grids", %{figure: figure} do assert %Figure{axes: %{element: elements, tick: %{x: x_ticks}}} = diff --git a/test/matplotex/figure/lead_test.exs b/test/matplotex/figure/lead_test.exs index 717a7ce..5c1cd93 100644 --- a/test/matplotex/figure/lead_test.exs +++ b/test/matplotex/figure/lead_test.exs @@ -1,16 +1,10 @@ defmodule Matplotex.Figure.LeadTest do alias Matplotex.Figure.TwoD alias Matplotex.Figure.Areal.Region - alias Matplotex.Figure.Coords - alias Matplotex.Figure.LinePlot alias Matplotex.Figure - alias Matplotex.Figure.Areal.LinePlot use Matplotex.PlotCase alias Matplotex.Figure.Lead - # all the padding and tickline spaces - @padding_and_tick_line_space 25 / 96 - @padding 10 / 96 setup do figure = Matplotex.FrameHelpers.sample_figure() @@ -34,224 +28,6 @@ defmodule Matplotex.Figure.LeadTest do {:ok, %{figure: figure, figure2: figure2}} end - describe "set_spines/1" do - test "sets coordinates of spines in a figure by reducing margin", %{figure: figure} do - frame_width = 8 - frame_height = 6 - - margin = 0.1 - font_size = 0 - title_font_size = 0 - - figure = - figure - |> Matplotex.figure(%{margin: margin}) - |> Matplotex.set_rc_params(%{ - x_tick_font_size: font_size, - y_tick_font_size: font_size, - title_font_size: title_font_size, - x_label_font_size: font_size, - y_label_font_size: font_size - }) - - assert %Figure{ - axes: %LinePlot{ - title: _title, - coords: %Coords{ - bottom_left: {blx, bly}, - bottom_right: {brx, bry}, - top_right: {trx, ytr}, - top_left: {tlx, tly} - } - } - } = - Lead.set_spines(figure) - - # Check bottom-left corner - assert blx == frame_width * margin + @padding_and_tick_line_space - - assert Float.floor(bly, 7) == - Float.floor(frame_height * margin + @padding_and_tick_line_space, 7) - - # Check bottom-right corner - assert brx == frame_width - frame_width * margin - # + @padding_and_tick_line_space - assert Float.floor(bry, 8) == - Float.floor(frame_height * margin + @padding_and_tick_line_space, 8) - - # Check top-right corner - assert trx == frame_width - frame_width * margin - # + @padding_and_tick_line_space - assert ytr == frame_height - frame_height * margin - @padding - # + @padding_and_tick_line_space - # Check top-left corner - assert tlx == frame_width * margin + @padding_and_tick_line_space - assert tly == frame_height - frame_height * margin - @padding - end - - test "sets coordinates by reducing title height", %{figure: figure} do - frame_height = 6 - - margin = 0 - font_size = 0 - title_font_size = 18 - - figure = - figure - |> Matplotex.figure(%{margin: margin}) - |> Matplotex.set_rc_params( - x_tick_font_size: font_size, - y_tick_font_size: font_size, - title_font_size: title_font_size, - x_label_font_size: font_size, - y_label_font_size: font_size - ) - - assert %Figure{ - axes: %LinePlot{ - coords: %Coords{ - top_left: {_tlx, tly}, - top_right: {_trx, ytr} - } - } - } = - Lead.set_spines(figure) - - assert Float.round(tly, 10) == - Float.round(frame_height - (title_font_size / 150 + 10 / 96), 10) - - # the offset of the title - assert Float.round(ytr, 10) == - Float.round(frame_height - (title_font_size / 150 + 10 / 96), 10) - end - - test "sets coordinates by reducing xlabel", %{figure: figure} do - margin = 0 - font_size = 16 - title_font_size = 0 - - figure = - figure - |> Matplotex.figure(%{margin: margin}) - |> Matplotex.set_rc_params( - x_tick_font_size: 0, - y_tick_font_size: 0, - title_font_size: title_font_size, - x_label_font_size: font_size, - y_label_font_size: 0 - ) - - assert %Figure{ - axes: %LinePlot{ - coords: %Coords{ - bottom_left: {_blx, bly}, - bottom_right: {_brx, bry} - } - } - } = - Lead.set_spines(figure) - - assert Float.round(bly, 10) == - Float.round(font_size / 150 + @padding_and_tick_line_space, 10) - - assert Float.round(bry, 10) == - Float.round(font_size / 150 + @padding_and_tick_line_space, 10) - end - - test "sets coordinates by reducing ylabel", %{figure: figure} do - margin = 0 - font_size = 16 - title_font_size = 0 - - figure = - figure - |> Matplotex.figure(%{margin: margin}) - |> Matplotex.set_rc_params( - x_tick_font_size: 0, - y_tick_font_size: 0, - title_font_size: title_font_size, - x_label_font_size: 0, - y_label_font_size: font_size - ) - - assert %Figure{ - axes: %LinePlot{ - coords: %Coords{ - bottom_left: {blx, _bly}, - top_left: {tlx, _tly} - } - } - } = - Lead.set_spines(figure) - - assert Float.round(tlx, 10) == - Float.round(font_size / 150 + @padding_and_tick_line_space, 10) - - assert Float.round(blx, 10) == - Float.round(font_size / 150 + @padding_and_tick_line_space, 10) - end - - test "set coordinates by reducing xticks", %{figure: figure} do - margin = 0 - font_size = 16 - title_font_size = 0 - - figure = - figure - |> Matplotex.figure(%{margin: margin}) - |> Matplotex.set_rc_params( - x_tick_font_size: font_size, - y_tick_font_size: 0, - title_font_size: title_font_size, - x_label_font_size: 0, - y_label_font_size: 0 - ) - - assert %Figure{ - axes: %LinePlot{ - coords: %Coords{ - bottom_left: {_blx, bly}, - bottom_right: {_brx, bry} - } - } - } = - Lead.set_spines(figure) - - assert bly == font_size / 150 + @padding_and_tick_line_space - assert bry == font_size / 150 + @padding_and_tick_line_space - end - - test "set coordinates by reducing yticks", %{figure: figure} do - margin = 0 - font_size = 16 - title_font_size = 0 - - figure = - figure - |> Matplotex.figure(%{margin: margin}) - |> Matplotex.set_rc_params( - x_tick_font_size: 0, - y_tick_font_size: font_size, - title_font_size: title_font_size, - x_label_font_size: 0, - y_label_font_size: 0 - ) - - assert %Figure{ - axes: %LinePlot{ - coords: %Coords{ - bottom_left: {blx, _bly}, - top_left: {tlx, _tly} - } - } - } = - Lead.set_spines(figure) - - assert tlx == font_size / 150 + @padding_and_tick_line_space - assert blx == font_size / 150 + @padding_and_tick_line_space - end - end - describe "set_regions/1" do test "sets region_xy", %{figure2: figure} do assert %Figure{ From 7b887dc4d4fd1dfd61253d8ae7951c462c9428f7 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 12 Dec 2024 17:42:24 +0530 Subject: [PATCH 13/14] minor fixes --- lib/matplotex/figure/font.ex | 2 ++ lib/matplotex/figure/rc_params.ex | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotex/figure/font.ex b/lib/matplotex/figure/font.ex index ec8d779..e414e73 100644 --- a/lib/matplotex/figure/font.ex +++ b/lib/matplotex/figure/font.ex @@ -9,6 +9,7 @@ defmodule Matplotex.Figure.Font do @text_rotation 0 @flate 0 @dominant_baseline "middle" + @default_text_anchor "middle" defstruct font_size: @default_font_size, font_style: @default_font_style, @@ -19,6 +20,7 @@ defmodule Matplotex.Figure.Font do pt_to_inch_ratio: @pt_to_inch_ratio, rotation: @text_rotation, dominant_baseline: @dominant_baseline, + text_anchor: @default_text_anchor, flate: @flate def font_keys() do diff --git a/lib/matplotex/figure/rc_params.ex b/lib/matplotex/figure/rc_params.ex index 897af08..1f963c2 100644 --- a/lib/matplotex/figure/rc_params.ex +++ b/lib/matplotex/figure/rc_params.ex @@ -20,7 +20,7 @@ defmodule Matplotex.Figure.RcParams do @default_legend_width_percentage 0.2 @default_legend_items_orientation :horizontal defstruct x_tick_font: %Font{dominant_baseline: "middle"}, - y_tick_font: @font, + y_tick_font: %Font{text_anchor: "start"}, x_label_font: @font, y_label_font: @font, title_font: %Font{font_size: @default_title_font_size}, From 30fe4c516ce5394384441bb6e901c6833b3dac51 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 12 Dec 2024 17:46:32 +0530 Subject: [PATCH 14/14] updated test --- test/matplotex/figure/rc_param_test.exs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/matplotex/figure/rc_param_test.exs b/test/matplotex/figure/rc_param_test.exs index f2b01fe..d5c9034 100644 --- a/test/matplotex/figure/rc_param_test.exs +++ b/test/matplotex/figure/rc_param_test.exs @@ -65,7 +65,7 @@ defmodule Matplotex.Figure.RcParamTest do x_label_font: font, y_label_font: font, x_tick_font: font, - y_tick_font: font + y_tick_font: %Font{font | text_anchor: "start"} } == RcParams.update_with_font(rc_params, params) end end @@ -85,7 +85,8 @@ defmodule Matplotex.Figure.RcParamTest do :y_label_unit_of_measurement, :y_label_pt_to_inch_ratio, :y_label_rotation, - :y_label_flate + :y_label_flate, + :y_label_text_anchor ] |> Enum.sort() end