Created
July 2, 2015 16:24
-
-
Save adam19691026/5968da15a47e05a3f50b to your computer and use it in GitHub Desktop.
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
/* | |
* water_torture.hpp | |
* | |
* Created on: Feb 12, 2013 | |
* Author: danny | |
*/ | |
#ifndef WATER_TORTURE_HPP_ | |
#define WATER_TORTURE_HPP_ | |
#include "ws2811.h" | |
#include <util/delay.h> | |
namespace water_torture | |
{ | |
using ws2811::rgb; | |
/// very crude pseudo random generator | |
uint16_t my_rand() | |
{ | |
static uint16_t state; | |
return state += 33203; // adding a prime number | |
} | |
uint8_t mult( uint8_t value, uint16_t multiplier) | |
{ | |
return (static_cast<uint16_t>( value) * multiplier) >> 8; | |
} | |
/// This class maintains the state and calculates the animations to render a falling water droplet | |
/// Objects of this class can have three states: | |
/// - inactive: this object does nothing | |
/// - swelling: the droplet is at the top of the led strip and swells in intensity | |
/// - falling: the droplet falls downwards and accelerates | |
/// - bouncing: the droplet has bounced of the ground. A smaller, less intensive droplet bounces up | |
/// while a part of the drop remains on the ground. | |
/// After going through the swelling, falling and bouncing phases, the droplet automatically returns to the | |
/// inactive state. | |
template<typename buffer_type> | |
class droplet | |
{ | |
public: | |
droplet( const rgb &color, uint16_t gravity) | |
:color( color), position(0), speed(0), | |
gravity( gravity), state(swelling) | |
{} | |
droplet() | |
:color(0,0,0), position(0), speed(0), | |
gravity(0), state( inactive) | |
{ | |
} | |
/// calculate the next step in the animation for this droplet | |
void step() | |
{ | |
static uint8_t maxpos = | |
ws2811::led_buffer_traits<buffer_type>::count - 1; | |
if (state == falling || state == bouncing) | |
{ | |
position += speed; | |
speed += gravity; | |
// if we hit the bottom... | |
const uint16_t maxpos16 = maxpos << 8; | |
if (position > maxpos16) | |
{ | |
if (state == bouncing) | |
{ | |
// this is the second collision, | |
// deactivate. | |
state = inactive; | |
} | |
else | |
{ | |
// reverse direction and dampen the speed | |
position = maxpos16 - (position - maxpos16); | |
speed = -speed/4; | |
color = scale( color, 10); | |
state = bouncing; | |
} | |
} | |
} | |
else if (state == swelling) | |
{ | |
++position; | |
if ( color.blue <= 10 || color.blue - position <= 10) | |
{ | |
state = falling; | |
position = 0; | |
} | |
} | |
} | |
/// perform one step and draw. | |
void step( buffer_type &leds) | |
{ | |
step(); | |
draw( leds); | |
} | |
/// Draw the droplet on the led string | |
/// This will "smear" the light of this droplet between two leds. The closer | |
/// the droplets position is to that of a particular led, the brighter that | |
/// led will be | |
void draw( buffer_type &leds) | |
{ | |
static uint8_t max_pos = | |
ws2811::led_buffer_traits<buffer_type>::count - 1; | |
if (state == falling || state == bouncing) | |
{ | |
uint8_t position8 = position >> 8; | |
uint8_t remainder = position; // get the lower bits | |
add_clipped_to( get( leds, position8), scale( color, 256 - remainder )); | |
if (remainder) | |
{ | |
add_clipped_to( get( leds, position8+1), scale( color, remainder)); | |
} | |
if (state == bouncing) | |
{ | |
add_clipped_to( get( leds, max_pos), color); | |
} | |
} | |
else if (state == swelling) | |
{ | |
add_clipped_to( get( leds, 0), scale( color, position)); | |
} | |
} | |
bool is_active() const | |
{ | |
return state != inactive; | |
} | |
private: | |
/// Add two numbers and clip the result at 255. | |
static uint8_t add_clipped( uint16_t left, uint16_t right) | |
{ | |
uint16_t result = left + right; | |
if (result > 255) result = 255; | |
return result; | |
} | |
/// Add the right rgb value to the left one, clipping if necessary | |
static void add_clipped_to( rgb &left, const rgb &right) | |
{ | |
left.red = add_clipped(left.red, right.red); | |
left.green = add_clipped( left.green, right.green); | |
left.blue = add_clipped( left.blue, right.blue); | |
} | |
/// multiply an 8-bit value with an 8.8 bit fixed point number. | |
/// multiplier should not be higher than 1.00 (or 256). | |
static uint8_t mult( uint8_t value, uint16_t multiplier) | |
{ | |
return (static_cast<uint16_t>( value) * multiplier) >> 8; | |
} | |
/// scale an rgb value up or down. amplitude > 256 means scaling up, while | |
/// amplitude < 256 means scaling down. | |
static rgb scale(rgb value, uint16_t amplitude) | |
{ | |
return rgb( | |
mult( value.red, amplitude), | |
mult( value.green, amplitude), | |
mult( value.blue, amplitude) | |
); | |
} | |
// how much of a color is left when colliding with the floor, value | |
// between 0 and 256 where 256 means no loss. | |
static const uint16_t collision_scaling = 40; | |
rgb color; | |
uint16_t position; | |
int16_t speed; | |
uint16_t gravity; | |
enum stateval { | |
inactive, | |
swelling, | |
falling, | |
bouncing | |
}; | |
stateval state; | |
}; | |
uint8_t debugcount = 0; | |
volatile uint16_t random_scale() | |
{ | |
return (my_rand() % 256); | |
} | |
template< typename buffer_type> | |
void create_random_droplet( droplet<buffer_type> &d) | |
{ | |
d = droplet<buffer_type>( | |
rgb( | |
mult( 100 ,random_scale()), | |
mult( 100, random_scale()), | |
mult(255, random_scale()) | |
), 5); | |
} | |
template<bool assertion> | |
struct static_assert_ {}; | |
template<> | |
struct static_assert_<true> | |
{ | |
static void is_true(){}; | |
}; | |
/// Create the complete water torture animation. | |
/// This will render droplets at random intervals, up to a given maximum number of droplets. | |
/// The maximum led count is 256 | |
template< uint8_t droplet_count = 2, typename buffer_type> | |
void animate( buffer_type &leds, uint8_t channel) | |
{ | |
static const uint16_t led_count = ws2811::led_buffer_traits<buffer_type>::count; | |
// if you get an error that 'is_true' is not a member of static_assert_, you're probably using | |
// more than 255 leds, which doesn't work for this function. | |
static_assert_< led_count <= 255>::is_true(); | |
typedef droplet<buffer_type> droplet_type; | |
droplet_type droplets[droplet_count]; // droplets that can animate simultaneously. | |
uint8_t current_droplet = 0; // index of the next droplet to be created | |
uint8_t droplet_pause = 1; // how long to wait for the next one | |
for(;;) | |
{ | |
if (droplet_pause) | |
{ | |
--droplet_pause; | |
} | |
else | |
{ | |
if (!droplets[current_droplet].is_active()) | |
{ | |
create_random_droplet( droplets[current_droplet]); | |
++current_droplet; | |
if (current_droplet >= droplet_count) current_droplet = 0; | |
droplet_pause = 100 + my_rand() % 80; | |
} | |
} | |
clear( leds); | |
for (uint8_t idx = 0; idx < droplet_count; ++idx) | |
{ | |
droplets[idx].step( leds); | |
} | |
send( leds, channel); | |
_delay_ms( 7); | |
} | |
} | |
} | |
#endif /* WATER_TORTURE_HPP_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment