registerPreloadMethod effects

I'm trying to write a p5 library that creates a new object. On loadObject function I need 2 parameters.
When I register the function to be used at preload, I notice that the 2nd parameter is of type function no matter what I do..
So, the 2nd and 3rd parameters are considered callback and callbackerror functions?
Beside turning the first parameter into an array, are there any other way to serve two parameters to a preload function?
Can a function not registered as a preload method be used at preload?

Am I doing any sense?

Answers

  • I already read it (by the way, registerPreloadMethod('loadSound', 'p5') is wrong: p5 is not a string).
    I also looked at the source code, but it's too advanced for someone without previous experience on javascript like me.
    I'm looking for some guidance.

  • edited March 2016

    I've got no experience in library dev either. 8-|
    But you're spot on about registerPreloadMethod()'s 2nd parameter not being a String.
    It's instead the prototype object where the registered method (1st parameter) belongs to:
    https://GitHub.com/processing/p5.js/blob/master/src/core/core.js#L531

    p5.prototype.registerPreloadMethod = function(fnString, obj) {
      // obj = obj || p5.prototype;
      if (!p5.prototype._preloadMethods.hasOwnProperty(fnString)) {
        p5.prototype._preloadMethods[fnString] = obj;
      }
    };
    

    Also I've spotted another mistake at the same sentence:
    https://GitHub.com/processing/p5.js/wiki/Libraries#use-registerpreloadmethod-to-register-names-of-methods-with-p5-that-may-be-called-in-preload

    ... call registerPreloadMethod() with the name of the method to register, and the name of the object the method belongs to (defaults to p5).

    "(defaults to p5)" is no longer true anymore b/c obj = obj || p5.prototype; is commented out.
    It means the 2nd parameter is obligatory too. Moreover it was p5.prototype, not just p5. b-(

    P.S.: I've corrected that wiki just now! :ar!

    Anyways, you should study how other libraries use registerPreloadMethod(): :-B
    https://GitHub.com/processing/p5.js-sound/blob/master/src/soundfile.js#L140

    p5.prototype.registerPreloadMethod('loadSound', p5.prototype);
    

    https://GitHub.com/processing/p5.js-sound/blob/master/src/soundfile.js#L176

    p5.prototype.loadSound = function(path, callback, onerror, whileLoading){
      // if loading locally without a server
      if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined' ) {
        alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
      }
    
      var s = new p5.SoundFile(path, callback, onerror, whileLoading);
      return s;
    };
    
  • Perhaps my doubts are related to some incoherence on registerPreloadMethod, like in https://github.com/processing/p5.js/issues/1229.
    For now, it seems I can't use more than one argument.

  • edited March 2016
    • That link doesn't even mention registerPreloadMethod(). :-@
    • As you can see from my reply, I've posted the source code for registerPreloadMethod().
    • And I've clarified the meaning of its 2nd parameter.
    • I can't figure out what you're asking exactly. Sorry... 8-|
  • Of course you're right. The link is about callback functions.
    My weirdness derive from the fact that my 2nd parameter, that should be a string, suddenly turns to a function when I do registerPreloadMethod().
    I don't understand the magic involved. :((
    Time to move on. I'll keep studying.
    Thank you for your time.
    I'll be back...

  • Answer ✓

    Just some last explanation attempt: registerPreloadMethod() does nothing more than append an extra entry to object _preloadMethods.

    It's already pre-initialized this way: https://GitHub.com/processing/p5.js/blob/master/src/core/core.js#L517

    p5.prototype._preloadMethods = {
      loadJSON: p5.prototype,
      loadImage: p5.prototype,
      loadStrings: p5.prototype,
      loadXML: p5.prototype,
      loadShape: p5.prototype,
      loadTable: p5.prototype,
      loadFont: p5.prototype
    };
    

    Something like p5.prototype.registerPreloadMethod('loadSound', p5.prototype); merely adds this following entry to _preloadMethods: { loadSound: p5.prototype }

  • Now that I cleared my head, let me explain what happened:
    I created a loadX function that return a X object.
    I added it to preload methods.
    Unlike other preload methods, this function needs 2 parameters (string, string), BUT, and this was where I messed up, the 2nd was optional with a default value.
    _wrapPreload add _decrementPreload() after the last parameter. My mistake was to think that all defined parameters where considered. Instead, only the "real" parameters (used when calling the function) are counted. My 2nd optional parameter was replaced by _decrementPreload() function.
    Bottom line: preload methods can't use optional parameters.
    Thanks for your patience. Now I know a little more magic. :)>-

  • Bottom line: preload methods can't use optional parameters.

    I confess I still dunno how the preload() voodoo stuff actually works.
    But they seem to accept optional parameters. For example loadTable():

    http://p5js.org/reference/#/p5/loadTable

  • edited March 2016

    Yes. But in LoadTable() all parameters are read in a for loop and tested against "function" type.
    I was using optional parameters "by the book": function(a, b = 1).
    It doesn't work this way.

  • Without seeing your code (or even your environment*) it's hard to see what the problem is. If for whatever reason you really can only have a single argument (the docs don't suggest this is the case) could you pass in an object? That's fairly standard in JS, since it's so easy to create them on the fly:

    function foo(options) {
      var opts = options || {};//defaults to empty object
      var a = opts.a;
      var b = opts.b || 1;//defaults to 1
      // do stuff...
    }
    
    // call foo:
    foo({a: 'hello', b: 3});
    

    * Note that default arguments are a new feature in JS so may not be supported by your browser; and are perhaps not supported by the p5js registerPreload method - so that could also be the root of your problem.

  • After understand a little more about callbacks and preload(), GoToLoop link to loadTable was what I needed. My main function is like this:

    p5.prototype.loadX = function () {
      'use strict';
      var name = arguments[0];
      var path = "";
      var callback = null;
      if (typeof(arguments[1]) === 'string') path = arguments[1];
      for (var i = 1; i < arguments.length; i++) {
        if (typeof(arguments[i]) === 'function') callback = arguments[i];
      }
      var x =  new p5.X(name, path);
      if (typeof(callback) === 'function') callback(x);
      return x;
    }
    

    and it works!
    So I moved to the next problem.
    Thank you all!

  • edited April 2016

    Seems like your loadX() got multiple signatures.
    Here's a quick tweaked version. Dunno whether it exactly matches the same behavior as yours though:

    p5.prototype.loadX = function (name, path, callback) {
      "use strict"
    
      if (typeof callback === 'function') {
        const x = new p5.X(name, path)
        callback(x)
        return x
      }
    
      const x = new p5.X(name, '')
      typeof path === 'function' && path(x)
      return x
    }
    
  • That could work.
    What I learned is that a preload method ALWAYS have a callback function. So, in this special case, if (typeof(callback) === 'function') callback(x); is necessary. callback will be a explicitly written callback function OR the added _decrementPreload(). Without calling it back, you don't get out of preload().
    I never coded on a language that gives so much rope to hang on, and I'm not starting to talk about async... :))
    On top of that, p5 is great, and I'm sure I will finish my library in the next few days.

  • @GoToLoop: since it seems you can rely on there always being a callback function this should suffice:

    p5.prototype.loadX = function (name, path, callback) {
      "use strict";
    
      var x;
    
      if(typeof path === 'function') {
        callback = path;
        path = '';
      }
    
      x = new p5.X(name, path);
      callback(x);
      return x;
    
    }
    

    @linuxman:

    I never coded on a language that gives so much rope to hang on

    JS is special :(|)

    I've been recommending the You don't know JS books a lot here lately: they do a good job of explaining some of the language's supposed idiosyncrasies; and also its flexibility :-B

  • edited April 2016

    @blindfish, I was trying to stay as much close to @linuxman's solution. :\">
    Nonetheless, here comes another over complicated (w/o any if () blocks) but shorter solution: :ar!

    p5.prototype.loadX = function (name, path, callback) {
      "use strict"
      typeof path === 'function' && (callback = path, path = '')
      const x = new p5.X(name, path)
      typeof callback === 'function' && callback(x)
      return x
    }
    
  • edited April 2016

    ... you can rely on there always being a callback function...

    That's hardly true. When using preload(), passing a callback is usually pointless: :-B
    http://p5js.org/reference/#/p5/preload

    That's why we still need to make sure callback parameter is indeed a function before invoking it: L-)

    1. typeof callback === 'function' && callback(x)
    2. if (typeof callback === 'function') callback(x)
  • edited April 2016

    Yesterday I finished my library (it's a port of Processing Ptmx).
    Now I'm working on some examples and soon I'll upload it to github. :)
    I decided to stick with arguments[] and embrace JS flexibility. With 4 parameters (3 optional), load method looks like this:

    p5.prototype.registerPreloadMethod('loadTiledMap', p5.prototype);
    
    *  method loadTiledMap
    *  param  {String}   mapName               Map name.
    *  param  {String}   [imagePath]           Path to the image file(s). Default: "/".
    *  param  {Number}   [transparentOffset]   Maximum difference on RGB channels to apply Tile
    *                                           transparency. Default: 4.
    *  param  {Function} [callback]            Name of a function to call once map loads.
    *  return {TiledMap}                       Returns a p5.TiledMap.
    
    p5.prototype.loadTiledMap = function () {
      'use strict';
      var mapname = arguments[0];
      var imagepath = "";
      var transparentoffset = 4;
      var callback = null;
      for (var i = 1; i < arguments.length; i++) {
        if (typeof(arguments[i]) === 'string') imagepath = arguments[i];
        if (typeof(arguments[i]) === 'number') transparentoffset = arguments[i];
        if (typeof(arguments[i]) === 'function') callback = arguments[i];
      }
      if(imagepath[-1] != "/") imagepath = imagepath + "/";
      if(!TileMaps) throw "No Tiled Map found!";
      if(!TileMaps[mapname]) throw "No Tiled Map named "+mapname+" found!";
      var t =  new p5.TiledMap(mapname, imagepath, transparentoffset);
      if (typeof(callback) === 'function') callback(t);
      return t;
    }
    

    In fact, since all optional arguments are of different types, they are interchangeable, but who cares? ;)
    Also, I shouldn't need to test callback function, but it's better be safe than sorry. Someone can call loadTiledMap outside preload without a callback function.

  • Finally it's done!
    p5.tiledmap - Add Tiled maps to p5.js - can be found at https://github.com/linux-man/p5.tiledmap.
    Your feedback is highly appreciated.
    Thanks for all the support.

Sign In or Register to comment.