Creating Stills

Hi, I used the following code under void draw(); in order to save images of my code rather than a screenshot. When I do so, I receive an error message saying Error “void”?

 void keyPressed(){
    if(keyCode == ENTER){
      saveFrame("ScreenNet-####.jpg");
      }
    }

Here is a bigger snip it of the code that I am trying to take a still of:

void draw() {
  background(0);

  // set the origin to the center of the screen, and move it 400px far away on z-axis
  translate(halfWidth, halfHeight, 0.05);  // zoom in (lower numbers) zoom out (higher numbers around 200)
  

  // switch between mesh and texured mode
  //if (frameCount % int(TAU*100.) == 0) {
  //  mode = !mode;
  //}

  if (mode) {
    //if textured mode no strokes and a light from viewpoint to object
    noStroke();
    directionalLight(255, 255, 255, 0, 0, -1);
  } else {
    // if mesh mode only show strokes
    noFill();
    stroke(250);
    strokeWeight(1.5);
    smooth(10);
  }
  
 void keyPressed(){
    if(keyCode == ENTER){
      saveFrame("ScreenNet-####.jpg");
      }
    }
    

Hello,

the function keyPressed() must be OUTSIDE draw()

so you need a closing } where draw() ends.

Warm regards,

Chrisir

void draw() {
  background(0);

  // set the origin to the center of the screen, and move it 400px far away on z-axis
  translate(halfWidth, halfHeight, 0.05);  // zoom in (lower numbers) zoom out (higher numbers around 200)
  

  // switch between mesh and texured mode
  //if (frameCount % int(TAU*100.) == 0) {
  //  mode = !mode;
  //}

  if (mode) {
    //if textured mode no strokes and a light from viewpoint to object
    noStroke();
    directionalLight(255, 255, 255, 0, 0, -1);
  } else {
    // if mesh mode only show strokes
    noFill();
    stroke(250);
    strokeWeight(1.5);
    smooth(10);
  } //else 
} //func !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  
void keyPressed(){
    if(keyCode == ENTER){
      saveFrame("ScreenNet-####.jpg");
    }
 } //func

Now I have a black screen when I compile the code.

I tried to create a still with a different code but I can’t find the still anywhere.

