Skip to content

Commit a69f6de

Browse files
Merge pull request #46 from BigThinkcode/colorscheme
Color Scheme
2 parents c3bc5d9 + 28b93ca commit a69f6de

File tree

20 files changed

+529
-28
lines changed

20 files changed

+529
-28
lines changed

lib/matplotex.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ defmodule Matplotex do
161161
162162
163163
"""
164+
alias Matplotex.Figure.Areal.PlotOptions
164165
alias Matplotex.Figure.Areal.Spline
165166
alias Matplotex.Figure.Areal.Histogram
166167
alias Matplotex.InputError
@@ -745,6 +746,10 @@ defmodule Matplotex do
745746
Figure.show_legend(figure)
746747
end
747748

749+
def set_options(figure, opts) do
750+
PlotOptions.set_options_in_figure(figure, opts)
751+
end
752+
748753
@doc """
749754
Function to update rc params, rc stands for runtime configuration
750755
## Examples

lib/matplotex/blueprint/frame.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ defmodule Matplotex.Blueprint.Frame do
100100
show_x_ticks: @show_by_default,
101101
show_y_ticks: @show_by_default,
102102
show_ticks: @show_by_default,
103-
border: nil
103+
border: nil,
104+
cmap: nil
104105
]
105106
|> Keyword.merge(opts)
106107
)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
defmodule Matplotex.Colorscheme.Blender do
2+
@moduledoc false
3+
alias Matplotex.Colorscheme.Rgb
4+
5+
@rgb_fields [:red, :green, :blue, :alpha]
6+
7+
@type color :: %Rgb{
8+
red: :float,
9+
green: :float,
10+
blue: :float,
11+
alpha: :float
12+
}
13+
14+
15+
def mix(color1, color2, weight \\ 0.5) do
16+
17+
p = weight
18+
w = p * 2 - 1
19+
a = color1.alpha - color2.alpha
20+
21+
w1 = (if(w * a == -1, do: w, else: (w + a) / (1 + w * a)) + 1) / 2.0
22+
w2 = 1 - w1
23+
24+
[r, g, b] =
25+
[:red, :green, :blue]
26+
|> Enum.map(fn key ->
27+
get_attribute(color1, key) * w1 + get_attribute(color2, key) * w2
28+
end)
29+
30+
alpha = get_alpha(color1) * p + get_alpha(color2) * (1 - p)
31+
rgb(r, g, b, alpha)
32+
end
33+
defdelegate rgb(red, green, blue), to: Rgb
34+
defdelegate rgb(red, green, blue, alpha), to: Rgb
35+
36+
37+
@doc """
38+
Gets the `:red` property of the color.
39+
"""
40+
@spec get_red(color) :: float
41+
def get_red(color), do: get_attribute(color, :red)
42+
43+
@doc """
44+
Gets the `:green` property of the color.
45+
"""
46+
@spec get_green(color) :: float
47+
def get_green(color), do: get_attribute(color, :green)
48+
49+
@doc """
50+
Gets the `:blue` property of the color.
51+
"""
52+
@spec get_blue(color) :: float
53+
def get_blue(color), do: get_attribute(color, :blue)
54+
55+
@doc """
56+
Gets the `:hue` property of the color.
57+
"""
58+
@spec get_hue(color) :: float
59+
def get_hue(color), do: get_attribute(color, :hue)
60+
61+
@doc """
62+
Gets the `:saturation` property of the color.
63+
"""
64+
@spec get_saturation(color) :: float
65+
def get_saturation(color), do: get_attribute(color, :saturation)
66+
67+
@doc """
68+
Gets the `:lightness` property of the color.
69+
"""
70+
@spec get_lightness(color) :: float
71+
def get_lightness(color), do: get_attribute(color, :lightness)
72+
73+
@doc """
74+
Gets the `:alpha` property of the color.
75+
"""
76+
@spec get_alpha(color) :: float
77+
def get_alpha(color), do: get_attribute(color, :alpha)
78+
79+
@doc """
80+
Get's any color attribute from the color.
81+
"""
82+
@spec get_attribute(color, atom()) :: float
83+
def get_attribute(color, key) do
84+
color
85+
|> cast_color_by_attribute(key)
86+
|> Map.fetch!(key)
87+
end
88+
89+
defp cast_color_by_attribute(color, attribute) when attribute in @rgb_fields, do: color
90+
91+
92+
end
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
defmodule Matplotex.Colorscheme.Colormap do
2+
@moduledoc false
3+
defstruct [:color, :offset, opacity: 1]
4+
def viridis do
5+
["#fde725","#21918c","#3b528b","#440154"]
6+
end
7+
8+
def plasma do
9+
["#F7E425","#ED6925", "#9C179E", "#0C0786" ]
10+
end
11+
12+
def inferno do
13+
["#FCFFA4","#F56C3E","#B12A90","#000004"]
14+
end
15+
16+
def magma do
17+
["#FCFDBF","#FB8861", "#B73779", "#000004"]
18+
end
19+
20+
def fetch_cmap(cmap) when is_binary(cmap), do: cmap |> String.to_atom() |> fetch_cmap()
21+
22+
def fetch_cmap(cmap) do
23+
apply(__MODULE__, cmap, []) |> make_colormap()
24+
end
25+
def make_colormap(colors) do
26+
size = length(colors)
27+
colors
28+
|> Enum.with_index()
29+
|> Enum.map(&colormap(&1, size))
30+
end
31+
32+
def default_cmap(), do: viridis()
33+
34+
defp colormap({color, idx}, size) do
35+
offset = idx / size * 100
36+
%__MODULE__{color: color, offset: offset}
37+
end
38+
39+
40+
end
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
defmodule Matplotex.Colorscheme.Garner do
2+
@moduledoc false
3+
alias Matplotex.Colorscheme.Rgb
4+
alias Matplotex.Colorscheme.Blender
5+
alias Matplotex.InputError
6+
7+
defstruct [:range, :color_cue, :cmap, :preceeding, :minor, :major, :final]
8+
9+
def garn_color({min, max} = range, point, cmap) when max != min do
10+
cue = (point - min) / (max - min)
11+
cmap
12+
|> make_from_cmap()
13+
|> put_range(range, cue)
14+
|> point_color()
15+
end
16+
17+
defp make_from_cmap(cmap) do
18+
cmap
19+
|>to_rgb()
20+
|>place_edges()
21+
end
22+
23+
defp put_range(%__MODULE__{} = garner, range, cue) do
24+
%__MODULE__{garner | range: range, color_cue: cue}
25+
end
26+
27+
defp to_rgb(color_map) do
28+
Enum.map(color_map, &Rgb.from_cmap!(&1))
29+
end
30+
31+
defp place_edges([preceeding, minor, major,final]) do
32+
%__MODULE__{preceeding: preceeding.color, minor: minor.color, major: major.color, final: final.color}
33+
end
34+
defp place_edges(_) do
35+
raise InputError, message: "Invalid colormap"
36+
end
37+
38+
defp point_color(%__MODULE__{color_cue: cue, preceeding: preceeding, minor: minor}) when cue < minor do
39+
minor|> Blender.mix(preceeding, cue)|> Rgb.to_string()
40+
end
41+
42+
defp point_color(%__MODULE__{color_cue: cue, minor: minor, major: major}) when cue < major do
43+
major|> Blender.mix(minor, cue)|> Rgb.to_string()
44+
end
45+
46+
defp point_color(%__MODULE__{color_cue: cue, major: major, final: final}) when cue >= major do
47+
final|> Blender.mix(major)|> Rgb.to_string()
48+
end
49+
end

