Skip to content

Commit 7da35b9

Browse files
committed
Propagate palette gamut and ceiling between tonal and gamut tabs
1 parent e0f2285 commit 7da35b9

4 files changed

Lines changed: 115 additions & 13 deletions

File tree

lib/color/palette/visualizer.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@ defmodule Color.Palette.Visualizer do
197197
gamuts: gamuts,
198198
planckian: truthy?(Map.get(params, "planckian")),
199199
overlay_seed: checkbox_default(params, "overlay_seed", true),
200-
overlay_palette: checkbox_default(params, "overlay_palette", true)
200+
overlay_palette: checkbox_default(params, "overlay_palette", true),
201+
palette_gamut: gamut_atom(Map.get(params, "palette_gamut")) || :SRGB,
202+
palette_chroma_ceiling: number_default(Map.get(params, "palette_chroma_ceiling"), 1.0)
201203
}
202204
end
203205

lib/color/palette/visualizer/gamut_view.ex

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,39 @@ defmodule Color.Palette.Visualizer.GamutView do
1313
{:ProPhoto, "ProPhoto RGB", "#f43f5e", false}
1414
]
1515

16+
# Palette-gamut dropdown options — mirrors TonalView so the
17+
# gamut view can reproduce the same tonal palette the Tonal tab
18+
# would generate.
19+
@palette_gamut_options [
20+
{:SRGB, "sRGB"},
21+
{:P3_D65, "P3 (D65)"},
22+
{:Rec2020, "Rec2020"},
23+
{:Adobe, "Adobe RGB"},
24+
{:ProPhoto, "ProPhoto"}
25+
]
26+
1627
def render(params, base) do
1728
projection = Map.get(params, :projection, :uv)
1829
enabled = Map.get(params, :gamuts, default_gamuts())
1930
show_planck = Map.get(params, :planckian, true)
2031
overlay_seed = Map.get(params, :overlay_seed, true)
2132
overlay_palette = Map.get(params, :overlay_palette, false)
33+
palette_gamut = Map.get(params, :palette_gamut, :SRGB)
34+
palette_chroma_ceiling = Map.get(params, :palette_chroma_ceiling, 1.0)
2235
seed = Map.get(params, :seed, "#3b82f6")
2336

2437
body =
2538
try do
26-
success_body(projection, enabled, show_planck, overlay_seed, overlay_palette, seed)
39+
success_body(
40+
projection,
41+
enabled,
42+
show_planck,
43+
overlay_seed,
44+
overlay_palette,
45+
seed,
46+
palette_gamut,
47+
palette_chroma_ceiling
48+
)
2749
rescue
2850
e ->
2951
["<div class=\"vz-error\">", Render.escape(Exception.message(e)), "</div>"]
@@ -35,15 +57,38 @@ defmodule Color.Palette.Visualizer.GamutView do
3557
seed: seed,
3658
body: body,
3759
base: base,
38-
extra_fields: extra_fields(projection, enabled, show_planck, overlay_seed, overlay_palette)
60+
extra_fields:
61+
extra_fields(
62+
projection,
63+
enabled,
64+
show_planck,
65+
overlay_seed,
66+
overlay_palette,
67+
palette_gamut,
68+
palette_chroma_ceiling
69+
),
70+
tab_params: %{
71+
"tonal" => %{
72+
"gamut" => Atom.to_string(palette_gamut),
73+
"chroma_ceiling" => format_ceiling(palette_chroma_ceiling)
74+
}
75+
}
3976
)
4077
end
4178

4279
defp default_gamuts do
4380
for {atom, _label, _colour, default?} <- @working_spaces, default?, do: atom
4481
end
4582

46-
defp extra_fields(projection, enabled, show_planck, overlay_seed, overlay_palette) do
83+
defp extra_fields(
84+
projection,
85+
enabled,
86+
show_planck,
87+
overlay_seed,
88+
overlay_palette,
89+
palette_gamut,
90+
palette_chroma_ceiling
91+
) do
4792
[
4893
"<label>proj <select name=\"projection\">",
4994
option("uv", "u′v′ (CIE 1976)", projection == :uv),
@@ -73,10 +118,24 @@ defmodule Color.Palette.Visualizer.GamutView do
73118
"<label><input type=\"checkbox\" name=\"overlay_palette\" value=\"1\"",
74119
if(overlay_palette, do: " checked", else: ""),
75120
"> Plot tonal palette</label>",
121+
"<label>palette gamut <select name=\"palette_gamut\">",
122+
Enum.map(@palette_gamut_options, fn {atom, label} ->
123+
selected = if atom == palette_gamut, do: " selected", else: ""
124+
["<option value=\"", Atom.to_string(atom), "\"", selected, ">", label, "</option>"]
125+
end),
126+
"</select></label>",
127+
"<label>palette ceiling <input type=\"number\" name=\"palette_chroma_ceiling\"",
128+
" min=\"0.1\" max=\"1.0\" step=\"0.05\" value=\"",
129+
format_ceiling(palette_chroma_ceiling),
130+
"\"></label>",
76131
"<input type=\"hidden\" name=\"submitted\" value=\"1\">"
77132
]
78133
end
79134