void setup(){
  size(1500,800,P3D);
  background(#FF7605);
  stroke(2);
  smooth(8);
  fill(#081EFF);
  float xstart = random(10);
  float ynoise = random(10);
  translate (width/2, height/2, 0);
  for (float y =-(height/8); y <= (height/8); y+=3){
    ynoise += 0.02;
  float xnoise = xstart;
  for (float x = -(width/8); x <= (width/8); x+=3){
    xnoise += 0.02;
    drawPoint(x,y,noise(xnoise,ynoise));
    }
  }
}
void drawPoint(float x, float y, float noiseFactor){
  pushMatrix();
  translate (x * noiseFactor * 6, y * noiseFactor * 6, y);
  float edgeSize = noiseFactor *26;
  ellipse(0,0, edgeSize, edgeSize);
  popMatrix();
}

 void keyPressed(){
    if(keyCode == ENTER){
      saveFrame("GenArtBook-####.jpg");
      }
 }

hit ctrl-k in the editor and check the folder

no wonder, since you don’t draw anything in the old version.

Not sure but maybe key, not keyCode

Use:

if (key == ENTER || key == RETURN)

See key / Reference / Processing.org for key


AND you need draw() !!!

no use using only setup() !!!

With P2D or P3D, I find it safer to only call saveFrame() from within the draw() function. So I typically use

boolean bSaveFrame = false;

void draw() {
  // draw some stuff

  if( bSaveFrame ) {
    saveFrame("whateverfilename.png");
    bSaveFrame = false;
  }
}

void keyPressed() {
  if( keyCode == ENTER )
    bSaveFrame = true;
}

1 Like

Hello @scudly,

Can you elaborate on “safer”?

The reference:
https://processing.org/reference/saveFrame_.html

States:

To save an image that is identical to the display window, run the function at the end of draw() or within mouse and key events such as mousePressed() and keyPressed() .

I slowed things down and this will save a frame in draw() and keyPressed() event:

boolean bSaveFrame = false;

void setup()
  {
  size(200, 200, P3D);
  frameRate(1);
  }

void draw() {
  // draw some stuff
  background(0);
  textSize(48);
  textAlign(CENTER, CENTER); 
  text(frameCount, width/2, height/2);

  if( bSaveFrame ) {
    saveFrame("draw.png");
    bSaveFrame = false;
  }
}

void keyPressed() {
  if( keyCode == ENTER )
    bSaveFrame = true;
    saveFrame("event.png");
}

Take a look at images saved to see the difference.

:)

In the past, on at least some platforms with at least some opengl drivers, calling saveFrame() from an event handler gave a black image – something about the gl context not being active or the image residing in GPU memory not being loaded to CPU memory before the save. Maybe it’s now fixed across all platforms, but it didn’t used to be.

Worse than the frameCount being off is any use of random() will create a new stream of values resulting in a different image than the one you wanted to save. You can work around this by remembering your seed valuing and using randomSeed( mySeed ); at the start of draw(). When you want a new (different) image, say mySeed = random(1000000); to get a new stream of random value. You can even include mySeed as part of your saved file names letting you reproduce a given image by hard-coding the seed value if, for instance, you want to regenerate an image at a higher resolution.

Likewise, don’t use “frameCount” directly in your image generation, but save some other value that you can increment only when appropriate for your animation.

1 Like

Hi @glv,

I’m recieving an “Unexpected token: void” error message. No saved image either. Here is the code:

// a class to representing a point on our grid.
// This should/would/could be done better by defining it as faces (of a triangle mesh).
// but would it keep as simple as possible 
class GridPoint {
  float x, y, v;
  public GridPoint(float px, float py, float pv) {
    x = px;  // x-pos of the grid point
    y = py;  // y-pos of the grid point
    v = pv;  // height of the grid point
  }
}

//helper variables
float halfWidth, halfHeight;
boolean mode = false;
// storage of out grid
int rows, cols;
GridPoint[][] grid;
// image used for texture and heightmap
PImage img;
boolean bSaveFrame = false;

void setup() {
  size(900, 900, P3D);
  frameRate(1);
  smooth(8);
  halfWidth  = width/2.;
  halfHeight = height/2.;
  initGrid(8.);
  // use textureMode IMAGE as the grid is not normalized
  textureMode(IMAGE);
}

void initGrid(float scl) {
  // load the image, used for texture and heightmap.
  // usually more than one picture will be used. One for heightmap, one for normalmap and one with the texture.
  // for simplicity it has the same size than our grid to not make the code more math intensive.
  // to get this work our image is somehow a bit blurred (like noise is), otherwise the mesh isn't smooth enough
  img = loadImage("July26.jpg");
  img.loadPixels();
  img.resize(900,900);
  // initialize our gridpoints
  rows = floor(height/scl);
  cols = floor(width/scl);
  grid= new GridPoint[rows][cols];
  for (int y = 0; y < rows; y++) {
    for (int x = 0; x < cols; x++) {
      int sx = floor(x*scl);
      int sy = floor(y*scl);
      // set x and y coordinates (center origin) and the height of the current point.
      // height is the normalized brightness value [0..1] of the pixel color from the image.
      // Subtract 0.5 to align it to center [-0.5..0.5]
      // scaling it by 50, so the range is [50.0*(-0.5)..50.0*(0.5)]
      grid[y][x] = new GridPoint(-halfWidth+sx+scl/2, -halfHeight+sy+scl/2, 160.*(brightness(img.get(sx, sy))/255-1.2));
    }
  }
}

void draw() {
  background(0);

  // set the origin to the center of the screen, and move it 400px far away on z-axis
  translate(halfWidth, halfHeight, 0.05);  // zoom in (lower numbers) zoom out (higher numbers around 200)
  

  // switch between mesh and texured mode
  //if (frameCount % int(TAU*100.) == 0) {
  //  mode = !mode;
  //}

  if (mode) {
    //if textured mode no strokes and a light from viewpoint to object
    noStroke();
    directionalLight(255, 255, 255, 0, 0, -1);
  } else {
    // if mesh mode only show strokes
    noFill();
    stroke(250);
    strokeWeight(1.5);
    smooth(10);
    
if( bSaveFrame) {
    saveFrame("ScreenNet-####.jpg");
    bSaveFrame = false;
  }
}

void mousePressed() {
  if( mousePressed == ENTER )
    bSaveFrame = true;
    saveFrame("ScreenNet-###.jpg");
}
  }

  // apply the rotation after lighting, otherwhile the light gets also rotated, which we not want.
  // tipping 30degree back on X-axis
  //rotateX(radians(30));
  // tipping left/right a bit per frame from -PI/4 - +PI/4 on Y-axis
  //rotateY(sin(frameCount/100.)*QUARTER_PI);

  // build the surface/terrain by triangle strips to display it
  for (int y = 0; y < rows-1; y ++) {
    beginShape (QUAD_STRIP); // or (TRIANGLE_STRIP),(QUAD_STRIP), 
    if (mode) {
      // on textured mode set the texture
      texture(img);
    }
    // common triangle strips
    for (int x = 0; x < cols; x++) {
      GridPoint ca = grid[y][x];
      GridPoint cb = grid[y+1][x];
      vertex(ca.x, ca.y, ca.v, ca.x+halfWidth, ca.y+halfHeight);
      if (mode) {
        // on textured mode we need to set the normal vector, so the light knows how to behave on hittng the surface
        // usually there are better calculation than this cheap hack
        // comment it out to see the difference if normals are messed up
        PVector na = new PVector(ca.x-cb.x, ca.y-cb.y, 0).sub(new PVector(ca.x-cb.x, ca.y-cb.y, 1)).normalize();
        normal(na.x, na.y, na.z);
      }
      vertex(cb.x, cb.y, cb.v, cb.x+halfWidth, cb.y+halfHeight);
      if (mode) {
        // on textured mode we need to set the normal vector, so the light knows how to behave on hittng the surface
        // usually there are better calculation than this cheap hack
        // comment it out to see the difference if normals are messed up
        PVector nb = new PVector(cb.x-ca.x, cb.y-ca.y, 0).sub(new PVector(cb.x-ca.x, cb.y-ca.y, 1)).normalize();
        normal(nb.x, nb.y, nb.z);
      }
     
    }
    endShape();
  }
}

