Read a Data Series - Control LEDs in a Matrix

Let’s say we want to read a fairly long data series (1000 values or so) to PB to control the number of LEDs “on” at one color in a fully lit matrix of another color.

How best to do that. I’m thinking an array…?

It would be cool to be able to run a subset of values in its own loop. Modes?

Some Notes:

The values do not change all that much between data points but can either go up or down. I’d like to run the values through a loop on variable speed and dwell timers to experiment with the initial effect of traversing through the data.

A trick part (at least looked at superficially) is I’d like to populate the matrix more or less spatially evenly, whether 2D or 3D.

Well dang, that’s a pretty big-seeming ask but it is what I really want to get going on, however best it can be done.

Your suggestions more than welcome! Cheers!

Needs more data. Yes, you could make a big array (unsure if you’d run out of room, there is a limit of available ram. V2 more than V3 PBs.)

The rest is pretty trivial… Divide data size by number of pixels, that’s your limiting factor, really. You can’t display 65 pieces of data with 64 pixels, for example.

If you want to run thru the data, decide on how, as frames of data (so 64 at a time, for example), or move serially thru it, so the first 64, then 2-65, etc…

There is a good example of this sort of idea in the text banner pattern. All letters are essentially mapped out as data. I’d look at it for some example code.

Is there a better way to approach feeding a 1000 piece data string into PB?

The values would be fed in and run serially. The subset “mode” would be, for example the “last 300” values.

I caught this thread - Manually populating arrays?

Do you think this is a good approach?

I figured it would be a kind of lit pixel = current pixel + pixelcount/value kind of thing.

Will check out that text banner code, thanks!

Still waiting on my V3…

Static data? Or how will you update it?

Static data. Just a series of fixed numbers controlling how many pixels are lit with an assigned color in the matrix. All other pixels a second color.

Agreeing with @scruffynerf here – @jeff’s “scrolling text marquee 2D” pattern in the library takes the idea of controlling pixels with an array of data to a logical conclusion. It’s got quite a lot of code and static font data, but it’s not as complex as it looks.

It pretty much does exactly what the character generators on old IBM PCs used to do, moving display memory to scroll and everything. I think it even borrows its font from that era.

Here’s something I did that illustrates a way of using an array to selectively set groups of pixels to a different color (in this case, bright white) over user specified background. No static data here – the pixels to light are generated by random(), but the concept is similar.

// flashes a random number of random pixels on a colored background at
// semi-random intervals.  Flash duration (spark hold) and maximum time
// between flashes (Max Delay) are controlled by sliders.
var frameTime = 0;            // time accumulator

// display mode flag:  true if displaying sparks, false if displaying only
// background color. 
var pix = -1;               

var hue = 0.7;                // background color
var sat = 0.5;
var bri = 0.2;

export var delay = 250;       // lifetime of current pulse
export var hold = 80;         // how long sparks are displayed
export var max_delay = 500;   // maximum delay between pulses 
export var density = 0.06;    // fraction of pixels eligible to be sparks

// array of per-pixel flags. "true" means display a "spark", false means
// display background color.
var sparks = array(pixelCount);  

// delay between spark "pulses" ranges
// from 0 to 2000 ms
export function sliderMaxDelay(v) {
  max_delay = v * 2000;
}

// how long sparks stay visible,
// from 0 to 250 ms
export function sliderSparkHold(v) {
  hold = (v * v) * 250;
}

// percentage of pixels that can "spark" 
// setting to zero gives you background only
export function sliderDensity(v) {
  density = v * v;
}

export function hsvPickerBackgroundColor(h, s, v) {
  hue = h; sat = s; bri = v;
}

