P5 Instance Mode Revisited

This is a re-do of another post I did where I tried to remember how to get P5 working again. This is a class-based version that doesn't set everything up in the constructor the way I did it previously. This adds more lines, but it seems better organized to me.

The Code

/**
 * Class-based instance mode setup
 * This uses properties instead of building everything in the constructor
 */
class Sketch {
  /** Constructor to setup the p5 calls
   * @param {p5} p: the P5 object
   * @param {object} setup_arguments: setup settings
   * @param {object} colors: color settings
   * @param {object} inverted: inverted color settings
   */
  constructor(p, setup_arguments, colors, inverted) {
    this._reset = null;
    this._p = null;
    this.setup_arguments = setup_arguments;
    this.colors = colors;
    this.inverted = inverted;
    this.p = p;
  }

  /**
   * creates the closure to create the p5 canvas
   * This is so we have access to our 'this'
   */
  get setup() {
    let self = this;
    return function(){
      self.p.createCanvas(
        $("#" + self.setup_arguments.parent_id).outerWidth(true),
        self.setup_arguments.height);
      self.p.strokeWeight(self.setup_arguments.stroke_weight);
      self.reset();
    };
  }

  /**
   * creates the closure to handle a mouse press
   */
  get mouse_pressed() {
    let self = this;
    return function() {
        self.p.background(self.inverted.background);
        self.p.fill(self.inverted.fill);
        self.p.stroke(self.inverted.stroke);
    };
  }

  /**
   * closure to handle a mouse release
   */
  get mouse_released() {
    let self = this;
    return function() {
      self.reset(); 
    };
  }

  /**
   * creates the closure for the default setup
   */
  get reset() {
    let self = this;
    if (self._reset === null){
      self._reset = function() {
        self.p.background(self.colors.background);
        self.p.fill(self.colors.fill);
        self.p.stroke(self.colors.stroke);
      };
    }
    return self._reset;
  }

  /**
   * creates closure to draw circles that change diameter 
   * based on mouse speed
   **/
  get draw() {
    var self = this;
    return function(){
      var diameter;
      /* get the distance from last click, raise to 1.5 */
      diameter = self.p.pow(self.p.dist(self.p.pmouseX,
                                        self.p.pmouseY,
                                        self.p.mouseX,
                                        self.p.mouseY), 1.5);
      /* move the ellipse using the new diameter */
      self.p.ellipse(self.p.mouseX, self.p.mouseY, diameter, diameter);
    };
  } // end draw

  /**
   * the P5 object with our methods
   */
  get p() {
    return this._p;
  } // end get p

  /**
   * sets up the P5 object with our methods
   * @param {P5} original: the P5 object
   */
  set p(original) {
    this._p = original;
    this._p.draw = this.draw;
    this._p.setup = this.setup;
    this._p.mousePressed = this.mouse_pressed;
    this._p.mouseReleased = this.mouse_released;
  } // end set p
} // end Sketch

/** Settings objects **/
let COLOR = [50, 0, 255];
let WHITE = 255;

let setup_arguments = {
  stroke_weight: 3,
  height: 200,
  parent_id: "class-based-instance-mode"
}

let inverted_colors = {
  background: COLOR,
  fill: COLOR,
  stroke: WHITE,
  height: 50
}

let default_colors = {
  background: WHITE,
  fill: WHITE,
  stroke: COLOR
}

/** Build the code **/

function builder(p) {
  sketch = new Sketch(p, setup_arguments, default_colors, inverted_colors);
}

var sketch = new p5(builder, setup_arguments.parent_id);

The Sketch

For completeness, here is the html to include the sketch and the output of the sketch.

<div id="class-based-instance-mode">
    <script language="javascript" type="text/javascript" src='class_based_instance_mode.js'></script>
</div>

The Closures

Although I like the way the code looks when things are set up (more or less) outside of the constructor, it created the problem that the object doesn't have access to the right this when P5 calls the methods I created. To get around this I used the get methods to create closures that bind the right context to the implemented `p5` methods.

In addition, since the setup and mouse_released both set the same colors I created a reset function/property that stores the creation of the function when it's first created. I didn't bother to store mouse_pressed because the getter only gets used once (in the constructor), after that it gets assigned to the p5 object anyway.

Putting the p attribute in a setter allowed me to set it up outside of the constructor, but makes it more fragile, as you have to make sure that it gets set after the other attributes or it will be an error.

A New Problem

I just noticed that the Mouse Presses trigger the inverting of colors for all the boxes on the page. Not a big deal in this case, I guess, although that might be something to work on later.