You have your code order mangled. You pasted the saveFrame code into the middle of your draw() instead of putting it at the end.

and the void mousePressed() function should be outside of draw().

My apologies, but I am still recieving an error message after organizing the code.

“Expecting EOF, but found if”

// a class to representing a point on our grid.
// This should/would/could be done better by defining it as faces (of a triangle mesh).
// but would it keep as simple as possible 
class GridPoint {
  float x, y, v;
  public GridPoint(float px, float py, float pv) {
    x = px;  // x-pos of the grid point
    y = py;  // y-pos of the grid point
    v = pv;  // height of the grid point
  }
}

//helper variables
float halfWidth, halfHeight;
boolean mode = false;
// storage of out grid
int rows, cols;
GridPoint[][] grid;
// image used for texture and heightmap
PImage img;


void setup() {
  size(900, 900, P3D);
  frameRate(1);
  smooth(8);
  halfWidth  = width/2.;
  halfHeight = height/2.;
  initGrid(8.);
  // use textureMode IMAGE as the grid is not normalized
  textureMode(IMAGE);
}

void initGrid(float scl) {
  // load the image, used for texture and heightmap.
  // usually more than one picture will be used. One for heightmap, one for normalmap and one with the texture.
  // for simplicity it has the same size than our grid to not make the code more math intensive.
  // to get this work our image is somehow a bit blurred (like noise is), otherwise the mesh isn't smooth enough
  img = loadImage("July26.jpg");
  img.loadPixels();
  img.resize(900,900);
  // initialize our gridpoints
  rows = floor(height/scl);
  cols = floor(width/scl);
  grid= new GridPoint[rows][cols];
  for (int y = 0; y < rows; y++) {
    for (int x = 0; x < cols; x++) {
      int sx = floor(x*scl);
      int sy = floor(y*scl);
      // set x and y coordinates (center origin) and the height of the current point.
      // height is the normalized brightness value [0..1] of the pixel color from the image.
      // Subtract 0.5 to align it to center [-0.5..0.5]
      // scaling it by 50, so the range is [50.0*(-0.5)..50.0*(0.5)]
      grid[y][x] = new GridPoint(-halfWidth+sx+scl/2, -halfHeight+sy+scl/2, 160.*(brightness(img.get(sx, sy))/255-1.2));
    }
  }
}

