createGraphics() and frameRate(): can you have a different value per canvas?

edited October 2014 in p5.js

I have 3 programs that I wanted to layout in a 800x800 square. I don't know CSS or JS really I'm just a beginner so I thought I'd bring all the functions into this one single file. This posed some challenges for me. I thought the easiest thing to do was create many canvases so I used the following in the setup():

createCanvas(800, 800);
pg = createGraphics(500, 500);
ab = createGraphics(800, 300);

So the second and third canvases overlay the main one as it spans accross the whole composite, like so(the main effect - a particle system - is drawn to the main canvas on the top right, even though the canvas is the full 800x800, and this allows for its particles to trickle down to ab, which I happen to like):

| pg    |  C   |
 | 500  |       | h:500

|-------------------|

|       ab      | h:300
        800

Then I realized I needed ab to be drawn at one frame per 5 sec. so I tried ab.frameRate(1/5); but didn't seem to work. So I settled for this in in the function which draws to ab:

if (frameCount % 150 == 0) {
ab.background(0);
ab.text(phrase, width/2, 25, 700, 400); 
}
...
image(ab, 0, 500, width, height); 

Is that proper and could have I used frameRate? What's the best practice?

About layout, I found this simple CSS block I added to a file to center the canvas on the html and have a black background:

canvas {
    padding-left: 0;
    padding-right: 0;
    margin-left: auto;
    margin-right: auto;
    display: block;
    width: 1000px;
}
body { background-color: black; }

So I'm trying to learn something from this. Maybe using many canvases wasn't such a good idea here, using one for all the functions is better; but here I had a function which required a different framerate so I don't see how I could have done both. I'm looking for some advice on what to do next time I want to do such a layout? Which of html, JS, CSS was the solution to layout those individual programs instead of merging them in the first place?


I'll add the whole script because ascii drawing on the forum works not so good to illustrate layout:

// //////////////////////////////////////////////////////////////////////////////// //
// "Montage pour..." - Un programme combinant 3 programmes en p5.js (p5js.org)      //
// //////////////////////////////////////////////////////////////////////////////// //
// 1- le motif bleu                                                                 //
// une "port" vers p5.js du sketch Processing "yabe1324065"; attribution:           //
// "yabe1324065" by Tetsuro Yabe, licensed under Creative Commons                   //
//  Attribution-Share Alike 3.0 and GNU GPL license.                                //
// Work: http://openprocessing.org/visuals/?visualID= 166810                        //
// License:                                                                         //
// http://creativecommons.org/licenses/by-sa/3.0/                                   //
// http://creativecommons.org/licenses/GPL/2.0/                                     //
//////////////////////////////////////////////////////////////////////////////////////
// 2- le système de particules triangulaires sur la photo                           //
// "Particle System" dans The Nature of Code, de Daniel Shiffman,                   //
// adapté en p5.js ici: http://p5js.org/learn/examples/Simulate_Particle_System.php //
// //////////////////////////////////////////////////////////////////////////////// //
// 3- le texte aléatoire construit à partir de listes de mots:                      //
// "Cadavre exquis" de PaHern_pAni( - 2014                                             //
//   License: CC-by-SA/GNU GPLv2                                                    //
// ///////////////////////////////////////////////////////////////////////////////////

var eColor = [];
// Matrice pour les "déplacements" dans le motif bleu
var speed = [
[2.0, 1.8, 1.6, 1.4, 1.2, 1.0],
[2.2, 2.0, 1.8, 1.6, 1.4, 1.2],
[2.4, 2.2, 2.0, 1.8, 1.6, 1.4]
];

var system;
var img; 

// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// 3 canvas; 1-motif bleu, 2- pg; fontaine sur image, 3- ab; texte
function setup() {
  createCanvas(800, 800);
  pg = createGraphics(500, 500);
  ab = createGraphics(800, 300);
  background(0, 50);
  img = loadImage("photo.jpg");
  system = new ParticleSystem(createVector(620, height/4));

  pg.smooth();
  pg.noStroke();
  pg.fill(0);
  for(var y = 0;y < 3; y++){
    eColor[y] = [];
    for(var x = 0;x < 6; x++){
        eColor[y][x] = 0;
        }
    }
}

// ///////////////////////////////////////////////////////////////////////////////////////////////////////

function draw() {
 cadavre();
 triangles();
 image(img, 500, 0, 300, 500);
 system.addParticle();
 system.run();
}

// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// Le motif de triangles bleus

