Electronic Minora project, 2D array question/problen

I am trying to create an “Electronic Minora” in the 2D 32x8 matrix. I am not a SW developer but here is a working code which does everything I wanted (BIG Thanks to @zranger1 who helped me to create nice looking candle flickering):

var candlesNumber     = 3

var matriWwidth       = 32
var matrixHeight      = 8

var candlesMaxNumber  = 8
var candleWidth       = 3

// Pre-defined Candle Base Size
var candleBaseHeight  = array(candlesMaxNumber)
candleBaseHeight[0]   = 2
candleBaseHeight[1]   = 3
candleBaseHeight[2]   = 4
candleBaseHeight[3]   = 3
candleBaseHeight[4]   = 2
candleBaseHeight[5]   = 3
candleBaseHeight[6]   = 4
candleBaseHeight[7]   = 3

// Predefined Pixel Colors Array
var candleOffset = array(candlesMaxNumber + 1)[candlesMaxNumber] =
[
  // X - Candle Offset per Candle
  // Y - Candle Numbers
  [15,  0,  0,  0,  0,  0,  0,  0],
  [ 9, 20,  0,  0,  0,  0,  0,  0],
  [ 6, 15, 24,  0,  0,  0,  0,  0],
  [ 4, 11, 18, 25,  0,  0,  0,  0],
  [ 3,  9, 15, 21, 27,  0,  0,  0],
  [ 2,  7, 12, 17, 22, 27,  0,  0],
  [ 1,  5, 10, 14, 19, 23, 28,  0],
  [ 0,  4,  8, 12, 16, 20, 24, 28]
]

// Candle's Base Brightness and Color
var vBase = 0.15
var hBase = 0
var sBase = 0

// Candle's Fire Brightness and Color
var vFire = 1
var hFire = 0.05
var sFire = 1

// Calculated Parameters
var t1    = 0;

export function beforeRender(delta)
{
  // Build a timer using accumulated milliseconds
  // Denominator controls speed of noise field traversal (flicker speed)
  // The % 256 is used to keep values in a reasonable range for
  // input to noise function.
  t1 = (t1 + delta/2000) % 256;
}

export function render2D(index, x, y)
{
  X = floor(x * matriWwidth)
  Y = floor(y * matrixHeight)

  candleNumber = floor(X / (matriWwidth / candlesNumber))

  candleStartX = candleOffset[candlesNumber - 1][candleNumber]
  candleStopX  = (candleStartX + candleWidth)

  if  ((X >= candleStartX) && (X < candleStopX))
  {
    if (Y >= (matrixHeight - candleBaseHeight[candleNumber]))
    {
      v = vBase
      h = hBase
      s = sBase
    }
    else
    {
      // generate positive perlin noise value for current coordinates, offset by timer
      // this will be used both for pixel brightness and to determine the maximum
      // height of the current candle ()
      v = abs(perlin(Y+t1, X-t1, 0.5, PI))

      // if pixel is above max flame height, turn it off.  Otherwise
      // set it to perlin noise value.
      v = (y >= (vFire * v)-0.1) ? v : 0;
      h = hFire
      s = sFire
    }
  }
  else
  {
    v = 0
  }

  hsv(h, s, v)
}

Code works but I have a question about 2-dimentional arrays. I was not able to create nice looking candle’s spacing mathematically so I decide to use 2D array for spacing/offsets which depends on number of lighted candles. Array has 8x8 elements. I was able to create this array :

// Predefined Pixel Colors Array
var candleOffset = array(candlesMaxNumber + 1)[candlesMaxNumber] =
[
  // X - Candle Offset per Candle
  // Y - Candle Numbers
  [15,  0,  0,  0,  0,  0,  0,  0],
  [ 9, 20,  0,  0,  0,  0,  0,  0],
  [ 6, 15, 24,  0,  0,  0,  0,  0],
  [ 4, 11, 18, 25,  0,  0,  0,  0],
  [ 3,  9, 15, 21, 27,  0,  0,  0],
  [ 2,  7, 12, 17, 22, 27,  0,  0],
  [ 1,  5, 10, 14, 19, 23, 28,  0],
  [ 0,  4,  8, 12, 16, 20, 24, 28]
]

but I had to increase first index by 1. Otherwise I am getting “out of range” error.
I really cannot understand why I cannot create a symmetrical array. This error always pops up for any size square (A x A) arrays. Asymmetrical arrays do not produce this error.
What I am missing?

I think the way you’re defining your 2D array looks off to me. Usually it would be:

var TwoDArray = [[x1, y1], [x2, y2]]

// or

var TwoDArray = array(2)
TwoDArray[0] = array(2)
TwoDArray[1] = array(2)
TwoDArray[0][0] = x1 // etc

Instead, this statement:


var candleOffset = array(candlesMaxNumber + 1)[candlesMaxNumber] =
[
  // X - Candle Offset per Candle
  // Y - Candle Numbers
  [15,  0,  0,  0,  0,  0,  0,  0],
// etc

While I’m not even sure how the interpreter would execute it, it seems to allocate a 9 element blank array and store the 2D array literal (the values in the square brackets) in the last element of the 9-element 2D array (so actually making a 3D array). The other elements in the 1st dimension are probably zeros in Pixelblaze, so they can’t be accessed like candleOffset[0][N] - there’s no second dimension in that first element.

Thank you very much for the response but now I am very confused. I am using 2D array definition what I posted in a few different patterns. Originally I copied this definition from the existing pattern I found in a Pattern Library (I forgot what it was). Surprisingly everything works as expected except for the first index cannot be the same as second.

I just tested yours suggested example (first line) and sure, everything is working as expected.

Many BIG Thank you for making things very clear.

To break this down a bit

It’s not like C where you allocate on the left side with an initializer list on the right side. e.g. NOT like this C example:

int arr[5] = {1, 2, 3, 4, 5};

In JS, an expression like [1,2,3] does both allocation and initialization.

In your code you allocate an array with the [...] syntax, and it fills in the elements with the values in the list, which happen to also be arrays. That part is all you really need.

The left side expression of the rightmost = operator also allocates an array with array(candlesMaxNumber + 1). Next, one of it’s elements is referenced because of the [candlesMaxNumber] bit. The original array reference is lost because it was never stored in a variable before being dereferenced in that expression.

The rightmost = operator causes the [...] value to be stored in the discarded array element, but these = operators also pass the value along to the left most = operator. Its like doing this: a = b = 123 where both variables store the 123 value. So your [...] value ends up being passed through to the candleOffset array.

The reason you were getting out of range errors is because you tried to access an array element outside of the range of the array. Arrays are zero indexed (start at 0), so when you have a variable like candlesMaxNumber remember that counts/lengths are higher than indexes. If you want to access the last element of an array, it’s always at index length - 1. Instead, you were making an array one larger than necessary and accessing an element beyond your originally intended count.

Turns out you didn’t need that bit anyway, but hope that helps explain why you were getting out of range errors and how to access arrays,

1 Like

Thank you very much for the detailed explanation.
I modified my code(s) according to @jeff suggestion above and sure, everything is working as expected and looks nice and clean. When I started to use Pixelblaze few years ago for near all my lighting projects and because I am not a sw engineer I was looking for the 2D Array definition in the existing codes available online. And I found one I was using. It happens to be absolutely wrong definition of 2D Array but surprisingly it worked until this my latest project. Further more, when I increased by 1 what I was thinking is a first index in array everything worked as it should. This was very confusing to my non sw developer eyes.