void draw() {
  background(0);

  // set the origin to the center of the screen, and move it 400px far away on z-axis
  translate(halfWidth, halfHeight, 0.05);  // zoom in (lower numbers) zoom out (higher numbers around 200)
  

  // switch between mesh and texured mode
  //if (frameCount % int(TAU*100.) == 0) {
  //  mode = !mode;
  //}

  if (mode) {
    //if textured mode no strokes and a light from viewpoint to object
    noStroke();
    directionalLight(255, 255, 255, 0, 0, -1);
  } else {
    // if mesh mode only show strokes
    noFill();
    stroke(250);
    strokeWeight(1.5);
    smooth(10);
    
    boolean bSaveFrame = false;
  }
  


  // apply the rotation after lighting, otherwhile the light gets also rotated, which we not want.
  // tipping 30degree back on X-axis
  //rotateX(radians(30));
  // tipping left/right a bit per frame from -PI/4 - +PI/4 on Y-axis
  //rotateY(sin(frameCount/100.)*QUARTER_PI);

  // build the surface/terrain by triangle strips to display it
  for (int y = 0; y < rows-1; y ++) {
    beginShape (QUAD_STRIP); // or (TRIANGLE_STRIP),(QUAD_STRIP), 
    if (mode) {
      // on textured mode set the texture
      texture(img);
    }
    // common triangle strips
    for (int x = 0; x < cols; x++) {
      GridPoint ca = grid[y][x];
      GridPoint cb = grid[y+1][x];
      vertex(ca.x, ca.y, ca.v, ca.x+halfWidth, ca.y+halfHeight);
      if (mode) {
        // on textured mode we need to set the normal vector, so the light knows how to behave on hittng the surface
        // usually there are better calculation than this cheap hack
        // comment it out to see the difference if normals are messed up
        PVector na = new PVector(ca.x-cb.x, ca.y-cb.y, 0).sub(new PVector(ca.x-cb.x, ca.y-cb.y, 1)).normalize();
        normal(na.x, na.y, na.z);
      }
      vertex(cb.x, cb.y, cb.v, cb.x+halfWidth, cb.y+halfHeight);
      if (mode) {
        // on textured mode we need to set the normal vector, so the light knows how to behave on hittng the surface
        // usually there are better calculation than this cheap hack
        // comment it out to see the difference if normals are messed up
        PVector nb = new PVector(cb.x-ca.x, cb.y-ca.y, 0).sub(new PVector(cb.x-ca.x, cb.y-ca.y, 1)).normalize();
        normal(nb.x, nb.y, nb.z);
      }
     
    }
    endShape();
  }
}  
    if( bSaveFrame) {
    saveFrame("ScreenNet-####.jpg");
    bSaveFrame = false;
  }
}

void mousePressed() {
  if( mousePressed == ENTER )
    bSaveFrame = true;
    saveFrame("ScreenNet-###.jpg");
   }
}

The boolean bSaveFrame = false; needs to be a global variable, so put it outside of draw().

The if( bSaveFrame ) ... block of code needs to be in draw() below the rest of the code that does your actual drawing. Very carefully look at where you have your curly-braces to make sure your code is properly nested the way you want it. Right now, you have the saveFrame code OUTSIDE of draw().

1 Like

In fact I think this needs fixed:


void mousePressed() {
  if( mousePressed == ENTER )
    bSaveFrame = true;
    saveFrame("ScreenNet-###.jpg");
   }
}

To simply;


void mousePressed() {
    bSaveFrame = true;
}

And it should work (can’t test as on phone)

2 Likes

When grabbing stills I have used the pdf library (export pdf) that saves stills as vector images. You can then scale them to any size without losing resolution. Especially good for making digital prints…

2 Likes