Putting entire sketch inside a function

I am working on an online artwork generator using the flickr API. For the prototype I want a user to enter a string into an input box and then flickr returns images based on that string that are then overlayed etc to make a new image.

This is my code so far:

`

Type a word to generate a new image

          <input type="text" id="subject" value="rainbow">

          <button onclick="createNewImage()">Create New Image</button>

          <script language="javascript" type="text/javascript">


          //The problem occurs with the calling of this function, if I remove it it works but I have to call it after user input....
          function createNewImage(){


            var keyword = document.getElementById("subject").value;


            $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?",
            {
              tags: keyword,
              tagmode: "any",
              format: "json"
            },

            function(data) {
              var image_1_index = Math.floor(Math.random() * data.items.length);
              var image_2_index = Math.floor(Math.random() * data.items.length);

              image1_src = data.items[image_1_index]['media']['m'].replace("_m", "_b");
              image2_src = data.items[image_2_index]['media']['m'].replace("_m", "_b");
              //$('body').css('background-image', "url('" + image_src + "')");

            });

            var blend_index;

            var xa, yb, xc, yd, xe, yf, xg, yh, r, g, b;

            function preload() {
              img1 = loadImage(image1_src);
              img2 = loadImage(image2_src);
              imgs = [loadImage("assets/1.jpg"), loadImage("assets/2.jpg"), loadImage("assets/3.jpg"), loadImage("assets/4.jpg"), loadImage("assets/5.jpg"), loadImage("assets/0.jpg")];
            }

            function setup() {
              frameRate(60);
              createCanvas(windowWidth, windowHeight);
              image_index = int(random(5));
            }



            function draw() {

              blend_index = int(random(4));

              switch (blend_index) {
                case 0: blendMode(ADD);
                break;
                case 1: blendMode(DARKEST);
                break;
                case 2: blendMode(LIGHTEST);
                break;
                case 3: blendMode(DIFFERENCE);
                break;
                case 4: blendMode(EXCLUSION);
                break;

                default: blendMode(ADD);

              }

              image(img1, 0, 0, windowWidth, windowHeight);
              image(img2, 0, 0,  windowWidth, windowHeight);
              noLoop();

            }

          } //end create new image function

          function windowResized() {
            resizeCanvas(windowWidth, windowHeight);
          }



          </script>`

This all works fine until I try to call the 'createNewImage' function, what could cause this? Thank you in advance.

Tagged:

Answers

  • The problem is caused by the way p5 works by default: it expects to find setup and draw functions in the global scope when it is first run (i.e. when the script is loaded). By putting setup and draw in the scope of your creatImage function, you've hidden them from p5 and so nothing will happen...

    The solution is to use instance mode. As per the example put the sketch code in a separate function; then in createImage runvar myp5 = new p5(sketch); to run the sketch...

    Notice how any reference to p5 in the sketch must use the argument being passed (in this case p) to access p5 methods and properties. It's also worth mentioning that from a JS perspective protecting the global scope in this way is better practice.

  • saisai
    edited September 2016

    Thank you both for your replies,

    I have updated my code and looked at both of the examples you linked and now the sketch works but the sketch() function is self invoking. I want the entire sketch to run only when the onClick event is fired so that the users input can be sent to the flickr api etc. I don't understand how the var myp5 = new p5(sketch); object works, why is it necessary to create this object and why does the entire sketch break when that line is removed?

    "index.html"

        <html>
        <head>
          <meta charset="UTF-8">
          <script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.3/p5.js"></script>
          <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        </head>
    
        <body>
    
          <p>
            Type a word to generate a new image
          </p>
    
          <input type="text" id="subject" value="rainbow">
    
          <button onclick="sketch()">Create New Image</button>
    
          <script src="js/index.js"></script>
    
    
        </body>
        </html>
    

    "index.js"

    var sketch = function( p ) {
    
    var keyword = document.getElementById("subject").value;
    
    $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?",
    {
      tags: keyword,
      tagmode: "any",
      format: "json"
    },
    
    function(data) {
      var image_1_index = Math.floor(Math.random() * data.items.length);
      var image_2_index = Math.floor(Math.random() * data.items.length);
    
      image1_src = data.items[image_1_index]['media']['m'].replace("_m", "_b");
      image2_src = data.items[image_2_index]['media']['m'].replace("_m", "_b");
    
    });
    
    var blend_index;
    
    p.preload = function() {
    img1 = p.loadImage(image1_src);
    img2 = p.loadImage(image2_src);
    imgs = [p.loadImage("assets/1.jpg"), p.loadImage("assets/2.jpg"), p.loadImage("assets/3.jpg"), p.loadImage("assets/4.jpg"), p.loadImage("assets/5.jpg"), p.loadImage("assets/0.jpg")];
    };
    
    
    p.setup = function() {
      p.createCanvas(p.windowWidth, p.windowHeight);
      image_index = p.int(p.random(5));
    };
    
    p.draw = function() {
      blend_index = p.int(p.random(4));
    
      switch (blend_index) {
        case 0: p.blendMode(p.ADD);
        break;
        case 1: p.blendMode(p.DARKEST);
        break;
        case 2: p.blendMode(p.LIGHTEST);
        break;
        case 3: p.blendMode(p.DIFFERENCE);
        break;
        case 4: p.blendMode(p.EXCLUSION);
        break;
    
        default: p.blendMode(p.ADD);
    
      };
    
      p.image(img1, 0, 0, p.windowWidth, p.windowHeight);
      p.image(img2, 0, 0,  p.windowWidth, p.windowHeight);
      p.noLoop();
    
    };
    };
    
    
    var myp5 = new p5(sketch);
    
  • You've misunderstood my suggestion slightly. Try:

    HTML

    <button onclick="runSketch()">Create New Image</button>

    JS

    var myp5;
    
    var runSketch = function() {
      myp5 = new p5(sketch);
    }
    

    Must admit I don't know what will happen with multiple button clicks. If you need multiple sketch instances running you'll need to store these on an array and dispose of as and when necessary. One thing to watch out for will be the possible perfomance hit of invoking many p5 sketches: you may want to set a limit...

  • saisai
    edited September 2016

    Ahhh yes It works!! I still don't understand how declaring an object makes a sketch run, this seems like a strange way of doing it. I'd expect it to be more like calling a function would cause the sketch to run? From the p5.js overview on github:

        var s = function( p ) {
    
          var x = 100; 
          var y = 100;
    
          p.setup = function() {
            p.createCanvas(700, 410);
          };
    
          p.draw = function() {
            p.background(0);
            p.fill(255);
            p.rect(x,y,50,50);
          };
        };
    
        var myp5 = new p5(s); //This fires the sketch!?!?!
    

    Either way it works now and I'm very thankful for your help, cheers!

  • I still don't understand how declaring an object makes a sketch run

    @sai: it's fairly simple. When you create an object you can choose for it to run any actions you like, including running any additional functions. In fact in JS you used to define objects as functions (the latest version now supports class declaration directly) :

       var MyObject = function(x, y, z) {
          this.x = x;
          this.y = y;
          this.z = z;
    
          this.init();
        }
    
        MyObject.prototype.init = function() {
          // do something else...
          console.log("init run");
        }
    
        var foo = new MyObject(1, 2, 3);
    

    Anyway - when you instantiate a new p5 object it just runs methods against the passed sketch parameter - just as it looks for setup and draw functions in the global scope...

  • edited September 2016

    A shorter version: :ar!

    new p5(p => {
      "use strict";
    
      const DIAM = 50;
      let x = 100, y = 100;
    
      p.setup = () => {
        p.createCanvas(700, 410);
        p.fill(0xff);
      };
    
      p.draw = () => {
        p.background(0).rect(x, y, DIAM, DIAM);
      };
    });
    
  • edited September 2016 Answer ✓

    I still don't understand how declaring an object makes a sketch run, ...

    • When we use keyword new upon constructor p5, a new object of datatype p5 is created.
    • It also ignites the whole p5.js framework to run the passed "sketch" function.
    • Moreover, p5.js framework looks for other callback functions, like preload(), setup(), draw(), etc.
    • However, the JS language doesn't allow external functions to access internal functions (and anything else).
    • 1 solution for this is to annex functions (and anything else) over an object.
    • Doing so makes possible to callback them from other places besides the local "sketch" function.
    • But not any object, but the very 1 of datatype p5 created by new p5(sketch);.
    • Luckily that very same reference is passed as argument to our sketch function by the p5 object.
    • By tradition, we name that p5 reference as p parameter for our sketch function.
    • And any established function, such as setup(), mousePressed(), etc., annexed to the p5 object, is recognized by the p5.js' API, and called back.
  • Ahhhh I get it, very well thought out. Awesome, thank you both for you help!!!

Sign In or Register to comment.