135+
defp format_ceiling(value) when is_float(value), do: :erlang.float_to_binary(value, decimals: 2)
136+
defp format_ceiling(value) when is_integer(value), do: Integer.to_string(value)
137+
defp format_ceiling(value), do: to_string(value)
138+
80139
defp option(value, label, selected?) do
81140
[
82141
"<option value=\"",
@@ -89,8 +148,20 @@ defmodule Color.Palette.Visualizer.GamutView do
89148
]
90149
end
91150

92-
defp success_body(projection, enabled, show_planck, overlay_seed, overlay_palette, seed) do
93-
palette = if overlay_palette, do: safe_tonal(seed), else: nil
151+
defp success_body(
152+
projection,
153+
enabled,
154+
show_planck,
155+
overlay_seed,
156+
overlay_palette,
157+
seed,
158+
palette_gamut,
159+
palette_chroma_ceiling
160+
) do
161+
palette =
162+
if overlay_palette,
163+
do: safe_tonal(seed, palette_gamut, palette_chroma_ceiling),
164+
else: nil
94165

95166
svg_binary =
96167
Color.Gamut.SVG.render(
@@ -120,8 +191,8 @@ defmodule Color.Palette.Visualizer.GamutView do
120191
]
121192
end
122193

123-
defp safe_tonal(seed) do
124-
Color.Palette.Tonal.new(seed)
194+
defp safe_tonal(seed, gamut, chroma_ceiling) do
195+
Color.Palette.Tonal.new(seed, gamut: gamut, chroma_ceiling: chroma_ceiling)
125196
rescue
126197
_ -> nil
127198
end

lib/color/palette/visualizer/render.ex

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ defmodule Color.Palette.Visualizer.Render do
3030
error = Keyword.get(assigns, :error)
3131
base = Keyword.fetch!(assigns, :base)
3232
extra_fields = Keyword.get(assigns, :extra_fields, [])
33+
tab_params = Keyword.get(assigns, :tab_params, %{})
3334

3435
[
3536
"<!doctype html><html lang=\"en\"><head>",
@@ -42,7 +43,7 @@ defmodule Color.Palette.Visualizer.Render do
4243
escape(base),
4344
"/assets/style.css\">",
4445
"</head><body>",
45-
header(active, seed, base, extra_fields),
46+
header(active, seed, base, extra_fields, tab_params),
4647
"<main class=\"vz-main\">",
4748
error_block(error),
4849
body,
@@ -51,7 +52,7 @@ defmodule Color.Palette.Visualizer.Render do
5152
]
5253
end
5354

54-
defp header(active, seed, base, extra_fields) do
55+
defp header(active, seed, base, extra_fields, tab_params) do
5556
tabs = [
5657
{"tonal", "Tonal"},
5758
{"theme", "Theme"},
@@ -73,14 +74,15 @@ defmodule Color.Palette.Visualizer.Render do
7374
"<nav class=\"vz-tabs\">",
7475
Enum.map(tabs, fn {path, label} ->
7576
cls = if path == active, do: "active", else: ""
77+
extras = Map.get(tab_params, path, %{})
78+
query = tab_query_string(seed, extras)
7679

7780
[
7881
"<a href=\"",
7982
escape(base),
8083
"/",
8184
path,
82-
"?seed=",
83-
URI.encode_www_form(seed),
85+
query,
8486
"\" class=\"",
8587
cls,
8688
"\">",
@@ -109,6 +111,27 @@ defmodule Color.Palette.Visualizer.Render do
109111
]
110112
end
111113

114+
# Builds the `?seed=…&k=v&…` query string appended to each tab's
115+
# nav link. The seed is always included; callers can supply
116+
# per-tab extras (e.g., palette_gamut) via the `tab_params` map
117+
# on `Render.page/1`.
118+
defp tab_query_string(seed, extras) when extras == %{} do
119+
"?seed=" <> URI.encode_www_form(seed)
120+
end
121+
122+
defp tab_query_string(seed, extras) do
123+
base = "?seed=" <> URI.encode_www_form(seed)
124+
125+
extra_pairs =
126+
extras
127+
|> Enum.map(fn {k, v} ->
128+
URI.encode_www_form(to_string(k)) <> "=" <> URI.encode_www_form(to_string(v))
129+
end)
130+
|> Enum.join("&")
131+
132+
base <> "&" <> extra_pairs
133+
end
134+
112135
@doc """
113136
Resolves any colour input to a 7-char hex string suitable for
114137
`<input type="color">`. Falls back to `default` if parsing

lib/color/palette/visualizer/tonal_view.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ defmodule Color.Palette.Visualizer.TonalView do
3333
body: body,
3434
error: error,
3535
base: base,
36-
extra_fields: extra_fields(hue_drift?, gamut, chroma_ceiling)
36+
extra_fields: extra_fields(hue_drift?, gamut, chroma_ceiling),
37+
tab_params: %{
38+
"gamut" => %{
39+
"palette_gamut" => Atom.to_string(gamut),
40+
"palette_chroma_ceiling" => format_ceiling(chroma_ceiling)
41+
}
42+
}
3743
)
3844
end
3945

0 commit comments

Comments
 (0)