Created
May 14, 2014 17:42
-
-
Save blindman2k/cfaf8cff1da87e6d9c83 to your computer and use it in GitHub Desktop.
This is a new Neopixel class designed specifically for rectangular X/Y coordinate systems but still works fine for linear strips. Note the function names have changed.
This file contains hidden or 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
class NeoPixels { | |
// when instantiated, the neopixel class will fill this array with blobs to | |
// represent the waveforms to send the numbers 0 to 255. This allows the blobs to be | |
// copied in directly, instead of being built for each pixel - which makes the class faster. | |
bits = null; | |
// Like bits, this blob holds the waveform to send the color [0,0,0], to clear pixels faster | |
clearblob = null; | |
// private variables passed into the constructor | |
spi = null; // imp SPI interface (pre-configured) | |
width = null; // the maximum X dimension | |
height = null; // the maximum Y dimension | |
frameSize = null; // number of pixels per frame (height x width) | |
frame = null; // a blob to hold the current frame buffer | |
canvas = null; // 2d array holding the next buffer to be drawn | |
snapshot = null; // holds a copy of the canvas for quick drawing of a background/template | |
cache = null; // holds the hsl2rgb cache | |
// _spi - A configured spi (MSB_FIRST, 7.5MHz) | |
// _width - X pixels wide | |
// _height - Y pixels high | |
constructor(_spi, _width, _height = 1) { | |
// This class uses SPI to emulate the newpixels' one-wire protocol. | |
// This requires one byte per bit to send data at 7.5 MHz via SPI. | |
// These consts define the "waveform" to represent a zero or one | |
const SPICLK = 7500; // kHz | |
const ZERO = 0xC0; | |
const ONE = 0xF8; | |
const BYTESPERPIXEL = 24; | |
spi = _spi; | |
width = _width; | |
height= _height; | |
spi.configure(MSB_FIRST, SPICLK); | |
frameSize = width * height; | |
frame = blob(frameSize*BYTESPERPIXEL + 1); | |
clearblob = blob(BYTESPERPIXEL); | |
cache = {}; | |
// prepare the bits array and the clearblob blob | |
initialize(); | |
// Blank the screen | |
clear(); | |
write(); | |
} | |
// fill the array of representative 1-wire waveforms. | |
// done by the constructor at instantiation. | |
function initialize() { | |
// fill the bits array first | |
bits = array(256); | |
for (local i = 0; i < 256; i++) { | |
local valblob = blob(BYTESPERPIXEL / 3); | |
valblob.writen((i & 0x80) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x40) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x20) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x10) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x08) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x04) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x02) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x01) ? ONE:ZERO,'b'); | |
bits[i] = valblob; | |
} | |
// now fill the clearblob | |
for(local j = 0; j < BYTESPERPIXEL; j++) { | |
clearblob.writen(ZERO, 'b'); | |
} | |
// finally, prepare the canvas | |
canvas = array(width); | |
for (local x = 0; x < width; x++) { | |
canvas[x] = array(height); | |
for (local y = 0; y < height; y++) { | |
canvas[x][y] = null; | |
} | |
} | |
} | |
// draw a single pixel onto the canvas | |
function drawPixel(x, y, color) { // or pdp | |
if (x >= 0 && x < width && y >= 0 && y < height) { | |
canvas[x][y] = color; | |
} | |
} | |
// draw a box or line on the canvas | |
function drawBox(x1, y1, x2, y2, color) { // or pdb | |
// Swap the coordinates to its always drawing uphill | |
if (x2 < x1) { local x3 = x1; x1 = x2; x2 = x3 } | |
if (y2 < y1) { local y3 = y1; y1 = y2; y2 = y3 } | |
for (local x = x1; x <= x2; x++) { | |
for (local y = y1; y <= y2; y++) { | |
if (x >= 0 && x < width && y >= 0 && y < height) { | |
canvas[x][y] = color; | |
} | |
} | |
} | |
} | |
// wipes the canvas to a single colour (or black) | |
function clear(color = null) { // or pcf | |
for (local x = 0; x < width; x++) { | |
for (local y = 0; y < height; y++) { | |
canvas[x][y] = color; | |
} | |
} | |
} | |
// sends the canvas to the neopixels | |
function write() { | |
local color, x, y, yy, alt = true; | |
local fwb = frame.writeblob.bindenv(frame); | |
frame.seek(0); | |
for (x = 0; x < width; x++) { | |
alt = !alt; | |
for (y = 0; y < height; y++) { | |
// Alternate direction of every alternate row | |
yy = alt ? (height - y - 1) : y; | |
color = canvas[x][yy]; | |
if (color) { | |
fwb(bits[color[1]]); | |
fwb(bits[color[0]]); | |
fwb(bits[color[2]]); | |
} else { | |
fwb(clearblob); | |
} | |
} | |
} | |
frame.writen(0, 'b'); // Drive MOSI low | |
// All done. Send. | |
spi.write(frame); | |
} | |
// stores the current canvas for future fast use | |
function storeSnapshot() { | |
snapshot = array(width); | |
for (local x = 0; x < width; x++) { | |
snapshot[x] = clone canvas[x]; | |
} | |
} | |
// restores a snapshot as the primary canvas | |
function restoreSnapshot() { | |
if (snapshot) { | |
for (local x = 0; x < width; x++) { | |
canvas[x] = clone snapshot[x]; | |
} | |
return true; | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Converts an HSL color value to RGB. Conversion formula | |
* adapted from http://en.wikipedia.org/wiki/HSL_color_space. | |
* Assumes h, s, and l are contained in the set [0, 255] and | |
* returns r, g, and b in the set [0, 255]. | |
* | |
* @param Number h The hue | |
* @param Number s The saturation | |
* @param Number l The lightness | |
* @return Array The RGB representation | |
*/ | |
function hsl2rgb(h, s, l, _cache=false) { | |
local cachekey = format("%02X%02X%02X", h, s, l); | |
if (cachekey in cache) return cache[cachekey]; | |
local hue2rgb = function(p, q, t) { | |
if (t < 0.0) t += 1.0; | |
if (t > 1.0) t -= 1.0; | |
if (t < 0.16666666) return p + (q - p) * 6.0 * t; | |
if (t < 0.5) return q; | |
if (t < 0.66666666) return p + (q - p) * (0.66666666 - t) * 6.0; | |
return p; | |
} | |
local r = 0.0; | |
local g = 0.0; | |
local b = 0.0; | |
h /= 255.0; | |
s /= 255.0; | |
l /= 255.0; | |
if (s == 0) { | |
r = g = b = l; // achromatic | |
} else if (l == 0) { | |
r = g = b = l; // off | |
} else { | |
local q = l < 0.5 ? l * (1.0 + s) : l + s - l * s; | |
local p = 2.0 * l - q; | |
r = hue2rgb(p, q, h + 0.33333333); | |
g = hue2rgb(p, q, h); | |
b = hue2rgb(p, q, h - 0.33333333); | |
} | |
local rgb = [math.floor(r*255.0), math.floor(g*255.0), math.floor(b*255.0)]; | |
if (_cache) cache[cachekey] <- rgb; | |
return rgb; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment