-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from amclain/animation-refactor
Animation refactor
- Loading branch information
Showing
12 changed files
with
234 additions
and
181 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
defmodule Xebow.Animation do | ||
alias Xebow.RGBMatrix | ||
|
||
@callback init_state(pixels :: list(RGBMatrix.pixel())) :: t | ||
@callback next_state(animation :: t) :: t | ||
|
||
@type t :: %__MODULE__{ | ||
type: type, | ||
tick: non_neg_integer, | ||
speed: non_neg_integer, | ||
delay_ms: non_neg_integer, | ||
pixels: list(RGBMatrix.pixel()), | ||
pixel_colors: list(RGBMatrix.pixel_color()) | ||
} | ||
defstruct [:type, :tick, :speed, :delay_ms, :pixels, :pixel_colors] | ||
|
||
# Helpers for implementing animations. | ||
defmacro __using__(_) do | ||
quote do | ||
alias Xebow.Animation | ||
|
||
@behaviour Animation | ||
|
||
@impl true | ||
def init_state(pixels) do | ||
init_state_from_defaults(__MODULE__, pixels) | ||
end | ||
|
||
# Increment the animation state to the next tick. | ||
@spec do_tick(animation :: Animation.t()) :: Animation.t() | ||
defp do_tick(animation) do | ||
%Animation{animation | tick: animation.tick + 1} | ||
end | ||
|
||
# Initialize an `Animation` struct with default values. | ||
# Defaults can be overridden by passing the corresponding keyword as `opts`. | ||
@spec init_state_from_defaults( | ||
animation_type :: Animation.type(), | ||
pixels :: list(RGBMatrix.pixel()), | ||
opts :: list(keyword) | ||
) :: Animation.t() | ||
defp init_state_from_defaults(animation_type, pixels, opts \\ []) do | ||
%Animation{ | ||
type: animation_type, | ||
tick: opts[:tick] || 0, | ||
speed: opts[:speed] || 100, | ||
delay_ms: opts[:delay_ms] || 17, | ||
pixels: pixels, | ||
pixel_colors: opts[:pixel_colors] || init_pixel_colors(pixels) | ||
} | ||
end | ||
|
||
# Initialize a list of default pixel colors. | ||
# The default sets all pixels to be turned off ("black"). | ||
@spec init_pixel_colors(pixels :: list(RGBMatrix.pixel())) :: list(RGBMatrix.pixel_color()) | ||
defp init_pixel_colors(pixels) do | ||
Enum.map(pixels, fn _pixel -> Chameleon.HSV.new(0, 0, 0) end) | ||
end | ||
|
||
defoverridable init_state: 1 | ||
end | ||
end | ||
|
||
@type type :: | ||
__MODULE__.CycleAll | ||
| __MODULE__.CycleLeftToRight | ||
| __MODULE__.Pinwheel | ||
|
||
@doc """ | ||
Returns a list of the available types of animations. | ||
""" | ||
@spec types :: list(type) | ||
def types do | ||
[ | ||
__MODULE__.CycleAll, | ||
__MODULE__.CycleLeftToRight, | ||
__MODULE__.Pinwheel | ||
] | ||
end | ||
|
||
@doc """ | ||
Returns an animation set to its initial state. | ||
""" | ||
@spec init_state(animation_type :: type, pixels :: list(RGBMatrix.pixel())) :: t | ||
def init_state(animation_type, pixels) do | ||
animation_type.init_state(pixels) | ||
end | ||
|
||
@doc """ | ||
Returns the next state of an animation based on its current state. | ||
""" | ||
@spec next_state(animation :: t) :: t | ||
def next_state(animation) do | ||
animation.type.next_state(animation) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
defmodule Xebow.Animation.CycleAll do | ||
@moduledoc """ | ||
Cycles hue of all keys. | ||
""" | ||
|
||
alias Chameleon.HSV | ||
|
||
alias Xebow.Animation | ||
|
||
import Xebow.Utils, only: [mod: 2] | ||
|
||
use Animation | ||
|
||
@impl true | ||
def next_state(animation) do | ||
%Animation{tick: tick, speed: speed, pixels: pixels} = animation | ||
time = div(tick * speed, 100) | ||
|
||
hue = mod(time, 360) | ||
color = HSV.new(hue, 100, 100) | ||
|
||
pixel_colors = Enum.map(pixels, fn {_x, _y} -> color end) | ||
|
||
%Animation{animation | pixel_colors: pixel_colors} | ||
|> do_tick() | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
defmodule Xebow.Animation.CycleLeftToRight do | ||
@moduledoc """ | ||
Cycles hue left to right. | ||
""" | ||
|
||
alias Chameleon.HSV | ||
|
||
alias Xebow.Animation | ||
|
||
import Xebow.Utils, only: [mod: 2] | ||
|
||
use Animation | ||
|
||
@impl true | ||
def next_state(animation) do | ||
%Animation{tick: tick, speed: speed, pixels: pixels} = animation | ||
time = div(tick * speed, 100) | ||
|
||
pixel_colors = | ||
for {x, _y} <- pixels do | ||
hue = mod(x * 10 - time, 360) | ||
HSV.new(hue, 100, 100) | ||
end | ||
|
||
%Animation{animation | pixel_colors: pixel_colors} | ||
|> do_tick() | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
defmodule Xebow.Animation.Pinwheel do | ||
@moduledoc """ | ||
Cycles hue in a pinwheel pattern. | ||
""" | ||
|
||
alias Chameleon.HSV | ||
|
||
alias Xebow.Animation | ||
|
||
import Xebow.Utils, only: [mod: 2] | ||
|
||
use Animation | ||
|
||
@center %{ | ||
x: 1, | ||
y: 1.5 | ||
} | ||
|
||
@impl true | ||
def next_state(animation) do | ||
%Animation{tick: tick, speed: speed, pixels: pixels} = animation | ||
time = div(tick * speed, 100) | ||
|
||
pixel_colors = | ||
for {x, y} <- pixels do | ||
dx = x - @center.x | ||
dy = y - @center.y | ||
|
||
hue = mod(atan2_8(dy, dx) + time, 360) | ||
|
||
HSV.new(hue, 100, 100) | ||
end | ||
|
||
%Animation{animation | pixel_colors: pixel_colors} | ||
|> do_tick() | ||
end | ||
|
||
defp atan2_8(x, y) do | ||
atan = :math.atan2(x, y) | ||
|
||
trunc((atan + :math.pi()) * 359 / (2 * :math.pi())) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.