Why is line() faster then point()?

Hello!

I currently have an sketch running 10000 simple particles with P2D. When I try to render the particles as individual points using point() i get about 25 fps. While running the SAME sketch, but using line() as the current (x,y) and (x+dx,y+dy) I get 45 fps, why? In my head, lines uses two points plus all the pixels between them, so it should be much slower...

The only other scenario I've thought is that point() uses set() and therefore is slow. So... what's correct?

Tagged:

Answers

  • edited May 2014 Answer ✓

    I have dug into the core of Processing's PGraphics class, and point() is not performed using set(), but the vertex() method, which is a quite long bit of code.

    I'm not entirely sure why a line is drawn faster than a vertex, but they are both drawn the same way, and both are shapes in the way that they both use beginShape() and endShape().

    If you don't intend to use 3D space for the points, then the fastest way to display specific pixels on the screen is through directly modifying the pixels[] array.

    I have wrote a function that, with my tests, achieves a stable 60 fps with 60000 pixels being drawn, versus the point() method, performing at 15 fps on my laptop.

    void setPixel(int x, int y, color c) {
      if (pixels == null) loadPixels();
      if (x < 0 || x >= width) return;
      if (y < 0 || y >= height) return;
      pixels[x + y * width] = c;
    }
    

    Just make sure you use updatePixels() at the end of the loop you draw the 10k+ pixels in.

    -- MenteCode

  • edited May 2014

    I believe it'd be even faster if the 3 if () blocks were removed from there! $-)

  • edited May 2014

    @GoToLoop It would, but they are for accuracy. The first one, unless done manually, is necessary. As for the last two, if the user is not aware of this either, pixels that should not be on the screen will be. See this example:

    void setup() {
      size(300, 300);
    }
    
    void draw() {
      background(255);
      for (int x = 0; x < 2; x++) {
        for (int y = 0; y < 2; y++) {
          setPixel(750 + x, 150 + y, color(0));
        }
      }
      updatePixels();
    }
    
    void setPixel(int x, int y, color c) {
      if (pixels == null) loadPixels();
      pixels[x + y * width] = c;
    }
    

    The setPixel() function's x is clearly off the screen, yet the pixels are displayed. The for loops are there only for the visibility of proof.

    However, you are correct in that, if all is taken into account by the code-writer, then the loops are, indeed, unnecessary.

  • edited May 2014

    ... if all is taken into account by the code-writer, then the loops are, indeed, unnecessary.

    If you meant the if ()'s, that's my opinion. Why the heck any1 would pass a null pixels[] anyways? :-w
    I even wouldn't invoke loadPixels() by myself! :P I like my snippets as fast as possible!
    Checks only for crucial limits which are beyond the control of the invoker's passed parameters.

    The example below doesn't check for neither null nor whether the other parameters are within expected range.
    However, it does make some tiny silent adjustments to them!

    http://forum.processing.org/two/discussion/4825/how-to-create-mask-for-transparency-between-2-pictures

    void alphaRect(PImage img, color a, int dim) {
      dim >>= 1; // diameter to radius.
      a <<= 030; // move alpha channel to its right octet.
    
      final color[] p = img.pixels;
      final int w = img.width, h = img.height;
    
      final int minX = max(mouseX - dim, 0);
      final int maxX = min(mouseX + dim, w);
    
      final int minY = max(mouseY - dim, 0);
      final int maxY = min(mouseY + dim, h);
    
      for (int row = minY; row != maxY;)
        for (int col = minX, rw = w*row++; col != maxX;) {
          final int idx = rw + col++;
          p[idx] = p[idx] & 0xFFFFFF | a;
        }
    
      img.updatePixels();
    }
    
  • Thank you both for the answers, I think that checking:

    if (pixels == null) loadPixels();

    would be necessary, since I asked to draw a point... but, on the other hand, I still have to call updatePixels() manually in my code... And since that is needed maybe the coder had to have called loadPixels() before, so I really don't know anymore.

    Anyway, out of curiosity, is there a way to render points really fast in 3D space? (I understood why you used this method in 2D and used the pixels array)

  • edited May 2014

    ... maybe the coder had to have called loadPixels() before, ...

    In my alphaRect() snippet, I do expect the coder to send me a PImage in a ready-to-use state.
    I've already done the favor of calling updatePixels() at the end, since I've modified the whole pixels[]! :P
    But perhaps I should remove even that, b/c the coder might want to do more things to it later on.
    We only need to call updatePixels() just before actually displaying a PImage.
    Multiple callings to it are both redundant and slow! >:)

  • edited May 2014

    Anyway, out of curiosity, is there a way to render points really fast in 3D space?

    If you intend to use the points in 3D space, and rotate the viewpoint around them, then that will require a strong knowledge of 3D to 2D projection, which I do not possess at the moment.

    However, if you are asking for as much as a stationary viewpoint, and the most you'd like to do is translating these 3D points, then it isn't all that complicated (to me, at least).

    I decided not to get too mathematical here, to keep the code simple and fast.

    void setup() {
      size(400, 400);
    }
    
    void draw() {
      background(255);
      loadPixels();
      setPixel3D(width/2 + sin(millis()/500f) * 60, height/2 - 60, 250 + cos(millis()/500f)*35, color(0));
      setPixel3D(width/2 + sin(millis()/500f + radians(90)) * 60, height/2 - 60, 250 + cos(millis()/500f + radians(90)) * 35, color(0));
      setPixel3D(width/2 + sin(millis()/500f + radians(180)) * 60, height/2 - 60, 250 + cos(millis()/500f + radians(180)) * 35, color(0));
      setPixel3D(width/2 + sin(millis()/500f + radians(270)) * 60, height/2 - 60, 250 + cos(millis()/500f + radians(270)) * 35, color(0));
      setPixel3D(width/2 + sin(millis()/500f) * 60, height/2 + 60, 250 + cos(millis()/500f) * 35, color(0));
      setPixel3D(width/2 + sin(millis()/500f + radians(90)) * 60, height/2 + 60, 250 + cos(millis()/500f + radians(90)) * 35, color(0));
      setPixel3D(width/2 + sin(millis()/500f + radians(180)) * 60, height/2 + 60, 250 + cos(millis()/500f + radians(180)) * 35, color(0));
      setPixel3D(width/2 + sin(millis()/500f + radians(270)) * 60, height/2 + 60, 250 + cos(millis()/500f + radians(270)) * 35, color(0));
      updatePixels();
      println(frameRate);
    }
    
    void setPixel3D(double x, double y, double z, color c) {
      int projectX = (int) ((x - width/2) * (158.58/z));
      int projectY = (int) ((y - height/2) * (158.58/z));
      for (int xOffs = 0; xOffs < 2; xOffs++) {
        for (int yOffs = 0; yOffs < 2; yOffs++) {
          setPixel(projectX + width/2 + xOffs, projectY + height/2 + yOffs, c);
        }
      }
    } 
    
    void setPixel(int x, int y, color c) {
      //if (pixels == null) loadPixels(); //This is no longer needed, due to the method being called every time draw() is called.
      if (x < 0 || x >= width) return;
      if (y < 0 || y >= height) return;
      pixels[x + y * width] = c;
    }
    

    The for loops in setPixel3D() are just for visibility. I am still experimenting with proper depth, angle, etc.. Otherwise, that should answer your question nicely.

  • edited May 2014 Answer ✓

    A few tests show that my computer handles about 35000 points at ~30 fps with the code.

    My computer is fairly weak, as well, so there are high chances that performance will be higher on anyone else's than mine.

    void setup() {
      size(400, 400);
      colorMode(HSB);
    }
    
    void draw() {
      int pixelCount = 0;
      background(0);
      loadPixels();
    
      for (int y = 6; y >= 0; y--) {
        for (int i = 0; i < 5000; i++) {
          setPixel3D(mouseX + sin((millis() + (i << 1))/500f) * 60, mouseY + y, 250 + cos((millis() + (i << 1))/1000f) * 35, color((millis()/100f + i) % 255, 255, 255));
          pixelCount++;
        }
      }
      updatePixels();
      println(frameRate + "," + pixelCount);
    }
    
    void setPixel3D(double x, double y, double z, color c) {
      int projectX = (int) ((x - width/2) * (158.58/z));
      int projectY = (int) ((y - height/2) * (158.58/z));
      setPixel(projectX + width/2, projectY + height/2, c);
    } 
    
    void setPixel(int x, int y, color c) {
      if (x < 0 || x >= width) return;
      if (y < 0 || y >= height) return;
      pixels[x + y * width] = c;
    }
    
  • Wow, that was really impressive, thank you very much for taking the time and posting a code! Cheers!!

Sign In or Register to comment.