lib/matplotex/colorscheme/rgb.ex

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
defmodule Matplotex.Colorscheme.Rgb do
2+
@moduledoc false
3+
alias Matplotex.Colorscheme.Colormap
4+
defstruct [
5+
red: 0.0, # 0-255
6+
green: 0.0, # 0-255
7+
blue: 0.0, # 0-255
8+
alpha: 1.0 # 0-1
9+
]
10+
11+
def rgb(red, green, blue, alpha\\1.0)
12+
def rgb({red, :percent}, {green, :percent}, {blue, :percent}, alpha) do
13+
rgb(red * 255, green * 255, blue * 255, alpha)
14+
end
15+
def rgb(red, green, blue, alpha) do
16+
%__MODULE__{
17+
red: cast(red, :red),
18+
green: cast(green, :green),
19+
blue: cast(blue, :blue),
20+
alpha: cast(alpha, :alpha)
21+
}
22+
end
23+
24+
def to_string(struct, type\\nil)
25+
26+
def to_string(struct, :nil) do
27+
type = case struct.alpha do
28+
1.0 -> :hex
29+
_ -> :rgba
30+
end
31+
to_string(struct, type)
32+
end
33+
34+
def to_string(%__MODULE__{red: r, green: g, blue: b, alpha: alpha}, :rgba) do
35+
"rgba(#{round(r)}, #{round(g)}, #{round(b)}, #{alpha})"
36+
end
37+
def to_string(%__MODULE__{red: r, green: g, blue: b, alpha: 1.0}, :hex) do
38+
"#" <> to_hex(r) <> to_hex(g) <> to_hex(b)
39+
end
40+
41+
def cast(value, field) when field in [:red, :green, :blue] do
42+
value/1
43+
|> min(255.0)
44+
|> max(0.0)
45+
end
46+
def cast(value, :alpha) do
47+
value/1
48+
|> min(1.0)
49+
|> max(0.0)
50+
end
51+
52+
defp to_hex(value) when is_float(value), do:
53+
to_hex(round(value))
54+
defp to_hex(value) when value < 16, do:
55+
"0" <> Integer.to_string(value, 16)
56+
defp to_hex(value) when is_integer(value), do:
57+
Integer.to_string(value, 16)
58+
59+
def from_hex!(input) do
60+
{:ok, color} = from_hex(input)
61+
color
62+
end
63+
64+
def from_cmap!(%Colormap{color: color} = cmap) do
65+
%Colormap{cmap | color: from_hex!(color) }
66+
end
67+
68+
def from_hex("#" <> <<r :: binary-size(2), g :: binary-size(2), b :: binary-size(2)>>) do
69+
{:ok, rgb(parse_hex(r), parse_hex(g), parse_hex(b))}
70+
end
71+
def from_hex("#" <> <<r :: binary-size(1), g :: binary-size(1), b :: binary-size(1)>>) do
72+
{:ok, rgb(parse_hex(r <> r), parse_hex(g <> g), parse_hex(b <> b))}
73+
end
74+
75+
defp parse_hex(s), do: String.to_integer(s, 16)
76+
77+
78+
end
79+
80+
81+
defimpl String.Chars, for: CssColors.RGB do
82+
def to_string(struct) do
83+
Matplotex.Colorscheme.Rgb.to_string(struct)
84+
end
85+
end

