Nested for loop not 'animating'

Hi,

I’m having trouble getting some nested for loops to ‘animate’…

My code draws an enlarged ‘pixel grid’ by iterating through a 3D array, which contains a number of pixel ‘configurations’, each containing 3 ‘rows’ of four ‘columns’.

int[][][] pixelConfigs = {

{{0, 0, 1, 1}, {0, 1, 0, 0}, {1, 0, 1, 1}},
{{0, 1, 1, 1}, {0, 0, 0, 0}, {1, 1, 1, 0}},
{{0, 1, 1, 1}, {0, 0, 0, 1}, {1, 0, 1, 0}}
};

I want to display these ‘grids’ in sequence, looping through first to last.

I’ve used three nested loops to run through the array - configuration, row, and column - and draw the pixel grid, with each ‘pixel’ being on or off depending on the value found in the pixelConfigs array.

When I run this code, It only displays the grid that represents the final ‘configuration’ in pixelConfigs,

int[][][] pixelConfigs = {

  {{0, 0, 1, 1}, {0, 1, 0, 0}, {1, 0, 1, 1}}, 
  {{0, 1, 1, 1}, {0, 0, 0, 0}, {1, 1, 1, 0}}, 
  {{0, 1, 1, 1}, {0, 0, 0, 1}, {1, 0, 1, 0}}
  
};

int LCD_pixelSize = 28;
int LCD_pixelGap = 2;
int fillVal;

void setup() {
  size(1100, 400);
}

void draw() {
  noStroke();
  drawSingleCharacter(0, 0);
}

void drawSingleCharacter() {

  for (int config = 0; config < pixelConfigs.length; config++) {
    for (int row = 0; row < pixelConfigs[0].length; row++) {
      for (int column = 0; column < pixelConfigs[0][row].length; column++) {

        fillVal = pixelConfigs[config][row][column] * 255;
        fill(fillVal);

        int xPos = (row * LCD_pixelSize) + (row * LCD_pixelGap);
        int yPos = (column * LCD_pixelSizet) + (column * LCD_pixelGap);

        rect(xPos, yPos, LCD_pixelSize, LCD_pixelSize);
      }
    }
  }
}

BUT when I remove the outermost for loop, and use a random number to decide which ‘configuration’ to display, it animates, but not as intended - i.e sequentially…

void drawSingleCharacter() {

  int randomIndex = int(random(0, pixelConfigs.length));

  for ( int row = 0; row < pixelConfigs[0].length; row++) {
    for (int column = 0; column < pixelConfigs[0][row].length; column++) {

      int fillVal = pixelConfigs[randomIndex][row][column] * 255;
      fill(fillVal);

      int xPos = (row * LCD_pixelSize) + (row * LCD_pixelGap);
      int yPos = (column * LCD_pixelSize) + (column * LCD_pixelGap);

      rect(xPos, yPos, LCD_pixelSize, LCD_pixelSize);
    }
  }
}

I think I’m missing something basic and fundamental about the way the first bit of code with 3 loops is iterating through the array - any advice gratefully received!

1 Like

Welcome to the forums!

You are! But we’ve all been there lol.
Our brains, for whatever reason, think that we’ll witness each of the for-loops one after another (and somewhere in memory, this is technically occurring). But we forget that draw() is not a real-time view into the main canvas - it’s just a function that gets called 60-times a second.

Try this analogy. Processing is a painter. You instruct him what to draw inside draw(), he paints it in his studio, and finally he takes a picture and emails it to you. So your code is telling him to sequentially draw 3 different configs, one on top of another, and when he whips out his camera to take a picture, all you see is the last “layer” he painted!

So what do you do? First off, get rid of the config-for-loop.

Then, the answer is to introduce time. If you want the frame to change once a second, you’re going to need to wait for draw() to execute 60 times. Then, you’ll have to iterate the config you’re on, and wait another 60 frames.

Happy coding!

3 Likes

See here the difference of behavior in the console.

int t, x, y, rows = 20, columns = 20;
boolean allowed = true;

void setup() { 
} 

//void draw() {
//  if (allowed) {
//    t++;
//    if(t == ((columns-1)*rows)+1) allowed = false;
//    if(x % columns-1 == 0 && t != 2) y++;
//    println(x+"  "+y); 
//    if(x == columns-1 ) x = 0;
//    x++;   
//  }
//}

void draw() {
  if (allowed) {
    t++;
    for (y = 0; y < rows; y++) {
      for (x = 0; x < columns; x++) {
        println(x+"  "+y);
      }
    }
    if (t == 1) allowed = false;
  }
}
2 Likes

Thanks for the replies, Tony that’s a nice analogy.

WiIl digest and get back to you!

1 Like

OK:

Initialized config outside the function.
Removed the outermost loop.
Incremented config outside the two loops that ’ draw’ the ‘character’.
Included a condition: if (config >= pixelConfigs.length) config = 0;.

Thanks for the help!

Noel - I’m still studying your answer, the code is a little too ‘compact’ if that’s the right word (maybe ‘efficient’, or ’ concise’?) for me to easily parse, but I’m working on it!

int config = 0;

void drawSingleCharacter() {
  
  config++;
  if (config >= pixelConfigs.length) config = 0;
  
  for ( int row = 0; row < pixelConfigs[0].length; row++) {
    for (int column = 0; column < pixelConfigs[0][row].length; column++) {

      int fillVal = pixelConfigs[myIndex][row][column] * 255;
      fill(fillVal);


      int xPos = (row * LCD_pixelSize) + (row * LCD_pixelGap);
      int yPos = (column * LCD_pixelSize) + (column * LCD_pixelGap);

      rect(xPos, yPos, LCD_pixelSize, LCD_pixelSize);
    }
  }
}

1 Like

Basically, draw() updates the screen only once at its end and not throughout.

Therefore, everything that happens in the for loop is not happening on the screen but gets accumulated internally and then is put all at once on to the screen at the end of draw ().

So when you want an animation get rid of the for loops and replace them by variables i1, i2, i3 and control them by your code like the for loops would act. E.g.:

...
i2++; 
if (i2 > max2) { 
    i1++; 
    i2=0;
}
...

Thus you use the fact that draw in itself loops automatically and gives you the animation.

Warm regards, Chrisir

Code example:



int[][][] pixelConfigs = {

  {{0, 0, 1, 1}, {0, 1, 0, 0}, {1, 0, 1, 1}}, 
  {{0, 1, 1, 1}, {0, 0, 0, 0}, {1, 1, 1, 0}}, 
  {{0, 1, 1, 1}, {0, 0, 0, 1}, {1, 0, 1, 0}}

};

int LCD_pixelSize = 28;
int LCD_pixelGap = 2;
int fillVal;

int config = 0;

void setup() {
  size(1100, 400);
  frameRate(2);
}

void draw() {
  noStroke();
  drawSingleCharacter();
}

void drawSingleCharacter() {

  // for (int config = 0; config < pixelConfigs.length; config++) {
  for (int row = 0; row < pixelConfigs[0].length; row++) {
    for (int column = 0; column < pixelConfigs[0][row].length; column++) {

      fillVal = pixelConfigs[config][row][column] * 255;
      fill(fillVal);

      int xPos =13+ (row * LCD_pixelSize) + (row * LCD_pixelGap);
      int yPos =13+ (column * LCD_pixelSize) + (column * LCD_pixelGap);

      rect(xPos, yPos, LCD_pixelSize, LCD_pixelSize);
    }
  }
  //}
  config++;
  if (config>2) 
    config = 0;
}
3 Likes

what an awesome response!!

/tf

3 Likes

Thanks Chrisir for your explanation.

Wow, thanks, that means a lot :grin: Also, welcome to the forums!

1 Like