export function beforeRender(delta) {
  frameTime += delta;

// if we're in spark mode (pix != 0), check frameTime to see if it's
// time to generate a new list of spark pixels, otherwise, just leave
// the current set of sparks on the screen
  if (pix == -1) {
    if (frameTime > delay) {
      nsparks = floor(random(pixelCount * density));
      for (var i = 0; i < nsparks;i++) {
        sparks[floor(random(1) * pixelCount)] = 1;
      }  
      frameTime = 0;
      pix = 0;
      delay = random(max_delay);
    }
  }
// if we're in background mode (pix == 0) clear out all the sparks
// and set up to just display the background color.
  else if (frameTime > hold) {
    pix = -1;
    for (var i = 0; i < pixelCount;i++) {
      sparks[i] = 0;
    }  
  }
}

// render bright white if it's a spark, background color otherwise
export function render(index) {
  hsv(hue,1-sparks[index],sparks[index] ? 1 : bri);
}
1 Like

Well you’ve given me a whole heaping helping of goodness here, everyone! Thanks! I’ll get back when I’ve modded it some.

Cool… So far…

Creating flags works great, and the following kind of syntax does the job of conditional variation of hue and brightness. Both great tools for the bag -

export function render(index) {
hsv(sparks[index] ? 0.0 : 0.66 , sat , sparks[index] ? 1 : bri )

I am iterating the number on each spark dwell cycle to a maximum then return to start, for now because I cannot figure how to implement a data-series array as input.

I created a test array, “trivial” I know, but, well how to “read” the data in is not at all clear to me at this point. e.g.

export var number = array(vals)

number [0] = 5
number [1] = 50
number [2] = 40
number [3] = 30
number [4] = 20
number [5] = 10

That code for the scrolling marquis IS VERY interesting but the original “message” array ( I’ll call it the feeder") seems to disappear into the code… I cannot clearly see where or how it is actually implemented.

Call this the “Stuck point de jour”. Help?

Hi! Let me first narrate your code. For the sake of understanding it, I think you’re using number[] as synonymous with sparks[].

export var number = array(vals)
number[0] = 5

Create a new array with ‘vals’ number of elements. Since we’ll use it in render in a sec, we want to be sure vals is at least as big as our strip, so it should maybe be var number array(pixelCount). It’s exported, so we can see it in the watcher or set values over websockets.

We also set some values by index.

export function render(index) {
hsv(number[index] ? 0.0 : 0.66 , sat , number[index] ? 1 : bri )

Compute each pixel. If the corresponding entry in the matrix is zero, the hue will red (0) and the brightness will be full-on (1). Otherwise, if the array has a nonzero entry in this pixel index, the hue will be full blue (.66) and the brightness will be set to bri, which is presumably set elsewhere, perhaps in the global code or with a UI slider.

OK - that seems like a data-series (array) driven input to render. Where else are you stuck?


Jumping to the Scrolling Text Marquee pattern, here’s the point where the message-of-characters array (message) is used.

Context: This is called when a timer tells us it’s time to shift the message over by one column. That might involve loading the next character, or reloading the same char we’re only partway through.

function loadNextCol() {
  charIndex = message[floor(messageColPointer / charCols)]
  fetchCharacter(charIndex) // loads global `character` with ASCII charIndex

charIndex: The index (0-based address in the array) of the character (like “A”)'s ascii value (like 65). So, if the message was “HA!” then the array would be ["H", "A", "!"] and charIndex of “A” would be 1.

messageColPointer: The index of an array that stores all bits for columns for the message. Since the font has 8 columns in a character (charCols == 8), messageColPointer for “HA!” goes from 0 to 23. A messageColPointer of 8 would be the leftmost column of pixels for the A in “HA!”

Keep up the good work learning arrays in PB. They unlock a lot of goodness.

3 Likes

Just for illustrative purposes, here’s a short pattern that takes the number of “foreground” pixels to light from an array, and displays them spread more-or-less evenly across a strip. It cycles through data array entries at a pace controlled by the “Speed” slider, and starts back at the beginning of the data when it reaches the end.

var frameTime = 10000;        // time accumulator
var pixelInterval = 0;        // interval (in pixels) between lit pixels.
     
var hBackground = 0.6667;        // background color
var sBackground = 1;
var vBackground = 0.3;

var hForeground = 0.05;        // foreground color
var sForeground = 1; 
var vForeground = 0.8;

var DATA_SIZE = 500;          // storage for our input data stream
var data = array(DATA_SIZE);
var data_index = 0;            // array index of "active" data item
var next_lit_pixel = 0;         // position of next pixel to be lit
var holdTime = 8;               // how long we display each data array entry

// initialize data here...
// data[0] = 5; data[1] = 200; etc...
// here's a fake initialization loop for testing that ramps the number of lit
// pixels up and down between 0 and pixelCount;
for (var i = 0; i < DATA_SIZE; i++) {
  data[i] = floor(pixelCount * triangle(i/(DATA_SIZE-1)));
}

// how long does it take to cycle through the data 
// array?  (from 0 to 8 seconds)
export function sliderSpeed(v) {
  holdTime = (v * 8000) / DATA_SIZE;
}

export function beforeRender(delta) {
  frameTime += delta;
  next_lit_pixel = 0;  
  
  if (frameTime > holdTime) {
    pixelInterval = pixelCount/data[data_index];
    data_index = (data_index+1) % DATA_SIZE;     
    frameTime = 0;
  }
}

export function render(index) {
  // if there are any pixels to display, see if index has reached a 
  // position that's supposed to be lit.  If so, set it to foreground
  // and move next_lit_pixel onward to the next target.
  if (pixelInterval && (index >= next_lit_pixel)) {
    next_lit_pixel += pixelInterval;      
    hsv(hForeground,sForeground,vForeground);       
  } else {
    hsv(hBackground,sBackground,vBackground); 
  }
}
3 Likes

Well I’m out in the ozone again - And I’d really rather have some intelligent questions/comments but …

  1. I am getting “execution steps exhausted” message anywhere past 56 data points. The whole 500 member array is populated (var watch). See example below.
  2. I get jiggly bits periodically with a a static pattern in between.
  3. Still don’t know how to make it DO anything reasonable. The test triangle wave input works fine, but how to get the code to “read” an array is beyond me. And I am very tired and bummed 'cause I looked at it ALL day and just feel stupid. And that’s the God’s awful truth.

for (var i = 0; i < DATA_SIZE; i++) {

data[i] =

data[1] = 137 ;
data[2] = 136 ;
data[3] = 136 ;

<mod added expander/collapser for length>

data[4] = 135 ;
data[5] = 134 ;
data[6] = 133 ;
data[7] = 132 ;
data[8] = 131 ;
data[9] = 131 ;
data[10] = 130 ;
data[11] = 129 ;
data[12] = 129 ;
data[13] = 128 ;
data[14] = 127 ;
data[15] = 127 ;
data[16] = 126 ;
data[17] = 125 ;
data[18] = 124 ;
data[19] = 124 ;
data[20] = 123 ;
data[21] = 123 ;
data[22] = 122 ;
data[23] = 121 ;
data[24] = 121 ;
data[25] = 120 ;
data[26] = 120 ;
data[27] = 119 ;
data[28] = 119 ;
data[29] = 119 ;
data[30] = 118 ;
data[31] = 118 ;
data[32] = 117 ;
data[33] = 116 ;
data[34] = 116 ;
data[35] = 115 ;
data[36] = 115 ;
data[37] = 114 ;
data[38] = 114 ;
data[39] = 113 ;
data[40] = 113 ;
data[41] = 112 ;
data[42] = 112 ;
data[43] = 111 ;
data[44] = 111 ;
data[45] = 110 ;
data[46] = 110 ;
data[47] = 110 ;
data[48] = 109 ;
data[49] = 109 ;
data[50] = 109 ;
data[51] = 108 ;
data[52] = 108 ;
data[53] = 107 ;
data[54] = 107 ;
data[55] = 107 ;
data[56] = 107 ;
data[57] = 106 ;
data[58] = 106 ;
data[59] = 106 ;
data[60] = 106 ;
data[61] = 105 ;
data[62] = 105 ;
data[63] = 105 ;
data[64] = 105 ;
data[65] = 104 ;
data[66] = 104 ;
data[67] = 104 ;
data[68] = 104 ;
data[69] = 103 ;
data[70] = 104 ;
data[71] = 104 ;
data[72] = 103 ;
data[73] = 104 ;
data[74] = 104 ;
data[75] = 104 ;
data[76] = 103 ;
data[77] = 104 ;
data[78] = 104 ;
data[79] = 104 ;
data[80] = 103 ;
data[81] = 103 ;
data[82] = 103 ;
data[83] = 102 ;
data[84] = 102 ;
data[85] = 103 ;
data[86] = 102 ;
data[87] = 101 ;
data[88] = 101 ;
data[89] = 101 ;
data[90] = 100 ;
data[91] = 99 ;
data[92] = 100 ;
data[93] = 98 ;
data[94] = 98 ;
data[95] = 99 ;
data[96] = 99 ;
data[97] = 98 ;
data[98] = 97 ;
data[99] = 97 ;
data[100] = 96 ;
data[101] = 97 ;
data[102] = 97 ;
data[103] = 96 ;
data[104] = 93 ;
data[105] = 92 ;
data[106] = 93 ;
data[107] = 93 ;
data[108] = 94 ;
data[109] = 93 ;
data[110] = 94 ;
data[111] = 94 ;
data[112] = 93 ;
data[113] = 93 ;
data[114] = 92 ;
data[115] = 93 ;
data[116] = 93 ;
data[117] = 93 ;
data[118] = 92 ;
data[119] = 93 ;
data[120] = 93 ;
data[121] = 93 ;
data[122] = 93 ;
data[123] = 93 ;
data[124] = 92 ;
data[125] = 92 ;
data[126] = 92 ;
data[127] = 93 ;
data[128] = 93 ;
data[129] = 91 ;
data[130] = 93 ;
data[131] = 92 ;
data[132] = 92 ;
data[133] = 92 ;
data[134] = 92 ;
data[135] = 92 ;
data[136] = 91 ;
data[137] = 91 ;
data[138] = 92 ;
data[139] = 91 ;
data[140] = 91 ;
data[141] = 91 ;
data[142] = 92 ;
data[143] = 92 ;
data[144] = 91 ;
data[145] = 91 ;
data[146] = 91 ;
data[147] = 91 ;
data[148] = 90 ;
data[149] = 90 ;
data[150] = 90 ;
data[151] = 91 ;
data[152] = 90 ;
data[153] = 90 ;
data[154] = 90 ;
data[155] = 90 ;
data[156] = 89 ;
data[157] = 88 ;
data[158] = 88 ;
data[159] = 89 ;
data[160] = 89 ;
data[161] = 89 ;
data[162] = 87 ;
data[163] = 89 ;
data[164] = 89 ;
data[165] = 88 ;
data[166] = 88 ;
data[167] = 87 ;
data[168] = 87 ;
data[169] = 86 ;
data[170] = 87 ;
data[171] = 88 ;
data[172] = 86 ;
data[173] = 86 ;
data[174] = 87 ;
data[175] = 88 ;
data[176] = 87 ;
data[177] = 86 ;
data[178] = 87 ;
data[179] = 87 ;
data[180] = 87 ;
data[181] = 86 ;
data[182] = 87 ;
data[183] = 87 ;
data[184] = 86 ;
data[185] = 86 ;
data[186] = 87 ;
data[187] = 87 ;
data[188] = 87 ;
data[189] = 86 ;
data[190] = 87 ;
data[191] = 87 ;
data[192] = 86 ;
data[193] = 87 ;
data[194] = 88 ;
data[195] = 88 ;
data[196] = 88 ;
data[197] = 87 ;
data[198] = 87 ;
data[199] = 88 ;
data[200] = 88 ;
data[201] = 88 ;
data[202] = 88 ;
data[203] = 88 ;
data[204] = 88 ;
data[205] = 89 ;
data[206] = 88 ;
data[207] = 89 ;
data[208] = 89 ;
data[209] = 89 ;
data[210] = 88 ;
data[211] = 89 ;
data[212] = 88 ;
data[213] = 88 ;
data[214] = 88 ;
data[215] = 88 ;
data[216] = 88 ;
data[217] = 88 ;
data[218] = 88 ;
data[219] = 88 ;
data[220] = 88 ;
data[221] = 88 ;
data[222] = 88 ;
data[223] = 86 ;
data[224] = 87 ;
data[225] = 85 ;
data[226] = 85 ;
data[227] = 85 ;
data[228] = 84 ;
data[229] = 83 ;
data[230] = 84 ;
data[231] = 84 ;
data[232] = 82 ;
data[233] = 82 ;
data[234] = 82 ;
data[235] = 81 ;
data[236] = 80 ;
data[237] = 79 ;
data[238] = 79 ;
data[239] = 78 ;
data[240] = 79 ;
data[241] = 79 ;
data[242] = 79 ;
data[243] = 79 ;
data[244] = 79 ;
data[245] = 80 ;
data[246] = 80 ;
data[247] = 80 ;
data[248] = 80 ;
data[249] = 76 ;
data[250] = 76 ;
data[251] = 75 ;
data[252] = 75 ;
data[253] = 75 ;
data[254] = 74 ;
data[255] = 74 ;
data[256] = 74 ;
data[257] = 73 ;
data[258] = 71 ;
data[259] = 69 ;
data[260] = 69 ;
data[261] = 68 ;
data[262] = 67 ;
data[263] = 65 ;
data[264] = 65 ;
data[265] = 64 ;
data[266] = 63 ;
data[267] = 63 ;
data[268] = 63 ;
data[269] = 62 ;
data[270] = 63 ;
data[271] = 63 ;
data[272] = 64 ;
data[273] = 63 ;
data[274] = 63 ;
data[275] = 63 ;
data[276] = 63 ;
data[277] = 63 ;
data[278] = 63 ;
data[279] = 65 ;
data[280] = 63 ;
data[281] = 62 ;
data[282] = 62 ;
data[283] = 62 ;
data[284] = 62 ;
data[285] = 62 ;
data[286] = 61 ;
data[287] = 63 ;
data[288] = 64 ;
data[289] = 63 ;
data[290] = 64 ;
data[291] = 68 ;
data[292] = 70 ;
data[293] = 70 ;
data[294] = 63 ;
data[295] = 63 ;
data[296] = 70 ;
data[297] = 72 ;
data[298] = 63 ;
data[299] = 74 ;
data[300] = 70 ;
data[301] = 65 ;
data[302] = 64 ;
data[303] = 65 ;
data[304] = 76 ;
data[305] = 76 ;
data[306] = 72 ;
data[307] = 74 ;
data[308] = 77 ;
data[309] = 80 ;
data[310] = 79 ;
data[311] = 76 ;
data[312] = 71 ;
data[313] = 72 ;
data[314] = 69 ;
data[315] = 75 ;
data[316] = 76 ;
data[317] = 77 ;
data[318] = 75 ;
data[319] = 77 ;
data[320] = 79 ;
data[321] = 76 ;
data[322] = 79 ;
data[323] = 77 ;
data[324] = 79 ;
data[325] = 82 ;
data[326] = 84 ;
data[327] = 86 ;
data[328] = 89 ;
data[329] = 87 ;
data[330] = 92 ;
data[331] = 91 ;
data[332] = 88 ;
data[333] = 89 ;
data[334] = 91 ;
data[335] = 91 ;
data[336] = 88 ;
data[337] = 93 ;
data[338] = 91 ;
data[339] = 92 ;
data[340] = 90 ;
data[341] = 89 ;
data[342] = 89 ;
data[343] = 93 ;
data[344] = 91 ;
data[345] = 92 ;
data[346] = 91 ;
data[347] = 89 ;
data[348] = 88 ;
data[349] = 88 ;
data[350] = 92 ;
data[351] = 92 ;
data[352] = 91 ;
data[353] = 96 ;
data[354] = 96 ;
data[355] = 94 ;
data[356] = 88 ;
data[357] = 88 ;
data[358] = 86 ;
data[359] = 86 ;
data[360] = 82 ;
data[361] = 80 ;
data[362] = 76 ;
data[363] = 75 ;
data[364] = 75 ;
data[365] = 73 ;
data[366] = 70 ;
data[367] = 70 ;
data[368] = 68 ;
data[369] = 68 ;
data[370] = 67 ;
data[371] = 66 ;
data[372] = 66 ;
data[373] = 67 ;
data[374] = 67 ;
data[375] = 67 ;
data[376] = 65 ;
data[377] = 67 ;
data[378] = 65 ;
data[379] = 64 ;
data[380] = 65 ;
data[381] = 63 ;
data[382] = 64 ;
data[383] = 66 ;
data[384] = 65 ;
data[385] = 65 ;
data[386] = 63 ;
data[387] = 66 ;
data[388] = 68 ;
data[389] = 64 ;
data[390] = 63 ;
data[391] = 67 ;
data[392] = 63 ;
data[393] = 62 ;
data[394] = 63 ;
data[395] = 68 ;
data[396] = 66 ;
data[397] = 64 ;
data[398] = 63 ;
data[399] = 62 ;
data[400] = 61 ;
data[401] = 66 ;
data[402] = 66 ;
data[403] = 66 ;
data[404] = 65 ;
data[405] = 63 ;
data[406] = 63 ;
data[407] = 63 ;
data[408] = 69 ;
data[409] = 71 ;
data[410] = 73 ;
data[411] = 66 ;
data[412] = 67 ;
data[413] = 68 ;
data[414] = 70 ;
data[415] = 77 ;
data[416] = 77 ;
data[417] = 73 ;
data[418] = 73 ;
data[419] = 76 ;
data[420] = 73 ;
data[421] = 75 ;
data[422] = 80 ;
data[423] = 81 ;
data[424] = 84 ;
data[425] = 80 ;
data[426] = 83 ;
data[427] = 81 ;
data[428] = 77 ;
data[429] = 77 ;
data[430] = 76 ;
data[431] = 75 ;
data[432] = 76 ;
data[433] = 77 ;
data[434] = 79 ;
data[435] = 79 ;
data[436] = 77 ;
data[437] = 80 ;
data[438] = 81 ;
data[439] = 82 ;
data[440] = 81 ;
data[441] = 82 ;
data[442] = 84 ;
data[443] = 82 ;
data[444] = 80 ;
data[445] = 86 ;
data[447] = 84 ;
data[448] = 80 ;
data[449] = 80 ;
data[450] = 81 ;
data[451] = 83 ;
data[452] = 84 ;
data[453] = 84 ;
data[454] = 82 ;
data[455] = 80 ;
data[456] = 71 ;
data[457] = 72 ;
data[458] = 69 ;
data[459] = 70 ;
data[460] = 69 ;
data[461] = 68 ;
data[462] = 72 ;
data[463] = 79 ;
data[464] = 78 ;
data[465] = 78 ;
data[466] = 75 ;
data[467] = 77 ;
data[468] = 78 ;
data[469] = 81 ;
data[470] = 82 ;
data[471] = 84 ;
data[472] = 80 ;
data[473] = 82 ;
data[474] = 81 ;
data[475] = 80 ;
data[476] = 82 ;
data[477] = 82 ;
data[478] = 82 ;
data[479] = 84 ;
data[480] = 87 ;
data[481] = 88 ;
data[482] = 93 ;
data[483] = 93 ;
data[484] = 88 ;
data[485] = 84 ;
data[486] = 83 ;
data[487] = 79 ;
data[488] = 77 ;
data[489] = 73 ;
data[490] = 72 ;
data[491] = 67 ;
data[492] = 71 ;
data[493] = 65 ;
data[494] = 66 ;
data[495] = 65 ;
data[496] = 66 ;

data[497] = 67 ;
data[498] = 68 ;
data[499] = 68 ;

}

@gandalf, you’re almost there. If you’re setting data items explicitly, you don’t need the for loop.
What for (i = 0; i < DATA_SIZE; i++) does,is generate the array index list from 0-499 so I don’t have to type all those numbers when I’m just generating synthetic data anyway.

You’ve got the real data all ready to go. Take out everything to do with the for loop, including the loop itself, the data[i]= and the pair of curly braces that start and end it, just leaving your list of data items, and it should work.

Yeah, @Gandalf - If you stared at this all day, I feel for ya and want to give you two related code examples. Try out each of these two patterns on your board and see if you understand what’s going on.

Populating an array's values manually

Let’s just work with the first 5 of your pixels for this one.

export var data = array(5) // Set up a 5-bin container

// Fill it with some data
data[0] = 0
data[1] = 1
data[2] = 0
data[3] = .5
data[4] = 1

// Notice there's no beforeRender(). Doni't need it this time. 

export function render(index) {
  if (index >= 5) {  // All pixels after the fifth are dark
    hsv(0, 0, 0) 
  } else {
    h = data[index] ? 0.0 : 0.66 // Red if this bin is nonzero, Blue if it's zero
    s = 1
    v = data[index] ? 1 :  .3    // Full brightness if this bin is nonzero, 30% otherwise
    hsv(h, s, v)
  }
}

Output:
Pixelblaze 2021-03-10 21-32-59


For loop

If you follow that, here’s how we can use the for loop to populate an array. You don’t really need an array to make this pattern, but let’s try anyway. Make the perfect squares (0, 1, 4, 9, 16, 25, 36, 49, etc) bright.

Pixelblaze 2021-03-10 21-29-11

var brightnesses = array(pixelCount)

// Loop through all the bins, setting the brightness for each pixel
for (i = 0; i < pixelCount; i++) { 
  // If there's no decimal part to the square root, 
  //  it's a perfect square (0,1,4,9,16,25,etc);
  //  set it's brightness to be full
  if (frac(sqrt(i)) == 0) {
    brightnesses[i] = 1
  } else {
    brightnesses[i] = .1
  }
}

export function render(index) {
  h = index/pixelCount // Rainbow
  s = 1
  v = brightnesses[index]
  hsv(h, s, v)
}

Hope this helps demystify arrays.

Blockquote

This is what “stuck” me more or less, thinking I had to “feed” the data in from inside beforeRender… when, after all it apparently is a block of data stored somewhere else. I suppose that is what is meant by “initialize”.

Pattern is now working, slowing it way down has effectively illustrated the visual and where I need to go next.

Thanks for your patience and input!

Thanks so much! I’ve looked at quite a bit of info on arrays and know the applications and forms are many… so, simplifying examples are just SO key at the front end. Really helpful!

Blockquote

Per my comment to @zranger1 above, this was quite a grand ol’ sticking point. Like, I was trying to see in the mind’s eye just how the data are accessed/employed in the code.

So that gets to looping… that example you provide using this tool

And

is just brilliant and solid, a great mechanism/tool in the bag.

As for looping with an explicitly defined set of array values, it seems to loop “automatically”?

By the way those 500 values and their format were pretty easily arranged in excel, and represent about half of the complete dataset I hope to run in the code. Question: Is this a computationally costly method or is there something better/faster? I am getting about 36 FPS on a v2.25 running a 16x16 matrix of 2812B.

Getting and setting values in static arrays are pretty fast. Why don’t you post your full code as it stands now (you can skip the middle 498 values!) and we should be able to speed it up.

Yes, for loops by there very nature will repeat themselves automatically. To expand on what zranger1 was explaining, the loop stuff goes like this:

for (SETUP INITIAL CONDITIONS;
KEEP LOOPING IF THIS IS TRUE;
RUN THIS BETWEEN EVERY ITERATION)
{ code to run repeatedly }

There’s actually very little computation going on – on my nearly identical PB2 16x16 setup, it runs at between 85 and 90 fps.

Check to make sure your data array initialization is outside any function – particularly that it isn’t being done inside your beforeRender() or render() functions. The data array is effectively a big database that lives outside your rendering loop. Once you’ve set an array value, it stays set unless you explicitly change it elsewhere in the pattern. So you don’t need to set the values on every frame.

Also performance is invariant with respect to the number of data items – to use 1000 items, just set the variable DATA_SIZE to 1000 and paste your new dataset into the pattern.

Here’s the code… looks pretty familiar, doesn’t it !!!

var frameTime = 10000;

var pixelInterval = 0; // interval (in pixels) between lit pixels. Calculated in code.

export function hsvPickerBlue(h,s,v) {
hBlue = h; sBlue = s; vBlue = v
}

export function hsvPickerRed(h,s,v) {
hRed = h; sRed = s; vRed = v
}

var DATA_SIZE = 500;

var data = array(DATA_SIZE); // storage for our input data stream FILLS ALL 500 with a number of pixels lit red

// export var data

var data_index = 0; // array index of “active” data item

var next_lit_pixel = 0; // position of next pixel to be lit

var holdTime = 8; // how long we display each data array entry // UNSURE OF AFFECT !!!
export var holdTime
// initialize
data [0] = 138 ;
data[1] = 137 ;
data[2] = 136 ;
data[3] = 136 ;

data[497] = 67 ;
data[498] = 68 ;
data[499] = 68 ;

// }

export function sliderSpeed(v) {
// holdTime = (v * 80) / DATA_SIZE;
holdTime = v * 500 // Modified hold time for longer dwell
}

export function beforeRender(delta) {
frameTime += delta;
next_lit_pixel = 0;

if (frameTime > holdTime) {
pixelInterval = pixelCount/data[data_index];
data_index = (data_index+1) % DATA_SIZE;
frameTime = 0;
}
}

export function render(index) {

if (pixelInterval && (index >= next_lit_pixel)) {
next_lit_pixel += pixelInterval;
hsv(hRed,sRed,vRed);
} else {
hsv(hBlue,sBlue,vBlue);
}
}

Examples of array usage I was poring over include sparks, spectomatrix optim, perlin simplex noise (not available say my notes), blinkfade, and spectro kalidastrip. And there may be others.

Syntax such as the following, (from @zranger1’s above (barely modified by me in any practical sense)) gets me thinking as to how it is working… I might have figured it out but might not -

A quick narrative on this use of the % operator would be absolutely great! That is, if I have not gone over time already…

% is the modulo operator. It’s used to get the remainder of a division operation.
10 % 5 returns 0
11 % 5 returns 1
12 % 5 returns 2
13 % 5 returns 3
and so on.

The range of the results will be {0,1,2,3,4} no matter what number you stick on the left side of the operation. So it’s commonly used as a handy and efficient way of restricting a number to a range.

In the array example above, I want to increment data_index to move it to the next item, and make sure it stays within the bounds of the data array – the range 0-DATA_SIZE. Modulo arithmentic is the simplest, fastest way to do this. You’ll see this sort of construct very frequently.

The other common use of % is retrieving the fractional portion of a number n with the expression n % 1.