Think I found a bug in P3D

So ive been working on a little project today and ran into an issue, wondering if anyone can tell my why its happening, theres a get() line commented out under the draw function, with it commented out it breaks and stops working as intended but with that line running it works as intended, maybe ive just overlooked something or maybe P3D needs fixing, i actually found the issue by accident as i put the same get function in a print statement to get color values, so it was a fluke but worked

heres my code

int total = 10000;
Mover[] movers = new Mover[total];
boolean paused = false;
PGraphics img;

void setup() {
  size(1000, 1000, P3D);
  surface.setVisible(true);

  img = createGraphics(width, height, P3D);
  img.loadPixels();
  noSmooth();

  for (int i = 0; i < total; i++) {
    movers[i] = new Mover();
  }
}

void draw() {
  surface.setTitle(str((int)frameRate));

  background(0);

  img.beginDraw();
  //img.get(0,0);

  if (!paused) {
    img.noStroke();
    img.fill(0, 30);
    img.rect(0, 0, width, height);
  }

  for (Mover m : movers) {
    if (!paused) {
      m.boundary();
      m.update();
    }
    m.show();
  }

  image(img, 0, 0);

  img.endDraw();
}

void keyPressed() {
  if (key == ' ') paused = !paused;
}

and heres the class for the movers

class Mover {

  PVector pp, pos, move, senseL, senseR, center;
  int senseDist;
  int index;
  float SL, SR;
  int sensorSpread;
  int sensorTurnRate;
  int sensorSize;
  int borderTurnRate;
  int turnRate;
  int xl,yl,xr,yr;
  int pixelCount = width*height;

  Mover() {
    pos = new PVector(width/2,height/2);
    pp = pos.copy();
    move = PVector.random2D().mult(3);
    senseL = new PVector();
    senseR = new PVector();
    senseDist = 20;
    sensorSpread = 30;
    sensorTurnRate = 20;
    sensorSize = 2;
    borderTurnRate = 30;
    turnRate = 15;
  }

  void update() {
    senseL.set(senseDist, 0);
    senseL.rotate(move.heading() + radians(-sensorSpread));
    senseL.add(pos.copy());

    senseR.set(senseDist, 0);
    senseR.rotate(move.heading() + radians(sensorSpread));
    senseR.add(pos.copy());

    SL = 0;
    SR = 0;

    for (int j = -sensorSize; j < sensorSize; j++) {
      for (int i = -sensorSize; i < sensorSize; i++) {
        xl = floor(senseL.x+i);
        yl = floor(senseL.y+j);
        xr = floor(senseR.x+i);
        yr = floor(senseR.y+j);
        SL += img.brightness(img.pixels[(xl + yl * width + pixelCount) % pixelCount]);
        SR += img.brightness(img.pixels[(xr + yr * width + pixelCount) % pixelCount]);
      }
    }

    if (SL > SR) {
      move.rotate(radians(-sensorTurnRate));
    } else if (SR > SL) {
      move.rotate(radians(sensorTurnRate));
    } else {
      move.rotate(radians(random(-turnRate, turnRate)));
    }
    pos.add(move);
  }

  boolean sense(PVector v) {
    if (v.x < 0 || v.x > width || v.y < 0 || v.y > height) {
      return true;
    }
    return false;
  }

  void boundary() {
    if (sense(senseL)) {
      move.rotate(radians(borderTurnRate));
    }
    if (sense(senseR)) {
      move.rotate(radians(-borderTurnRate));
    }
    if (sense(senseL) && sense(senseR)) {
      move.rotate(PI);
    }
  }

  void show() {
    img.stroke(255);
    img.strokeWeight(2);
    img.line(pp.x, pp.y, pos.x, pos.y);

    pp.set(pos.copy());
  }
}
1 Like

Cool!

I save all my “unintended consequences” in a separate folder called “cool” for future study.

Experimented a little and came up with this:

Code (class not included):

Click here to expand
//https://discourse.processing.org/t/think-i-found-a-bug-in-p3d/40117

int total = 10000;
Mover[] movers = new Mover[total];
boolean paused = false;
PGraphics img;

void setup() {
  size(1000, 1000, P2D);
  surface.setVisible(true);

  img = createGraphics(width, height, P3D);
  img.loadPixels();
  noSmooth();

  for (int i = 0; i < total; i++) {
    movers[i] = new Mover();
  }
}

void draw() {
  surface.setTitle(str((int)frameRate));

  background(0);

  //img.get();
  img.loadPixels();
  
  img.beginDraw();
  if (!paused) 
    {
    img.noStroke();
    img.fill(0, 30);
    img.rect(0, 0, width, height);
    }

  for (Mover m : movers) 
    {
    if (!paused) 
      {
      m.boundary();
      m.update();
      }
    m.show();
    }

  //image(img, 0, 0);
  img.endDraw();
  
  //img.updatePixels();
  
  image(img, 0, 0);
}

void keyPressed() {
  if (key == ' ') paused = !paused;
}

:)

2 Likes

Haha nice, theres alot you can do with it, been experimenting all arvo, simply adding sensors opened up a whole new way of playing with particles

Just saw the code part you added, yeah i had img.loadPixels(); in setup as i usually do, simply moving that to draw works but it doesnt explain why the issue is there, its really strange as loading the pixels usually only has to happen once in setup, it may be because im using them on a PGraphics that calls to endDraw(); ? thats my only reasoning, thanks for the fix though

1 Like

After a bit of experimentation I found the solution, calling img.endDraw() was making the pixel array dormant making the sensors stop functioning which is why it stopped working, you dont even have to call img.loadPixels() to reference the array, using img.get(0,0) will automatically load them and img.endDraw() unloads them which is why calling loadPixels every frame is needed, thanks @glv for the hint

2 Likes

Hello @Mikey83,

Again… very cool!

What was your inspiration for this?
Your work is worthy of a gallery post.

I tried this without PGraphics as well to optimize for speed and this works:

void draw() 
  {
  surface.setTitle(str((int)frameRate));
   
  noStroke();
  fill(0, 30);
  rect(0, 0, width, height);

  loadPixels();
  
  for (Mover m : movers) 
    {
    m.boundary();
    stroke(255);
    m.update();
    m.show();
    }
  }

// Start over!
void keyPressed()
  {
  frameCount = -1;
  }
  
void mousePressed()
  {
  noLoop();  // Holding down the mouse activates looping
  }

void mouseReleased()
  {
  loop();  // Releasing the mouse stops looping draw()
  }

The reference definition applies to above since you are reading the pixels in update() :

In the Mover class I removed anything that had an img. in front of it.

Commenting:

// noSmooth()

seemed to speed things up! I expected the opposite.

I got some cool results with these:

  if(frameCount%30 == 0)
    loadPixels();
  if(frameCount == 60)
    loadPixels();

I got the same results as you with PImage and loadPixels() and was able to “loadPixels()” different ways… I will dig more under the hood (the source code) when time permits.

:)

1 Like

Cheers mate, the frameCount%30 is a cool effect, glad you’re having fun with it, I never comment my code so im glad you could find your way around lol.

As for inspiration, about a year ago, maybe more I had an idea to have random walkers move around the screen but wanted a PImage layer to paint walls on, I really wanted the walkers to be able to interact and avoid the wall but couldnt figure out how the collision math would even work, then it clicked, pixel color, thats when I figured 2 vectors attached to the particle could work and had a lot of fun with that project, wasn’t until the other day I came back to the idea and thought, why not have the walkers themselves draw the walls, wasn’t too interesting and quite chaotic, wasn’t until I made the walls fade that it ended up like it did, was quite blown away at the result.

The reason I had the particles on a PGraphics layer is because I was planning on using the main screen space for something else.

Actually took out the for loops on the sensors and just used a single point of reference for the pixel values and that sped it up quite a lot too, can run 10k particles at 60fps now.

Also found if you alter the rotation in the sensor section you get something akin to a multi neighborhood cellular automata which was very unexpected

if (SL > SR) {
        move.rotate(radians(-sensorTurnRate+90));
      } else if (SR > SL) {
        move.rotate(radians(sensorTurnRate+90));
      }

keep having fun, id love to know if you find anything else out

2 Likes