part 2 of 2
// Wave positions and colors are updated...
for(w=0; w<N_WAVES; w++) {
wave[w].center += wave[w].speed; // Move wave; wraps around ends, is OK!
if(wave[w].hue == wave[w].hueTarget) { // Hue not currently changing?
// There's a tiny random chance of picking a new hue...
if(!random(FPS * 4)) {
// Within 1/3 color wheel
wave[w].hueTarget = random(wave[w].hue - 30, wave[w].hue + 30);
}
} else { // This wave's hue is currently shifting...
if(wave[w].hue < wave[w].hueTarget) wave[w].hue++; // Move up or
else wave[w].hue--; // down as needed
if(wave[w].hue == wave[w].hueTarget) { // Reached destination?
wave[w].hue = 90 + wave[w].hue % 90; // Clamp to 90-180 range
wave[w].hueTarget = wave[w].hue; // Copy to target
}
wave[w].r = h2rgb(wave[w].hue - 30);
wave[w].g = h2rgb(wave[w].hue);
wave[w].b = h2rgb(wave[w].hue + 30);
}
}
// Now render the LED strip using the current brightness & wave states
for(i=0; i<NUM_LEDS; i++) { // Each LED in strip is visited just once...
// Transform 'i' (LED number in pixel space) to the equivalent point
// in 8-bit fixed-point space (0-255). "* 256" because that would be
// the start of the (N+1)th pixel. "+ 127" to get pixel center.
x = (i * 256 + 127) / CIRCUMFERENCE;
r = g = b = 0; // LED assumed off, but wave colors will add up here
for(w=0; w<N_WAVES; w++) { // For each item in wave[] array...
// Calculate distance from pixel center to wave center point,
// using both signed and unsigned 8-bit integers...
d1 = abs((int8_t)x - (int8_t)wave[w].center);
d2 = abs((uint8_t)x - (uint8_t)wave[w].center);
// Then take the lesser of the two, resulting in a distance (0-128)
// that 'wraps around' the ends of the strip as necessary...it's a
// contiguous ring, and waves can move smoothly across the gap.
if(d2 < d1) d1 = d2; // d1 is pixel-to-wave-center distance
if(d1 < wave[w].width) { // Is distance within wave's influence?
d2 = wave[w].width - d1; // d2 is opposite; distance to wave's end
// d2 distance, relative to wave width, is then proportional to the
// wave's brightness at this pixel (basic linear y=mx+b stuff).
// Normally this would require a fraction -- floating-point math --
// but by reordering the operations we can get the same result with
// integers -- fixed-point math -- that's why brightness is cast
// here to a 16-bit type; the interim result of the multiplication
// is a big integer that's then divided by wave width (back to an
// 8-bit value) to yield the pixel's brightness. This massive wall
// of comments is basically to explain that fixed-point math is
// faster and less resource-intensive on processors with limited
// capabilities. Topic for another Adafruit Learning System guide?
y = (uint16_t)brightness * d2 / wave[w].width; // 0 to 200
// y is a brightness scale value -- proportional to, but not
// exactly equal to, the resulting RGB value. Values from 0-127
// represent a color ramp from black to the wave's assigned RGB
// color. Values from 128-255 ramp from the RGB color to white.
// It's by design that y only goes to 200...white peaks then occur
// only when certain waves overlap.
if(y < 128) { // Fade black to RGB color
// In HSV colorspace, this would be tweaking 'value'
n = (uint16_t)y * 2 + 1; // 1-256
r += (wave[w].r * n) >> 8; // More fixed-point math
g += (wave[w].g * n) >> 8; // Wave color is scaled by 'n'
b += (wave[w].b * n) >> 8; // >>8 is equiv to /256
} else { // Fade RGB color to white
// In HSV colorspace, this would be tweaking 'saturation'
n = (uint16_t)(y - 128) * 2; // 0-255 affects white level
m = 256 * n;
n = 256 - n; // 1-256 affects RGB level
r += (m + wave[w].r * n) >> 8;
g += (m + wave[w].g * n) >> 8;
b += (m + wave[w].b * n) >> 8;
}
}
}
// r,g,b are 16-bit types that accumulate brightness from all waves
// that affect this pixel; may exceed 255. Now clip to 0-255 range:
if(r > 255) r = 255;
if(g > 255) g = 255;
if(b > 255) b = 255;
// Store resulting RGB value and we're done with this pixel!
strip.setPixelColor(i, r, g, b);
}
// Once rendering is complete, a second pass is made through pixel data
// applying gamma correction, for more perceptually linear colors.
// https://learn.adafruit.com/led-tricks-gamma-correction
uint8_t *pixels = strip.getPixels(); // Pointer to LED strip buffer
for(i=0; i<NUM_LEDS*3; i++) pixels[i] = pgm_read_byte(&gamma[pixels[i]]);
}
EMPTY_INTERRUPT(PCINT0_vect); // Pin change (does nothing, but required)
// COLOR-HANDLING CODE -----------------------------------------------------
// A full HSV-to-RGB function wasn't required by sketch, just needed limited
// hue-to-RGB. There are 90 distinct hues (0-89) around color wheel (to
// allow 4-bit table entries). This function gets called three times (for
// R,G,B, with different offsets relative to hue) to produce a fully-
// saturated color. Was a little more compact than a full HSV function.
static const uint8_t PROGMEM hueTable[45] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xED,0xCB,0xA9,0x87,0x65,0x43,0x21,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
uint8_t h2rgb(uint8_t hue) {
hue %= 90;
uint8_t h = pgm_read_byte(&hueTable[hue >> 1]);
return ((hue & 1) ? (h & 15) : (h >> 4)) * 17;
}
// Gamma-correction table (see earlier comments). It's big and ugly
// and would interrupt trying to read the code, so I put it down here.
const uint8_t gamma[] PROGMEM = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };