From f678ae5497aad0886e430f9945c4d0d3bc096747 Mon Sep 17 00:00:00 2001 From: KingStarsRobot Date: Sun, 17 Feb 2019 09:11:46 +0000 Subject: [PATCH] mx is a minimal set of functions for working with serpentine arrays amend: one branch missed in test --- lib/mx.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/core.js | 38 ++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 lib/mx.js diff --git a/lib/mx.js b/lib/mx.js new file mode 100644 index 0000000..81cbdab --- /dev/null +++ b/lib/mx.js @@ -0,0 +1,97 @@ +/* Jake & Bobby Pearse 2018. + +j.t.pearse@gmail.com + +Using a string of lights as a 2d Array + +A single strip can be treated as a serpentine array. + +We can treat a single strip of lights as a serpentine array by correcting the address of pixels on odd numbered columns + +Regular 2d array serpentine array + + |0|5|10| |0|9|10| + |1|6|11| |1|8|11| + |2|7|12| Need to |2|7|12| + |3|8|13| === correct odd ===> |3|6|13| + |4|9|14| numbered columns |4|5|14| + +NOTE: if for some mad reason you want to start with pixel 0 at the bottom, you should reverse EVEN numbered rows :) + +As we can see in a sane world, column 1 is reversed. As are all odd-numbered columns. +address will calculate the corrected value for pixels. + +examples +======== + + get a corrected pixel number + ============================ + + const NUM_ROWS = 5 + + address(6,NUM_ROWS) + => 8 + + + light up all pixels on row 1 of strip + ===================================== + + for (const p = 0; p < strip.length; p++){ + if (rowNum(p,NUM_ROWS) === 1){ + strip.pixel(address(p,ROW_NUM)).color('white') + } + } + +*/ +const abs = Math.abs +const floor = Math.floor + +// return the uncorrected row number of pixel p +const rowNum = (p, rows) => p % rows + +// return the uncorrected column number of pixel p +const columnNum = (p, rows) => floor(p/rows) + +// find the corrected pixel number at the top of a reversed column +const rowZeroVal = (p,rows) => { + + const columnHalf = floor(columnNum(p,rows)/2)+1 + const doubleRows = rows * 2 + return (columnHalf * doubleRows) - 1 +} + +// pixel p is in an odd numbered column => true || false +const columnIsOdd = (p, rows) => columnNum(p, rows) % 2 > 0 + +//corrected value of pixel p, for odd numbered columns +const address = (p, rows) => { + + if (columnIsOdd(p, rows)) { + return p + abs( p - rowZeroVal(p,rows)) - rowNum(p,rows) + } + return p +} + + +// check if a pixel is inside a bounding rectangle +function inRange(p,first,last,rows){ + return ( + rowNum(p,rows) >= rowNum(first,rows) && + rowNum(p,rows) <= rowNum(last,rows) && + columnNum(p,rows) >= columnNum(first,rows) && + columnNum(p,rows) <= columnNum(last,rows) + ) +} + +// reduce a 2d array with a bounding rectangle +const rectRange = (first, last, rows, pixelArray) => pixelArray.filter(p => inRange(p, first, last, rows)) + +module.exports = { + address, + columnNum, + rowZeroVal, + columnIsOdd, + rowNum, + inRange, + rectRange +} diff --git a/test/core.js b/test/core.js index 9f94015..5f3e207 100644 --- a/test/core.js +++ b/test/core.js @@ -8,6 +8,8 @@ var sinon = require("sinon"); var five = require("johnny-five"); var pixel = require("../lib/pixel.js"); +var mx = require("../lib/mx"); + var Board = five.Board; function newBoard() { @@ -360,3 +362,39 @@ exports["Pixel"] = { test.done(); }, }; + exports["mx"] = { + rowNum : function(test) { + test.equals(3,mx.rowNum(13,5),"In a grid with 5 rows, pixel 13 is in row 3"); + test.done(); + }, + colNum : function(test) { + test.equals(2,mx.columnNum(13,5), "in a grid with 5 rows, pixel 13 is in column 2"); + test.done(); + }, + rowZeroVal : function(test) { + // pixel 5 will be in column 1 + test.equals(9, mx.rowZeroVal(5,5), "in a grid with 5 rows pixel 9 is at the top of column 1"); + test.done(); + }, + columnIsOdd : function(test) { + test.equals(true,mx.columnIsOdd(5,5),"in a grid with 5 rows, pixel five is in an odd-numbered column"); + test.equals(false,mx.columnIsOdd(11,5), "in a grid with 5 rows, pixel 11 is not in an odd-numbered column"); + test.done(); + }, + address: function(test) { + test.equals(9,mx.address(5,5),"in a grid with 5 rows pixel 5 should map to pixel 9"); + test.equals(4,mx.address(4,5),"in a grid with 5 rows pixel 4 should not be altered") + test.done(); + }, + inRange: function(test) { + test.equals(true,mx.inRange(8,2,13,5),"in a grid of 5 rows pixel 8 is in the range of 2,13"); + test.equals(false,mx.inRange(6,2,13,5),"in a grid of 5 rows pixel 6 is not in range 2,13"); + test.done(); + }, + rectRange : function(test){ + const grid = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14] + const rec = [2,3,7,8,12,13]; + test.deepEqual(rec,mx.rectRange(2,13,5,grid),"in a grid of 5 rows return all pixel values which lie inside a rectangle bounded by 2 and 13") + test.done(); + } +};