Firefly and multi-hue form work supporting firefly/rocker and for future mcc and similar

ec8490d · gmorell · 2023-01-08 23:01

15 files +264 -10

Files changed

modified README.md
+1 −1
@@ -26,8 +26,8 @@ A Reimplementation of Lambent Aether 4, in Elixir
26 26 - Chaser (Single, ~Multi~)
27 27 - Rocker
28 28 - Scapes
29 −- ##### TODO
30 29 - Firefly
30 +- ##### TODO
31 31 - Growth
32 32 - Twinkles
33 33 - GSI Receiver
added lib/lambent_ex/machine/steps/firefly.ex
+84 −0
@@ -0,0 +1,84 @@
1 +defmodule LambentEx.Machine.Steps.Firefly.Cfg do
2 + # @enforce_keys [:h]
3 + defstruct h: [0, 128], mult: 1000, s: 255, v: 255, id: 0, status: 0
4 +end
5 +
6 +defmodule LambentEx.Machine.Steps.Firefly do
7 + @moduledoc false
8 + use GenServer
9 + # @behaviour LambentEx.Machine.Steps
10 +
11 + @registry :lambent_steps
12 + @cls :firefly
13 +
14 + alias LambentEx.Machine.Steps.Firefly.Cfg
15 + alias LambentEx.Utils.Color
16 +
17 + defp n(opts) do
18 + "#{@cls}-#{opts[:name]}-#{opts[:id]}"
19 + end
20 +
21 + def start_link(opts) do
22 + GenServer.start_link(__MODULE__, opts, name: via_tuple(n(opts)))
23 + end
24 +
25 + def init(opts) do
26 + opts = opts |> Map.put(:status, opts.id)
27 + cfg = struct(%LambentEx.Machine.Steps.Firefly.Cfg{}, opts) |> Map.from_struct()
28 +
29 + {:ok,
30 + cfg
31 + |> Map.put(:active, false)
32 + |> Map.put(:h_curr, cfg[:h] |> Enum.random)
33 + |> Map.put(:v_curr, 0)
34 + |> Map.put(:active_total, 5)
35 + |> Map.put(:active_done, 0)}
36 + end
37 +
38 + defp via_tuple(name) do
39 + {:via, Registry, {@registry, name}}
40 + end
41 +
42 + defp read(state) do
43 + Color.hsv2rgb([state[:h_curr], state[:s], state[:v_curr]])
44 + end
45 +
46 + defp multier(mult), do: [true] ++ for(_i <- 0..mult, do: false)
47 +
48 + defp gen_curr(state) do
49 + case state[:active] do
50 + true ->
51 + state
52 + |> Map.put(:h_curr, Enum.random(state[:h]))
53 + |> Map.put(:active_total, Enum.random(3..7))
54 + |> Map.put(:active_done, 0)
55 +
56 + false ->
57 + state
58 + end
59 + end
60 +
61 + def handle_cast(:step, state) do
62 + state =
63 + case(state[:active]) do
64 + true ->
65 + case state[:active_done] >= state[:active_total] do
66 + true -> state |> Map.put(:active, false)
67 + false -> case state[:v_curr] do
68 + 0 -> state |> Map.put(:v_curr, Enum.random([state[:v], div(state[:v], 2), div(state[:v], 3)]))
69 + otherwise -> state |> Map.put(:v_curr, 0) |> Map.put(:active_done, state[:active_done] + 1)
70 + end
71 + end
72 +
73 + false ->
74 + state
75 + |> Map.put(:active, multier(state[:mult]) |> Enum.random()) |> gen_curr
76 + end
77 +
78 + {:noreply, state}
79 + end
80 +
81 + def handle_call(:read, _from, state) do
82 + {:reply, read(state), state}
83 + end
84 +end
modified lib/lambent_ex/schema/machines.ex
+8 −5
@@ -8,10 +8,10 @@ defmodule LambentEx.Schema.Machines do
8 8 alias LambentEx.Schema.Machines
9 9
10 10 @values %{
11 − gen_solid: "Solid", #
11 + gen_solid: "Solid",
12 12 gen_solid_h: "Solid (HSV)", #
13 − gen_color_cycle: "Color Cycler", #
14 − gen_chaser: "Chaser", #
13 + gen_color_cycle: "Color Cycler",
14 + gen_chaser: "Chaser",
15 15 gen_chaser_h: "Chaser (HSV)", #
16 16 gen_chaser_m: "Multi-Chaser",
17 17 gen_chaser_mh: "Multi-Chaser (HSV)",
@@ -22,7 +22,8 @@ defmodule LambentEx.Schema.Machines do
22 22 gen_rainbow_s: "Rainbow (Solid)", #
23 23 gen_scape: "Scape", #
24 24 gen_scape_s: "Scape (Solid)", #
25 − gen_twinkler: "LA3PortTwinler",
25 + gen_twinkler: "LA3PortTwinkler",
26 + gen_firefly: "Firefly", #
26 27 rcv_fft: "FFT Receiver",
27 28 rcv_gsi: "GSI Receiver",
28 29 rcv_hyp: "Hyperion Receiver"
@@ -40,6 +41,7 @@ defmodule LambentEx.Schema.Machines do
40 41 gen_rocker_s: LambentEx.Machine.Steps.Rocker,
41 42 gen_scape: LambentEx.Machine.Steps.Scape,
42 43 gen_scape_s: LambentEx.Machine.Steps.Scape,
44 + gen_firefly: LambentEx.Machine.Steps.Firefly,
43 45 }
44 46
45 47 embedded_schema do
@@ -58,7 +60,8 @@ defmodule LambentEx.Schema.Machines do
58 60 gen_scape: LambentEx.Schema.Steps.Scape,
59 61 gen_scape_s: LambentEx.Schema.Steps.Scape,
60 62 gen_rainbow: LambentEx.Schema.Steps.Rainbow,
61 − gen_rainbow_s: LambentEx.Schema.Steps.Rainbow
63 + gen_rainbow_s: LambentEx.Schema.Steps.Rainbow,
64 + gen_firefly: LambentEx.Schema.Steps.Firefly,
62 65 ],
63 66 on_type_not_found: :raise,
64 67 on_replace: :update
added lib/lambent_ex/schema/steps/firefly.ex
+20 −0
@@ -0,0 +1,20 @@
1 +defmodule LambentEx.Schema.Steps.Firefly do
2 + use Ecto.Schema
3 + import Ecto.Changeset
4 +
5 + @primary_key false
6 +
7 + embedded_schema do
8 + field :h, {:array, :integer}, default: []
9 + field :h_sel, :integer
10 + field :mult, :integer, default: 1000
11 + field :s, :integer, default: 255
12 + field :v, :integer, default: 255
13 + end
14 +
15 + def changeset(source, params) do
16 + source
17 + |> cast(params, ~w(h h_sel mult s v)a)
18 + |> validate_required([:h, :h_sel, :mult, :s, :v])
19 + end
20 +end
modified lib/lambent_ex_web/live/machines/form_component.ex
+88 −4
@@ -35,8 +35,34 @@ defmodule LambentEx.MachinesLive.FormComponent do
35 35 end
36 36 end
37 37
38 + defp is_multi(v) do
39 + case v do
40 + "gen_firefly" -> true
41 + "gen_rocker" -> true
42 + "gen_rocker_s" -> true
43 + _otherwise -> false
44 + end
45 + end
46 +
38 47 @impl true
39 48 def handle_event("validate", %{"machines" => machine_params}, socket) do
49 + machine_params = case machine_params["type"] do
50 + "gen_firefly" -> case Ecto.Changeset.get_field(socket.assigns.changeset, :class) do
51 + nil -> machine_params
52 + val -> machine_params |> put_in(["class","h"], val |> Map.get(:h))
53 + end
54 + "gen_rocker" -> case Ecto.Changeset.get_field(socket.assigns.changeset, :class) do
55 + nil -> machine_params
56 + val -> machine_params |> put_in(["class","h"], val |> Map.get(:h))
57 + end
58 + "gen_rocker_s" -> case Ecto.Changeset.get_field(socket.assigns.changeset, :class) do
59 + nil -> machine_params
60 + val -> machine_params |> put_in(["class","h"], val |> Map.get(:h))
61 + end
62 + _otherwise -> machine_params
63 + end
64 +
65 + IO.inspect(machine_params)
40 66 # machine_params = case machine_params["type"] do
41 67 # "gen_rocker" -> rocker_params(machine_params)
42 68 # "gen_rocker_s" -> rocker_params(machine_params)
@@ -59,6 +85,7 @@ defmodule LambentEx.MachinesLive.FormComponent do
59 85 :gen_rocker_s -> changeset.changes[:class] |> rocker_keys
60 86 :gen_scape -> changeset.changes[:class] |> scape_keys
61 87 :gen_scape_s -> changeset.changes[:class] |> scape_keys
88 + :gen_firefly -> changeset.changes[:class] |> hue_keys
62 89 _otherwise -> []
63 90 end
64 91 |> IO.inspect()
@@ -90,6 +117,14 @@ defmodule LambentEx.MachinesLive.FormComponent do
90 117 end
91 118
92 119 def handle_event("save", %{"machines" => machines_params}, socket) do
120 + machines_params = case machines_params["type"] do
121 + "gen_firefly" -> machines_params |> put_in(["class","h"], Ecto.Changeset.get_field(socket.assigns.changeset, :class) |> Map.get(:h))
122 + "gen_rocker" -> machines_params |> put_in(["class","h"], Ecto.Changeset.get_field(socket.assigns.changeset, :class) |> Map.get(:h))
123 + "gen_rocker_s" -> machines_params |> put_in(["class","h"], Ecto.Changeset.get_field(socket.assigns.changeset, :class) |> Map.get(:h))
124 +
125 + _otherwise -> machines_params
126 + end
127 +
93 128 case Machines.update_machines(socket.assigns.machine, machines_params) do
94 129 {:ok, _machine} ->
95 130 {:noreply,
@@ -108,9 +143,32 @@ defmodule LambentEx.MachinesLive.FormComponent do
108 143 end
109 144
110 145 def handle_event("add-sel", %{"data" => data}, socket) do
111 − curr_h = Ecto.Changeset.get_change(socket.assigns.changeset, :h) || []
112 − changeset = socket.assigns.changeset |> Ecto.Changeset.change(h: curr_h ++ [data])
113 − {:noreply, socket}
146 + curr_cls = Ecto.Changeset.get_field(socket.assigns.changeset, :class)
147 + curr_h = case curr_cls do
148 + %LambentEx.Schema.Steps.Firefly{h: h} -> h
149 + %LambentEx.Schema.Steps.Rocker{h: h} -> h
150 + cs -> Ecto.Changeset.get_field(cs, :h)
151 + end
152 +
153 + cls_up = case curr_cls do
154 + %LambentEx.Schema.Steps.Firefly{} = x -> x |> Map.put(:h, curr_h ++ [data])
155 + %LambentEx.Schema.Steps.Rocker{} = x -> x |> Map.put(:h, curr_h ++ [data])
156 + cs -> Ecto.Changeset.get_field(socket.assigns.changeset, :class) |> Ecto.Changeset.change(h: curr_h ++ [data])
157 + end
158 +
159 + changeset = socket.assigns.changeset |> Ecto.Changeset.change(class: cls_up) |> Map.put(:action, :validate)
160 + {:noreply, assign(socket, :changeset, changeset)}
161 + end
162 +
163 + def handle_event("add-sel", _params, socket) do
164 + {:noreply, socket |> put_flash(:info, "Please select another hue")}
165 + end
166 +
167 + def handle_event("del-sel", %{"idx" => idx}, socket) do
168 + curr_cls = Ecto.Changeset.get_field(socket.assigns.changeset, :class)
169 + cls_up = curr_cls |> Map.put(:h, curr_cls.h |> List.delete_at(int(idx)))
170 + changeset = socket.assigns.changeset |> Ecto.Changeset.change(class: cls_up) |> Map.put(:action, :validate)
171 + {:noreply, assign(socket, :changeset, changeset)}
114 172 end
115 173
116 174 defp values,
@@ -120,19 +178,45 @@ defmodule LambentEx.MachinesLive.FormComponent do
120 178 (int / 255 * 360) |> round
121 179 end
122 180
181 + def get_sel(values) do
182 + case values |> Map.get(:params) do
183 + %{"h_sel" => sel} -> sel
184 + %{} -> nil
185 + end
186 + end
187 +
188 + defp int(v) when is_binary(v) do
189 + {i, _v} = Integer.parse(v)
190 + i
191 + end
192 + defp int(v) when is_integer(v), do: v
193 +
123 194 defp hue_keys(nil), do: []
124 195
125 − defp hue_keys(%{h: h, s: s, v: v}) do
196 + defp hue_keys(%{h_sel: h, s: s, v: v}) do
126 197 [r, g, b] = LambentEx.Utils.Color.hsv2rgb([h, s, v])
127 198 [r: r, g: g, b: b, h: h]
128 199 end
129 200
201 + defp hue_keys(%{h: h, s: s, v: v}) do
202 + [r, g, b] = LambentEx.Utils.Color.hsv2rgb([h, s, v])
203 + [r: r, g: g, b: b, h: h]
204 + end
130 205
131 206 defp hue_keys(%{h: h, s: s, v: v, h_sel: h_sel}) do
132 207 [r, g, b] = LambentEx.Utils.Color.hsv2rgb([h, s, v])
133 208 [r: r, g: g, b: b, h: h]
134 209 end
135 210
211 + defp hue_keys(%{h: h}) do
212 + [r, g, b] = LambentEx.Utils.Color.hsv2rgb([int(h), 255, 255])
213 + [r: r, g: g, b: b]
214 + end
215 +
216 + defp hrgb([r: r, g: g, b: b]) do
217 + rgb(r,g,b)
218 + end
219 +
136 220 defp rocker_keys(nil), do: [] |> IO.inspect()
137 221
138 222 defp rocker_keys(%{h_sel: h_sel, h: h, s: s, v: v}) do
modified lib/lambent_ex_web/live/machines/form_component.html.heex
+63 −0
@@ -141,6 +141,13 @@
141 141 </btn>
142 142 </div>
143 143 </div>
144 + <div class="flex">
145 + <%= for {h, idx} <- grr.data.h |> Enum.with_index do %>
146 + <btn class="btn" style={"background-color: #{hue_keys(%{h: h}) |> hrgb }"} phx-target={@myself} phx-click="del-sel" phx-value-idx={idx}>
147 + <%= h %>
148 + </btn>
149 + <% end %>
150 + </div>
144 151 <div tabindex="0" class="collapse collapse-arrow border border-base-300 bg-base-100 rounded-box">
145 152 <div class="collapse-title text-xl font-medium">
146 153 Extra Configuration Values
@@ -173,6 +180,18 @@
173 180 </div>
174 181 </div>
175 182 <div class="flex-none text-5xl p-8"><%= @h %></div>
183 + <div class="flex-none py-10">
184 + <btn class="btn btn-accent btn-lg" phx-click="add-sel" phx-target={@myself} phx-value-data={grrs.data.h_sel}>
185 + <i class="fa-solid fa-plus"></i>
186 + </btn>
187 + </div>
188 + </div>
189 + <div class="flex">
190 + <%= for {h, idx} <- grrs.data.h |> Enum.with_index do %>
191 + <btn class="btn" style={"background-color: #{hue_keys(%{h: h}) |> hrgb }"} phx-target={@myself} phx-click="del-sel" phx-value-idx={idx}>
192 + <%= h %>
193 + </btn>
194 + <% end %>
176 195 </div>
177 196 <div tabindex="0" class="collapse collapse-arrow border border-base-300 bg-base-100 rounded-box">
178 197 <div class="collapse-title text-xl font-medium">
@@ -286,6 +305,50 @@
286 305 <% end %>
287 306
288 307
308 + <%= :gen_firefly -> %>
309 + <%= polymorphic_embed_inputs_for f, :class, :gen_firefly, fn gff -> %>
310 + <div class="flex">
311 + <div class="flex-auto">
312 + <.input field={{gff, :h}} type="hidden" value={[]} />
313 + <.input field={{gff, :h_sel}} type="range" min="0" max="255" label="Hue (Selecting)" class="range range-priamry range-lg" />
314 + <div class="w-full flex justify-between text-xs px-2 mb-4" style={"color: #{@rgb}"}>
315 + <span>&#9608;</span>
316 + <span>&#9608;</span>
317 + <span>&#9608;</span>
318 + <span>&#9608;</span>
319 + <span>&#9608;</span>
320 + <span>&#9608;</span>
321 + <span>&#9608;</span>
322 + </div>
323 + </div>
324 + <div class="flex-none text-5xl p-8"><%= @h %></div>
325 + <div class="flex-none py-10">
326 + <btn class="btn btn-accent btn-lg" phx-click="add-sel" phx-target={@myself} phx-value-data={get_sel(gff)}>
327 + <i class="fa-solid fa-plus"></i>
328 + </btn>
329 + </div>
330 + </div>
331 + <div class="flex">
332 + <%= for {h, idx} <- gff.data.h |> Enum.with_index do %>
333 + <btn class="btn" style={"background-color: #{hue_keys(%{h: h}) |> hrgb }"} phx-target={@myself} phx-click="del-sel" phx-value-idx={idx}>
334 + <%= h %>
335 + </btn>
336 + <% end %>
337 + </div>
338 + <div tabindex="0" class="collapse collapse-arrow border border-base-300 bg-base-100 rounded-box">
339 + <div class="collapse-title text-xl font-medium">
340 + Extra Configuration Values
341 + </div>
342 + <div class="collapse-content">
343 + <p>
344 + <.input field={{gff, :s}} type="range" min="0" max="255" label="Saturation" class="range range-priamry range-lg" />
345 + <.input field={{gff, :v}} type="range" min="0" max="255" label="Value" class="range range-priamry range-lg" />
346 + </p>
347 + </div>
348 + </div>
349 + <% end %>
350 +
351 +
289 352 <% end %>
290 353 </div>
291 354 </div>

Parents: ed137c8