function triangles() {
pg.background(255);
  for(var y = 0;y < 3; y++){
    for(var x = 0;x < 6; x++){
        eColor[y][x] += speed[y][x];
        if (eColor[y][x] > 255 || eColor[y][x] < 0) {
            speed[y][x] = -speed[y][x];
            }
        pg.fill(0, eColor[y][x], 255, 175);
        pg.triangle(0+100*x, 84+168*y, 50+100*x, 0+168*y, -50+100*x, 0+168*y);

        pg.fill(0, eColor[y][x], 255, 200);
        pg.triangle(50+100*x, 168+168*y, 100+100*x, 84+168*y, 0+100*x, 84+168*y);

        pg.fill(0, eColor[y][x], 255, 225);
        pg.triangle(50+100*x, 0+168*y, 100+100*x, 84+168*y, 0+100*x, 84+168*y);

        pg.fill(0, eColor[y][x], 255, 250);
        pg.triangle(0+100*x, 84+168*y, 50+100*x, 168+168*y, -50+100*x, 168+168*y);
        image(pg, 0, 0);
        }
    }
}

// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// Le cadavre exquis - une liste pour sujet/complément, une pour verbe, etc. accent aigu= \xE9, grave \xE8

function cadavre() {
var sujcompl = [
"le loup-garou",
"le cri du chauve... souris",
"le cadavre",
"le festin",
"le cerf-volant",
"l'orange",
"la fleur",
"la tombe",
"la maison hant\xE9e",
"le sushi",
"la f\xE9e",
"le vent",
"le zombie",
"la souffleuse",
"le vampire",
"le tire-bouchon",
"le sorcier",
"le capitaine",
"l'\xE9toile de ninja"
];
var verbe = [
"mange",
"mord",
"lance",
"ouvre",
"ferme",
"aime",
"explose",
"d\xE9coupe",
"embrasse",
"vole",
"lib\xE8re",
"op\xE8re",
"dig\xE8re",
"dessine",
"attaque",
"d\xE9fend",
"effraie"
];
var adverbe = [
"all\xE8grement",
"terriblement",
"vertigineusement",
"mollement",
"courageusement",
"indubitablement",
"\xE9pouvantablement",
"verbalement",
"affectueusement",
"d\xE9licieusement"
];
var adjectif = [
"invert\xE9br\xE9(e)",
"\xE9gar\xE9(e)",
"triste",
"bleu(e)",
"scintillant",
"vibrant(e)",
"dangereu(se)",
"congel\xE9(e)",
"multicolore",
"d\xE9goulinant(e)",
"sucr\xE9(e)",
"glac\xE9(e)"
];

// on utilise le nombre d'éléments dans une liste, on tire au hasard, on ramène à un nbre entier, on assigne
var a = floor(random(sujcompl.length));
var b = floor(random(adjectif.length));
var c = floor(random(verbe.length));
var d = floor(random(adverbe.length));
var e = floor(random(sujcompl.length));

// Comme on utilise la même liste pour sujet et complément, on veut pas que ce soit le même mot.
if (a == e) {
      e = floor(random(sujcompl.length));
    } else {

    }
// On construit la phrase avec les éléments bout à bout sélectionnés au hasard
var phrase = sujcompl[a]+" "+adjectif[b]+" "+verbe[c]+" "+adverbe[d]+" "+sujcompl[e]+"...";

// Paramètres du texte, taille etc.
ab.stroke(0, 102, 153, 51);
ab.strokeWeight(3);
ab.textLeading(30);
ab.textSize(30);
ab.textAlign(CENTER);

// Texte affiche seulement si compte des images total est un multiple de 150; 30fps par defaut donc aux 5 secs.
if (frameCount % 150 == 0) {
ab.background(0);
ab.text(phrase, width/2, 25, 700, 400); 
} else {
}
image(ab, 0, 500, width, height); 
}

// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// Particle System Components
//
// A simple Particle class (sous forme de triangles multicolores ici)

var Particle = function(position) {
  this.acceleration = createVector(0, -0.02);
  this.velocity = createVector(random(-0.5, 0.5), random(4));
  this.position = position.get();
  this.lifespan = 255;
};

Particle.prototype.run = function() {
  this.update();
  this.display();
};

// Method to update position
Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  this.lifespan -= 1;
};

// Method to display
Particle.prototype.display = function() {
  stroke(random(255), random(255), random(255), this.lifespan);
  strokeWeight(2);
  fill(random(255), random(255), random(255), this.lifespan);

  if (this.velocity.y > 0) {
  triangle(this.position.x, this.position.y, 6+this.position.x, this.position.y, 3+this.position.x, 6+this.position.y);
  } else {
  triangle(3+this.position.x, this.position.y, this.position.x, 6+this.position.y,  6+this.position.x, 6+this.position.y);
    }
};

// Is the particle still useful?
Particle.prototype.isDead = function(){
  if (this.lifespan < 0) {
    return true;
  } else {
    return false;
  }
};

var ParticleSystem = function(position) {
  this.origin = position.get();
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  this.particles.push(new Particle(this.origin));
};

ParticleSystem.prototype.run = function() {
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
    if (p.isDead()) {
      this.particles.splice(i, 1);
    }
  }
};

Answers

  • edited October 2014

    Still noob about p5js. Nonetheless, how about this example? ~O)

    // forum.processing.org/two/discussion/7809/
    // creategraphics-and-framerate-can-you-have-a-different-value-per-canvas
    
    // 2014/Oct/26 by GoToLoop
    
    const FPS = 60, SECS = 3, INTERVAL = SECS*FPS; // each 3 seconds
    var pg, count = 0;
    
    function setup() {
      createCanvas(600, 400);
      frameRate(FPS).smooth().imageMode(CORNER);
    
      textAlign(CENTER), textStyle(BOLD), textSize(0200);
      fill(255), noStroke();
    
      pg = createGraphics(width, height>>1);
      pg.textAlign(CENTER), pg.textStyle(ITALIC), pg.textSize(060);
      pg.fill(0), pg.noStroke();
    }
    
    function draw() {
      background(0);
      text(frameCount/FPS|0, width>>1, height - (height>>3));
    
      if (!(frameCount%INTERVAL))  updatePG(pg);
      image(pg, 0, 0);
    }
    
    function updatePG(pg) {
      const r = random(0400)|0, g = random(0400)|0, b = random(0400)|0;
      pg.background(r, g, b);
    
      pg.text("R : " + r + ", G : " + g + ", B : " + b
        , pg.width>>1, pg.height/1.8);
      pg.text(count + " x " + SECS*count++, pg.width>>1, pg.height/1.1);
    }
    
  • edited October 2014

    Thank you! I'm analyzing what you wrote. I notice sometimes the change occurs after 6 or 9 seconds instead of 3... was it intended on your part that I witness that? Also, I have to look up; I don't know the syntax of if (test) updatePG(pg); what does the part on the same line after the test do again? As you can see there are different levels of noobs lol.

  • edited October 2014

    I notice sometimes the change occurs after 6 or 9 seconds instead of 3...

    You're right on that! Just found out a bug in p5.js! :@)
    Variable frameCount is supposed to represent how many times draw() was called back by p5.js's framework:
    http://p5js.org/reference/#/p5/frameCount
    But it's clearly skipping the counting at random times! And that makes frameCount%INTERVAL == 0 fail! :(

    I don't know the syntax of if (test) updatePG(pg);

    It means if test is true, execute that statement. The statement can be in the same line as well! =:)

  • edited October 2014
  • edited October 2014

    Thank you! I gather using modulo was a good idea to do this despite the issue. Thanks for the cue on if, I struggle with syntax variations... :)

    Still looking for further info on making a more intelligent layout out of "my"(I did write one of the 3 programs loll) script...

  • edited October 2014 Answer ✓

    Since frameCount is buggy in p5.js right now, what about a millis() version. Which btW is much more precise:

    // forum.processing.org/two/discussion/7809/
    // creategraphics-and-framerate-can-you-have-a-different-value-per-canvas
    
    // 2014/Oct/27 by GoToLoop
    
    const FPS = 60, SECS = 3, INTERVAL = SECS*1000; // each 3 seconds
    var pg, nextMillis, count = 0;
    
    function setup() {
      createCanvas(600, 400);
      frameRate(FPS).smooth().imageMode(CORNER);
    
      textAlign(CENTER), textStyle(BOLD), textSize(0200);
      fill(255), noStroke();
    
      pg = createGraphics(width, height>>1);
      pg.textAlign(CENTER), pg.textStyle(ITALIC), pg.textSize(060);
      pg.fill(0), pg.noStroke();
    
      nextMillis = millis() + 1500;
    }
    
    function draw() {
      background(0);
      text(frameCount/FPS|0, width>>1, height - (height>>3));
    
      if (millis() >= nextMillis)
        updatePG(), nextMillis = millis() + INTERVAL;
    
      image(pg, 0, 0);
    }
    
    function updatePG() {
      const r = random(0400)|0, g = random(0400)|0, b = random(0400)|0;
      pg.background(r, g, b);
    
      pg.text("R : " + r + ", G : " + g + ", B : " + b
        , pg.width>>1, pg.height/1.8);
      pg.text(count + " x " + SECS*count++, pg.width>>1, pg.height/1.1);
    }
    
  • edited October 2014

    So this works and I'll be able to use that!! I'll mark your answer and will make another post focused on layout. Thanks again for your insight!

Sign In or Register to comment.