Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animation refactor #12

Merged
merged 14 commits into from
Apr 22, 2020
Merged

Conversation

amclain
Copy link
Member

@amclain amclain commented Apr 19, 2020

Related: #8 (comment)

This PR is an attempt at refactoring animations based on my prior comment, #8 (comment). There were a lot of steps taken to get to this point, so if the final diff looks cluttered feel free to review the individual commits to see a cleaner sequence of steps. The goal of this refactor is to push more of the generic animation responsibilities towards Animation and away from the ancillary modules like RGBMatrix and the implementations of animations (like Pinwheel).

A paradigm shift in this PR is to push animations towards a common data model, and formalizes this with the addition of the %Animation{} struct and increased opacity around the modules that implement specific animations. An example of the effects of this change can be seen in RGBMatrix, where animations are initialized and the state changes occur. This new code cares less about which instance of an animation it is working with, and is able to work with animations at a more general level.

defp set_animation(state, animation_type) do
  %State{state | animation: Animation.init_state(animation_type, @pixels)}
end
@impl true
def handle_info(:get_next_state, state) do
  new_animation_state = Animation.next_state(state.animation)

  paint(state.spidev, new_animation_state.pixel_colors)

  Process.send_after(self(), :get_next_state, new_animation_state.delay_ms)

  {:noreply, %State{state | animation: new_animation_state}}
end

And although I wasn't able to completely remove the abstract module.function() calls, I consolidated them into the Animation module, along with the different animation types (formerly Animations.list). This way Animation is responsible for handling all of the internal details of animations, and external consumers of animations work with them in a more opaque manner through the Animation module.

def init_state(animation_type, pixels) do
  animation_type.init_state(pixels)
end
def next_state(animation) do
  animation.type.next_state(animation)
end

I also pulled Animation and all of the implemented animations out of the RGBMatrix namespace, which unless I'm overlooking something, should help with the mindset of having animations somewhat decoupled from the physical hardware. More on this in the Pixels section below.

Future Expansion

This PR is rather complex so I'm reluctant to add additional scope to it, but in the process of this refactor I have identified some other opportunities.

Simplifying the responsibilities of Animation

The %Animation{} struct contains properties like :speed and :delay_ms that look more like the responsibilities of a rendering engine rather than properties of the animation itself. For example, an animation may only need to care about how to change the graphics from one tick to the next, whereas a separate engine could be concerned with how fast/slow an animation plays since any animation should be able to be played faster or slower. If an implementation of an animation needs to speed up or slow down on certain ticks, maybe a speed scaling factor is a better parameter for an %Animation{}, which could then be used as a relative recommendation for how much the render engine should adjust its speed for any given tick.

Pixels

Initializing an Animation with the pixels from the RGBMatrix (animation.ex#L25) feels more tightly coupled than I would like. I haven't thought up a solution to this yet, but wanted to point it out. Maybe the introduction of a render engine like mentioned above could allow animations to operate on an abstract grid, and the engine could apply that abstract grid to the physical grid of pixels in the RGBMatrix.

Introducing a Frame to draw on may be another applicable abstraction between Animation and RGBMatrix (or a render engine).

Animation types

Rather than being hard-coded, the animation types list could be defined in a more flexible manner, like in config.exs. This could allow a developer using Xebow to more easily disable animations they don't want in their project, or extend the animations via libraries rather than solely having to rely on contributing animations upstream.


With all that being said, I'm curious to hear thoughts on this PR.

lib/xebow/rgb_matrix.ex Outdated Show resolved Hide resolved
lib/xebow/rgb_matrix.ex Outdated Show resolved Hide resolved
lib/xebow/rgb_matrix.ex Outdated Show resolved Hide resolved
lib/xebow/animation.ex Show resolved Hide resolved
lib/xebow/rgb_matrix/animation.ex Outdated Show resolved Hide resolved
@amclain amclain self-assigned this Apr 21, 2020
@amclain amclain force-pushed the animation-refactor branch from f680daa to 34bb0d2 Compare April 22, 2020 03:38
doughsay
doughsay previously approved these changes Apr 22, 2020
Copy link
Member

@doughsay doughsay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@amclain amclain force-pushed the animation-refactor branch from 34bb0d2 to 1648338 Compare April 22, 2020 04:21
doughsay
doughsay previously approved these changes Apr 22, 2020
@amclain
Copy link
Member Author

amclain commented Apr 22, 2020

Force-merging through the stuck tests. They passed on my fork here:
https://github.com/amclain/xebow/actions/runs/84440785

@amclain amclain merged commit a68721d into nerves-keyboard:master Apr 22, 2020
@amclain amclain deleted the animation-refactor branch April 22, 2020 05:44
@amclain amclain mentioned this pull request May 1, 2020
@amclain amclain mentioned this pull request May 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants