diff --git a/lib/matplotex/figure/areal/bar_chart.ex b/lib/matplotex/figure/areal/bar_chart.ex index 4fe6cfb..7dfa848 100644 --- a/lib/matplotex/figure/areal/bar_chart.ex +++ b/lib/matplotex/figure/areal/bar_chart.ex @@ -1,5 +1,6 @@ defmodule Matplotex.Figure.Areal.BarChart do import Matplotex.Figure.Numer + alias Matplotex.Figure.Region alias Matplotex.Figure.Dataset alias Matplotex.Element.Rect alias Matplotex.Figure.RcParams @@ -7,14 +8,18 @@ defmodule Matplotex.Figure.Areal.BarChart do alias Matplotex.Figure alias Matplotex.Figure.Areal use Areal - frame( legend: %Legend{}, coords: %Coords{}, dimension: %Dimension{}, tick: %TwoD{}, limit: %TwoD{}, - label: %TwoD{} + label: %TwoD{}, + region_x: %Region{}, + region_y: %Region{}, + region_title: %Region{}, + region_legend: %Region{}, + region_content: %Region{} ) @impl Areal diff --git a/lib/matplotex/figure/areal/line_plot.ex b/lib/matplotex/figure/areal/line_plot.ex index e1c6497..ee52330 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.Figure.Region alias Matplotex.Figure.Areal.Ticker alias Matplotex.Figure.Marker alias Matplotex.Figure.Dataset @@ -16,7 +17,12 @@ defmodule Matplotex.Figure.Areal.LinePlot do dimension: %Dimension{}, tick: %TwoD{}, limit: %TwoD{}, - label: %TwoD{} + label: %TwoD{}, + region_x: %Region{}, + region_y: %Region{}, + region_title: %Region{}, + region_legend: %Region{}, + region_content: %Region{} ) @marker_size 3.5 diff --git a/lib/matplotex/figure/areal/scatter.ex b/lib/matplotex/figure/areal/scatter.ex index 164d1e1..83dcf42 100644 --- a/lib/matplotex/figure/areal/scatter.ex +++ b/lib/matplotex/figure/areal/scatter.ex @@ -1,4 +1,5 @@ defmodule Matplotex.Figure.Areal.Scatter do + alias Matplotex.Figure.Region alias Matplotex.Figure.Areal.Ticker alias Matplotex.Figure.Marker alias Matplotex.Figure.Dataset @@ -17,7 +18,12 @@ defmodule Matplotex.Figure.Areal.Scatter do dimension: %Dimension{}, tick: %TwoD{}, limit: %TwoD{}, - label: %TwoD{} + label: %TwoD{}, + region_x: %Region{}, + region_y: %Region{}, + region_title: %Region{}, + region_legend: %Region{}, + region_content: %Region{} ) @impl Areal diff --git a/lib/matplotex/figure/region.ex b/lib/matplotex/figure/region.ex new file mode 100644 index 0000000..55f37a1 --- /dev/null +++ b/lib/matplotex/figure/region.ex @@ -0,0 +1,3 @@ +defmodule Matplotex.Figure.Region do + defstruct [:x, :y, :width, :height, :name, :theta] +end diff --git a/lib/matplotex/pie_chart/content.ex b/lib/matplotex/pie_chart/content.ex deleted file mode 100644 index 9eb17f5..0000000 --- a/lib/matplotex/pie_chart/content.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule Matplotex.PieChart.Content do - @type t() :: %__MODULE__{} - - @labels_default false - @legends_default true - defstruct [ - :width, - :height, - :radius, - :cx, - :cy, - :x1, - :y1, - :color_palette, - :stoke_width, - :legend_frame, - labels: @labels_default, - legends: @legends_default - ] - - defmodule LegendFrame do - defstruct [:x, :y, :uheight, :uwidth] - end -end diff --git a/lib/matplotex/pie_chart/element.ex b/lib/matplotex/pie_chart/element.ex deleted file mode 100644 index fa54747..0000000 --- a/lib/matplotex/pie_chart/element.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule Matplotex.PieChart.Element do - defstruct [:slices, :labels, :legends] -end diff --git a/lib/matplotex/pie_chart/generate_svg.ex b/lib/matplotex/pie_chart/generate_svg.ex deleted file mode 100644 index 4188720..0000000 --- a/lib/matplotex/pie_chart/generate_svg.ex +++ /dev/null @@ -1,48 +0,0 @@ -defmodule Matplotex.PieChart.GenerateSvg do - alias Matplotex.PieChart.Legend - alias Matplotex.PieChart - alias Matplotex.Utils.Svg - alias Matplotex.Element.Stencil - alias Matplotex.PieChart.Element - - @spec generate(PieChart.t(), String.t()) :: String.t() - def generate(chartset, pre_svg) do - {chartset.element, pre_svg} - |> add_element(:slices) - |> add_element(:labels) - |> add_element(:legends) - |> then(fn {_, svg} -> Svg.wrap_with_frame(chartset.size, svg) end) - end - - defp add_element({%Element{slices: slices} = element, svg}, :slices) do - slices = - "#{for slice <- slices do - Stencil.slice(slice) - end}" - - {element, svg <> slices} - end - - defp add_element({%Element{labels: labels} = element, svg}, :labels) do - labels = - " #{for label <- labels do - Stencil.label(label) - end} - " - - {element, svg <> labels} - end - - defp add_element({%Element{legends: legends} = element, svg}, :legends) do - legends = - " #{for legend <- legends do - legend = Legend.generate_label(legend) - Stencil.legend(legend) - end} - " - - {element, svg <> legends} - end - - defp add_element(element, _), do: element -end diff --git a/lib/matplotex/pie_chart/legend.ex b/lib/matplotex/pie_chart/legend.ex deleted file mode 100644 index 25a1c5f..0000000 --- a/lib/matplotex/pie_chart/legend.ex +++ /dev/null @@ -1,25 +0,0 @@ -defmodule Matplotex.PieChart.Legend do - alias Matplotex.Element.Label - - @stroke_width 1 - @stroke "rgba(0,0,0,0)" - @legend_size 20 - @label_type "legend.label" - defstruct [ - :x, - :y, - :color, - :label, - width: @legend_size, - height: @legend_size, - label_margin: @legend_size, - stroke: @stroke, - stroke_width: @stroke_width - ] - - def generate_label( - %__MODULE__{label: text, x: x, y: y, width: width, label_margin: label_margin} = legend - ) do - %{legend | label: %Label{x: x + width + label_margin, y: y, text: text, type: @label_type}} - end -end diff --git a/lib/matplotex/pie_chart/plot.ex b/lib/matplotex/pie_chart/plot.ex deleted file mode 100644 index 1fccc8d..0000000 --- a/lib/matplotex/pie_chart/plot.ex +++ /dev/null @@ -1,217 +0,0 @@ -defmodule Matplotex.PieChart.Plot do - alias Matplotex.PieChart.GenerateSvg - alias Matplotex.PieChart.Element - alias Matplotex.PieChart.Legend - - alias Matplotex.PieChart.Slice - alias Matplotex.PieChart.Content - alias Content.LegendFrame - alias Matplotex.PieChart - - use Matplotex.Blueprint - @default_size 400 - @width_key "width" - @height_key "height" - @label_key "labels" - @content_params [:color_palette, :legends, :stroke_width] - @full_circle 2 * :math.pi() - @label_roatetion_radius_percentage 0.2 - @slice_label_type "label.slice" - @start_angle -:math.pi() / 2 - - defmodule SliceAcc do - defstruct [:slices, :datasum, :legend_frame, :x1, :y1, :legends, :labels, :start_angle] - end - - @impl true - @spec new(module(), PieChart.params()) :: {PieChart.t(), map()} - def new(module, params) do - # Fields are dataset, title, size, margin, valid, element, type - # type: pie chart, dataset from params combination of data and labels, - {params, content_params} = - params - |> validate_params() - |> generate_chart_params() - |> segregate_content(@content_params) - - {struct(module, params), content_params} - end - - @impl true - @spec set_content({PieChart.t(), map()}) :: - PieChart.t() - def set_content( - {%PieChart{size: %{width: width, height: height}, margin: margin, dataset: dataset} = - graphset, content_params} - ) do - content = struct(%Content{}, content_params) - c_width = width - margin * 2 - c_height = height - margin * 2 - radius = c_height / 2 - legend_uheight = c_height / length(dataset) - legend_frame = %LegendFrame{x: c_height + 2 * margin, y: margin, uheight: legend_uheight} - cx = radius + margin - cy = height - (radius + margin) - y1 = margin - - %PieChart{ - graphset - | content: %Content{ - content - | width: c_width, - height: c_height, - radius: radius, - cx: cx, - cy: cy, - x1: cx, - y1: y1, - legend_frame: legend_frame - } - } - end - - @impl true - def add_elements(graphset) do - # adding elements means adding slices and legends and labels - # dataset to its_percentage and slices - # generate angle - %__MODULE__.SliceAcc{slices: slices, labels: labels, legends: legends} = - generate_slices(graphset) - - %PieChart{graphset | element: %Element{slices: slices, labels: labels, legends: legends}} - end - - @impl true - def generate_svg(%PieChart{} = chartset) do - GenerateSvg.generate(chartset, "") - end - - defp generate_slices(%PieChart{ - dataset: dataset, - label: label, - content: %Content{ - radius: radius, - x1: x1, - y1: y1, - cx: cx, - cy: cy, - color_palette: color_palette, - legend_frame: legend_frame, - labels: labels_turned_on?, - legends: legends_turned_on? - } - }) do - # cx and cy are the center - # will calculate the angle by percentage and take the xn and yn by using R.cos(angle*pie/180) and R.sign(angle*pie/180) - total = Enum.sum(dataset) - - datacombo = label |> Enum.zip(color_palette) |> Enum.zip(dataset) - - Enum.reduce( - datacombo, - %__MODULE__.SliceAcc{ - slices: [], - labels: [], - legends: [], - x1: x1, - y1: y1, - legend_frame: legend_frame, - start_angle: @start_angle - }, - fn {{label, color}, data}, - %__MODULE__.SliceAcc{ - slices: slices, - labels: labels, - legends: legends, - x1: x1, - y1: y1, - start_angle: start_angle, - legend_frame: - %LegendFrame{x: legend_x, y: legend_y, uheight: legend_uheight} = legend_frame - } = acc -> - # TODO - The slices are getting bit moved to some angle at first and the rest is getting right, look like some rotational issues re write the logic with rotating properly - angle = data / total * @full_circle + start_angle - cos = :math.cos(angle) - sin = :math.sin(angle) - x2 = cx + radius * cos - y2 = cy + radius * sin - - label_rotation_radius = radius * @label_roatetion_radius_percentage - # TODO: give clear rotation for labels also - label_roatation_angle = angle + angle / 2 - lcos = :math.cos(label_roatation_angle) - lsin = :math.sin(label_roatation_angle) - lx = cx + label_rotation_radius * lcos - ly = cy + label_rotation_radius * lsin - label_rotation_angle_degrees = label_roatation_angle * (180 / :math.pi()) - - # rot_max = MathFunc.homogeneous_transformation_matrix(:math.pi()/2 , 0, 0 ) - - %__MODULE__.SliceAcc{ - acc - | slices: - slices ++ - [ - %Slice{ - x1: x1, - y1: y1, - x2: x2, - y2: y2, - radius: radius, - data: data, - color: color, - cx: cx, - cy: cy - } - ], - labels: - add_labels( - labels, - {lx, ly, label, data, label_rotation_angle_degrees}, - labels_turned_on? - ), - legends: - add_legends(legends, {legend_x, legend_y, color, label, data}, legends_turned_on?), - legend_frame: %LegendFrame{legend_frame | x: legend_x, y: legend_y + legend_uheight}, - x1: x2, - y1: y2, - start_angle: angle - } - end - ) - end - - defp add_labels( - labels, - {lx, ly, label, data, label_rotation_angle_degrees}, - _labels_turned_on = true - ) do - labels ++ - [ - %Label{ - x: lx, - y: ly, - text: "#{label}-#{data}", - type: @slice_label_type, - rotate: label_rotation_angle_degrees - } - ] - end - - defp add_labels(labels, _, _), do: labels - - defp add_legends(legends, {legend_x, legend_y, color, label, data}, _legends_turned_on = true) do - legends ++ [%Legend{x: legend_x, y: legend_y, color: color, label: "#{label}-#{data}"}] - end - - defp add_legends(legends, _, _), do: legends - - defp generate_chart_params({:ok, params}) do - {width, params} = Map.pop(params, @width_key, @default_size) - {height, params} = Map.pop(params, @height_key, @default_size) - {labels, params} = Map.pop(params, @label_key) - size = %{width: width, height: height} - params = for {k, v} <- params, into: %{}, do: {String.to_atom(k), v} - Map.merge(params, %{size: size, label: labels}) - end -end diff --git a/lib/matplotex/pie_chart/slice.ex b/lib/matplotex/pie_chart/slice.ex deleted file mode 100644 index 4dadd98..0000000 --- a/lib/matplotex/pie_chart/slice.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule Matplotex.PieChart.Slice do - defstruct [:x1, :y1, :x2, :y2, :cx, :cy, :data, :radius, :color, :label, :legend] -end diff --git a/test/matplotex/figure/areal/line_plot_test.exs b/test/matplotex/figure/areal/line_plot_test.exs index 7e33019..6eadc75 100644 --- a/test/matplotex/figure/areal/line_plot_test.exs +++ b/test/matplotex/figure/areal/line_plot_test.exs @@ -58,4 +58,14 @@ defmodule Matplotex.Figure.Areal.LinePlotTest do assert Enum.any?(element, fn line -> line.fill == "green" && line.linestyle == "-." end) end end + + describe "create/2" do + test "created with all regions", %{figure: figure} do + assert Map.has_key?(figure.axes, :region_x) + assert Map.has_key?(figure.axes, :region_y) + assert Map.has_key?(figure.axes, :region_title) + assert Map.has_key?(figure.axes, :region_legend) + assert Map.has_key?(figure.axes, :region_content) + end + end end diff --git a/test/matplotex/pie_chart_test.exs b/test/matplotex/pie_chart_test.exs deleted file mode 100644 index b6e0d33..0000000 --- a/test/matplotex/pie_chart_test.exs +++ /dev/null @@ -1,66 +0,0 @@ -defmodule Matplotex.PieChartTest do - alias Matplotex.PieChart.Element - alias Matplotex.PieChart.Content - use Matplotex.PlotCase, async: true - alias Matplotex.PieChart - - setup do - params = %{ - "id" => "chart-container", - "dataset" => [280, 45, 133, 152, 278, 221, 56], - "labels" => ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], - "color_palette" => ["#f66", "pink", "orange", "gray", "#fcc", "green", "#0f0"], - "width" => 600, - "height" => 400, - "margin" => 15, - "legends" => true - } - - {pie_chart, content_params} = PieChart.Plot.new(PieChart, params) - chartset_with_content = PieChart.Plot.set_content({pie_chart, content_params}) - chartset_with_element = PieChart.Plot.add_elements(chartset_with_content) - - {:ok, - %{ - params: params, - content_params: content_params, - chartset: pie_chart, - chartset_with_content: chartset_with_content, - chartset_with_elements: chartset_with_element - }} - end - - describe "new/1" do - test "return pie_chart set and content params", %{params: params} do - assert {pie_chart, content_params} = PieChart.Plot.new(PieChart, params) - assert pie_chart.id == Map.get(params, "id") - assert content_params.legends - end - end - - describe "set_content/1" do - test "will return a piechart with content", %{ - content_params: content_params, - chartset: chartset - } do - assert %PieChart{content: %Content{}} = - PieChart.Plot.set_content({chartset, content_params}) - end - end - - describe "add_elements/1" do - test "will return a chartset with elements", %{chartset_with_content: chartset} do - assert %PieChart{element: %Element{slices: slices}} = PieChart.Plot.add_elements(chartset) - assert length(slices) > 0 - end - end - - describe "generate_svg/1" do - test "will generate svg by a chartset with valid elements", %{ - chartset_with_elements: chartset - } do - svg = PieChart.Plot.generate_svg(chartset) - assert is_binary(svg) - end - end -end