neiam
/lambent_ex
Lambent Aether 5
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
Firefly and multi-hue form work supporting firefly/rocker and for future mcc and similar
ec8490d · gmorell · 2023-01-08 23:01
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>█</span> | |
| 316 | + <span>█</span> | |
| 317 | + <span>█</span> | |
| 318 | + <span>█</span> | |
| 319 | + <span>█</span> | |
| 320 | + <span>█</span> | |
| 321 | + <span>█</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