lib/matplotex/element.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ defmodule Matplotex.Element do
1414
quote do
1515
@behaviour Matplotex.Element
1616
import Matplotex.Element, only: [to_pixel: 1]
17+
18+
defimpl String.Chars, for: __MODULE__ do
19+
def to_string(%module{} = element) do
20+
module.assemble(element)
21+
end
22+
end
1723
end
1824
end
1925
end

lib/matplotex/element/cmap.ex

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
defmodule Matplotex.Element.Cmap do
2+
@moduledoc false
3+
alias Matplotex.Element.Rect
4+
alias Matplotex.Element
5+
use Element
6+
7+
defstruct type: "figure.cmap",
8+
id: nil,
9+
cmap: [],
10+
container: %Rect{}
11+
12+
@impl Element
13+
def assemble(element) do
14+
~s(<defs>
15+
<linearGradient id="#{element.id}" x1="0%" y1="0%" x2="0%" y2="100%">
16+
#{for stp <- element.cmap do
17+
tag_stop(stp)
18+
end}
19+
</linearGradient>
20+
</defs>
21+
#{Element.assemble(element.container)})
22+
end
23+
24+
def tag_stop(%{offset: offset, color: color, opacity: opacity}) do
25+
~s(<stop offset="#{offset}%" style="stop-color:#{color};stop-opacity:#{opacity}" />)
26+
end
27+
28+
def color_gradient(%__MODULE__{container: container} = element) do
29+
%__MODULE__{element | container: %{container | color: "url(##{element.id})"}}
30+
end
31+
32+
def get_x(%{x: x}), do: to_pixel(x)
33+
def get_y(%{y: y}), do: to_pixel(y)
34+
def get_width(%{width: width}), do: to_pixel(width)
35+
def get_height(%{height: height}), do: to_pixel(height)
36+
end

0 commit comments

Comments
 (0)