Alternatives to for loops within draw()?

edited December 2014 in p5.js

Hello again,

I'm trying to write a code that will cycle through all possible color combinations of every pixel for a certain sized canvas. Basically, take the color values in pixels[] and increase a value until it gets to 255, then start it over at 0 and increase the next value by 1 until it gets to 255, at which point it will reset and increase the next value by one, etc.

I found a way that I think would do it with a for loop, but because of the fact that the loop occurs once every frame, it does this to all pixels at once instead of going pixel by pixel. The loop I've written looks like this:

loadPixels();
for (p = 0; p < pixels.length; p++) {

    //to avoid alpha values:
    if (p % 4 == 3) {
        continue;
    }

    pixels[p] += 1;

    if (pixels[p] == 255) {
        pixels[p+1] += 1;
        pixels[p] = 0;
    }
}
updatePixels();

(I'm pretty sure my logic on how I built this is flawed, I still don't have a perfect grasp on javascript)

My thought for that loop was that would function like this:

  • Skip alpha values
  • For every frame, add 1 to the current value of pixels[]
  • If the current value of pixels[] becomes 255, reset it to zero and add 1 to the next value of pixels[]
  • Do this continually through the rest of the array.

Instead, the whole canvas just slowly goes from black to white and then repeats. Am I correct in my assumption that this is because the loop is executed once per frame?

I've written super rudimentary code that does a portion of what I need, but the problem with it is that I'd have to write three if statements for every pixel of the canvas (which is pretty much impossible). Could somebody tell me how to streamline this? I've been banging my head against the wall on it for a while now.

loadPixels();

pixels[0] += 1;

if (pixels[0] == 255) {
    pixels[1] += 1;
    pixels[0] = 0;
}
if (pixels[1] == 255) {
    pixels[2] += 1;
    pixels[1] = 0;
}
if (pixels[2] == 255) {
    pixels[4] += 1;
    pixels[2] = 0;
}
//End first pixel, start second:
if (pixels[4] == 255) {
    pixels[5] += 1;
    pixels[4] = 0;
}
if (pixels[5] == 255) {
    pixels[6] += 1;
    pixels[5] = 0;
}
if (pixels[6] == 255) {
    pixels[8] += 1);
    pixels[6] = 0;
}
//End of second pixel
//etc.
updatePixels();

Thanks in advance for any help or advice!

Answers

  • edited December 2014

    Not so sure if I understood correctly what you want there!
    Either way, made a "class" called ColorIncreaser in order to streamline & keep tabs of some needed values:
    These are the 3 ColorIncreaser's instance variables:

    1. p5.Image as img,
    2. Its current pixels[]'s index as idx,
    3. And optional value increasing step as sp.

    Create an instance of it via: new ColorIncreaser(). All of its 3 parameters are optional.
    After that, you can increase value inside current pixels[]'s index via inc() method or decrease it via dec().
    It goes to either next or previous index when trying to inc() w/ px[idx] == 255 or dec() w/ px[idx] == 0.
    Besides reseting current value to 0 or 255 respectively 1st! :D

    Hope I've hit bull's eye this time. Check it out: (*)

    /**
     * ColorIncreaser Class (v1.11)
     * by GoToLoop (2014/Dec/08)
     *
     * forum.processing.org/two/discussion/8537/
     * alternatives-to-for-loops-within-draw
     */
    
    const STEP = 4, BG = 0150;
    var colInc, forward = true;
    
    function setup() {
      createCanvas(100, 100);
      frameRate(100).background(BG);
      colInc = new ColorIncreaser(null, STEP);
    }
    
    function draw() {
      forward? colInc.inc() : colInc.dec();
    
    /*print(colInc.idx + '\t' + (colInc.img
        ? colInc.img.pixels[colInc.idx]
        : pixels[colInc.idx]));*/
    }
    
    function mouseClicked() {
      print('Forward? ' + (forward = !forward));
    }
    
    function ColorIncreaser(img, sp, idx) {
      img instanceof p5.Image || (img = null);
      (this.img = img)? img.loadPixels() : loadPixels();
    
      this.sp = abs(+sp || 1);
    
      const len = img? img.pixels.length : pixels.length;
      this.idx  = min(abs(+idx || 0), len-2);
    }
    
    ColorIncreaser.prototype.inc = function() {
      const px = this.img? this.img.pixels : pixels;
    
      if (px[this.idx] == 0xff)
        px[this.idx] = 0, this._next(px.length);
    
      px[this.idx] += this.sp;
      this.img? this.img.updatePixels() : updatePixels();
    }
    
    ColorIncreaser.prototype._next = function(len) {
      (++this.idx & 3) == 3 && ++this.idx;
      if (this.idx >= len)  this.idx = 0;
    }
    
    ColorIncreaser.prototype.dec = function() {
      const px = this.img? this.img.pixels : pixels;
    
      if (!px[this.idx])
        px[this.idx] = 0xff, this._prev(px.length);
    
      px[this.idx] -= this.sp;
      this.img? this.img.updatePixels() : updatePixels();
    }
    
    ColorIncreaser.prototype._prev = function(len) {
      (--this.idx & 3) == 3 && --this.idx;
      if (this.idx < 0)  this.idx = len-2;
    }
    
  • edited December 2014

    Thanks for taking a look at this, GoToLoop! What you've written is almost it, there's one thing that's different though.

    When px[idx] == 255 it resets px[idx] to 0 and increases idx by one. Basically meaning that it only focuses on changing the color value of one pixel at a time. I'm trying to get it to continue increasing the initial pixel while increasing the subsequent pixel, which would then go on to increase the pixel after that, etc. What I'm trying to set up is something that basically would function like an odometer:

    [ r ] [ g ] [ b ] [ a ] [ r ] [ g ] [ b ] [ a ]

    (Here the brackets are just delineating the individual wheels of an odometer, not meaning arrays)

    [ 0 ] [ 0 ] [ 0 ] [ 255 ] [ 0 ] [ 0 ] [ 0 ] [ 255 ]

    (255 because the alpha values start there and should stay there)

    As the first red value hits 255:

    [ 255 ] [ 0 ] [ 0 ] [ 255 ] [ 0 ] [ 0 ] [ 0 ] [255]

    The next value, the green value, becomes 1 and the red resets to 0.

    [ 0 ] [ 1 ] [ 0 ] [ 255 ] [ 0 ] [ 0 ] [ 0 ] [ 255 ]

    Then red1 continues to go up to 255 and reset to zero, raising green1 each time it completes a cycle:

    [ 255 ] [ 1 ] [ 0 ] [ 255 ] [ 0 ] [ 0 ] [ 0 ] [ 255 ]

    [ 0 ] [ 2 ] [ 0 ] [ 255 ] [ 0 ] [ 0 ] [ 0 ] [ 255 ]

    This happens until green1 gets to 255, at which point it advances blue1 by one, and resets itself to 0.

    [ 255 ] [ 255 ] [ 0 ] [ 255 ] [ 0 ] [ 0 ] [ 0 ] [ 255 ]

    [ 0 ] [ 0 ] [ 1 ] [ 255 ] [ 0 ] [ 0 ] [ 0 ] [ 255 ]

    From here, the red1 value has to cycle to 255 to advance green1 255 more times in order for the blue1 to become 2. And then once blue1 hits 255 and returns to 0, red2 (the first red after the alpha value) increases by one.

    Is there some way to make this happen? I've written a mathematic formula for how long it would take each slot to advance, but I don't really know how to translate it into code that Javascript can actually use:

    //Assuming the first index, or red1, goes from 0 to 255 once every second:
    color value of any index place = seconds/256^(index place)
    
    or, using the names you set up in ColorIncreaser:
    px[idx] = seconds/256^(idx-1)
    
    Since the first value ( px[0] ) hits 255 and resets to 0 once every second,
    that means the second value ( px[1] ) increases by 1 every second:
    
    seconds = 1
    px[1] = 1/256^0
    px[1] = 1/1
    
    and that the third value, green of the first pixel ( px[2] ),
    will increase by one every 256 seconds.
    
    seconds = 256
    px[2] = 256/256^1
    px[2] = 256/256 = 1
    
    Values further down in the array take an increasingly longer 
    amount of time to advance, *255 for each place.
    

    Does that make it any clearer?

    I realize this looks really inefficient or strange, but the idea is that it is something that is supposed to unfold over a very long amount of time

  • edited December 2014

    I believe that if there were a way for me to get the if statements I wrote in the original post to work like a formula the solution would be pretty simple:

    if (pixels[currentValue] == 255) {
      pixels[nextValue] += 1;
      pixels[currentValue] = 0;
    }
    

    I'd still have to adjust to avoid the alpha values, but that would get me 99% there. Is there anything built into either P5.js or Javascript that would allow me to do something like that?

  • edited December 2014 Answer ✓

    255 * 255 * 255=16581375

    int[] d;
    
    void setup() {
      size(220, 220);
      d = new int[30]; // [width*height*3];
      for (int i=0; i<d.length; d[i++]=0);
    }
    void draw() {
      int i=0;
      boolean go=true;
     // int counter=0;
      while (go) {
        if (d[i]>=255) {
          d[i]=0;
          i++;
        } else {
          d[i]+=5;
          go=false;
        }
     //   counter++;
      }
     // if (counter > 2000) { 
     //   println("WARN");
    //  }
     // loadPixels();
      for (int j=0; j<d.length; j+=3) {
        //pixels[j/3]=color(d[j], d[j+1], d[j+2]);
        fill(d[j], d[j+1], d[j+2]);
        rect(10*(j/3),0,10,10);
      }
    //  for(int t=0;t<d.length;t++){
    //    print( d[t] + " ");
    //  }
    //  println("");
    //  updatePixels();
    }
    

    Do whatever with this.

  • edited December 2014

    .

  • TfGuy44, thanks! I hadn't considered breaking it into an if/else statement. This should work quite well. And thanks again to GoToLoop for taking a serious stab at it. You guys are awesome!

Sign In or Register to comment.