Emergent Circles

This is taken from Chapter 6: Emergence in [Generative Art]_. It uses randomness to develop an 'emergent' pattern among the circles.

P5 Instance Mode Revisited

This is a re-do of another re-do 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.js (Source)

/**
 * 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.

Testing The Re-Post

This is a re-do of the last post to make sure that a second post will work like I think it will.

The Re-Do

If you can't tell, there's a processing sketch in that blank box above this line. If you move your mouse over it p5 will draw some circles with the radii based on how fast your mouse is moving. If you click and drag it inverts the colors.

What This Accomplished

The first thing that happened when I added this post was that both the class based versions crashed and the function-based version started working. I don't think adding this was what caused it, it seems that p5 wasn't getting loadded before I created the p5 object.

var p5_restart = new p5(build_get_re_started, parent_div_id);

The question is really why it was working at all before. Anyway, I moved the script tag to download p5 from the CDN to the HTML HEAD and that seems to have fixed it (so far). The main thing is that this post can coexist with the previous post on the same page (and they don't interfere with each other), and the p5 library is only getting downloaded once (in the HEAD).

Testing With RST

This is a re-do of an old post I made to my main site about making a p5 restructured-text post in nikola based on the p5 get started tutorial. I'm repeating it to make sure I can get it to work here, since it's been so long since I've done it.

First the link to the p5 code:

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.10/p5.min.js"></script>

Note

On the old post I had included the html tag directly in the post (using the .. raw:: directive), but I thought that would be wasteful since you can have multiple posts on one page so I moved it the the BODY_END variable in the conf.py file which adds it to the end of the HTML body. This seemed to work for the first sketch but when I moved to the instance-mode version (see below) it broke (probably because p5 wasn't loaded when I was trying to create an instance of it) so I moved it to EXTRA_HEAD_DATA which so far seems to work. I also experimented with the meta-template plugin but since this repository is all about using P5, that didn't seem the way to go, since it would basically have the same problem as using the .. raw:: directive (which is really doing the same thing as the meta_template approach, meta_template just makes it a little more convenient to do it).

A Version That Worked Once

Here's the javascript for the broken version.

get_started.js (Source)

function setup() {
  var parent_div_id = "get_started";
  // this sets the width of the canvas to match the div (center column)
  canvas = createCanvas($("#" + parent_div_id).outerWidth(true), 300);
  canvas.parent(parent_div_id);
  background(255);
  strokeWeight(3);
  stroke(0, 0, 255);
  fill(255);
}

function mousePressed() {
  /* set background to blue */
  background(0, 0, 255);
}

function mouseReleased() {
  /* set background to white */
  background(255);
}

function draw() {
  var diameter;
  /* Draw circles that change diameter based on mouse speed */
  /* and color based on if mouse-pressed (or not pressed)   */
  if (mouseIsPressed) {
    fill(0, 0, 255);
    stroke(255);
  } else {
    fill(255);
    stroke(0, 0, 255);
  }
  diameter = pow(dist(pmouseX, pmouseY, mouseX, mouseY), 1.5);
  ellipse(mouseX, mouseY, diameter, diameter);
}

This next bit shows how to include the sketch in the post. The naming of the div is important ("get_started" in this case) - the javascript has to reference it (i.e. canvas.parent("get_started")) or the sketch will not appear right here (see the notes below). I'm also using it to get the width for the image. As you can see from the src='get_started.js part, this loads a file called 'get_started.js' that I created earlier and put in the files/posts/testing-with-rst folder (the directory name matches the name of this blog-post file - testing-with-rst.rst).

<div id="get_started">
    <script language="javascript" type="text/javascript" src='get_started.js'></script>
</div>

This at first worked to create a processing sketch in that blank box above this line, but once I added another sketch it broke. Originally this sketch seemed to break the second one, but then I fixed the second sketch and it broke this one. I realized that I was defining the same function names (e.g. mousePressed) in both so one of them was overwriting the other. Now it seems to work again, kind of... If you move your mouse cursor over fast enough that the circle is large while the sketch below is visible you'll see that the two of them have created some kind of super-canvas where they events over one also effects the other. So leaving this in has broken both of them to some degree. I think this function-based approach just won't work.

The Class-Based Version

My solution was to use the instance mode concept (which was surprisingly hard to find using google), basically wrapping everything in a class rather than using the global namespace.

If you can't tell, there's a processing sketch in that blank box above this line. If you move your mouse over it p5 will draw some circles with the radii based on how fast your mouse is moving. If you click and drag it inverts the colors.

Here's the javascript that created it.

get_started_2.js (Source)

/**
 * Instance mode implementation of a simple sketch that draws blue circles
 * on a white background when the user moves the mouse cursor over it, basing
 * the size of the circles on how fast the cursor is moving. If the user clicks
 * and drags the mouse it draws white circles on a blue background instead.
 **/
class GetStarted2 {
  /**
   * The constructor sets up the p5 functions
   *
   * @param {object} p: the `p5` object
   * @param {string} parent_div_id: ID of the div to attach the canvas to
   * @param {integer} height: Pixel-heght for the canvas
   **/
  constructor(p, parent_div_id, height){
    /**
     * Creates the canvas using ``height`` and the width of the parent div
     * Also sets up some coloring
     **/
    p.setup = function() {
      var canvas = p.createCanvas($("#" + parent_div_id).outerWidth(true),
                                  height);
      p.background(255);
      p.strokeWeight(3);
      p.stroke(0, 0, 255);
      p.fill(255);
    };

    /**
     * sets the background and fill to blue
     * and the stroke to white
     **/
    p.mousePressed = function() {
      p.background(0, 0, 255);
      p.fill(0, 0, 255);
      p.stroke(255);
    };

    /**
     * sets the background and fill to white
     * and the stroke to blue
     **/
    p.mouseReleased = function() {
      p.background(255);
      p.fill(255);
      p.stroke(0, 0, 255);
    };

    /**
     * Draws circles that change diameter based on mouse speed
     **/
    p.draw = function() {
      var diameter;
      diameter = p.pow(p.dist(p.pmouseX, p.mouseY, p.mouseX, p.mouseY), 1.5);
      p.ellipse(p.mouseX, p.mouseY, diameter, diameter);
    };
  }; // end constructor
};// end GetStarted2

/**
 * Builds the GetStarted2 sketch
 *
 * p5 thinks it's getting a function, not a class
 * so it doesn't use the `new` statement. This function
 * works around that and passes in some parameters
 *
 * @param {object} p: a ``p5`` instance
 **/
function build_get_started_2(p){
  return new GetStarted2(p, "get_started_2", 300);
};

var p5_retest = new p5(build_get_started_2, "get_started_2");

A Little Explanation

So, for my future self who will likely forget what this is doing, here's some notes.

var p5_retest = new p5(build_get_started_2, "get_started_2");

The last line creates a p5 object which calls the build_get_started_2 function, passing the instance into it. In addition, if you pass in the ID of the container, it will attach the sketch to it. So the second argument "get_started_2" does what you would otherwise do in the constructor like this:

p.canvas.parent("get_started_2");

If you omit the name of the parent ID when creating the p5 object then this line would need to go back into the constructor. I kind of like using the explicit p.canvas.parent("get_started_2"); call, but I already did something similar to it in the previous (broken) sketch so I thought I'd pass it in to the p5 constructor here so I'd have it documented.

The build_get_started_2 function:

function build_get_started_2(p){
  return new GetStarted2(p, "get_started_2", 300);
};

This works around a problem that I've often frequently with callback-functions - you have no control over how the function is going to be called, and without the code or explicit documentation you have to work with it as a black-box (I'm sure it's documented somewhere but I find the processing documentation for things other than the sketching to be a little hard to find). In this case I didn't really plan on it being a function, which probably wouldn't be a problem for someone who works with javascript a lot, but I'm not one of them. I'm not really taking advantage of any class-based features in my sketch so I could have converted it to a function, but I prefer classes (probably because I mostly code in python) so I'll probably be using them in the future, so it was good to get this figured out now. Also, since I had to make a builder anyway, this allowed me to put the constants (like the height) outside of the class and pass them in as parameters.

Other than that, the code is pretty close to the function-based version, except using th. p5 instance instead of the global functions.

Notes

  • In order to get the javascript into the final HTML you need to put in the listings folder at the root of the nikola folder and use the listing reStructuredText directive instead of include (this is a special nikola directive). This not only shows it but creates two links - the one with the file name links to a formatted version of the code and the (Source) link lets you download it as plain-text. Showing the code and creating another page seemed kind of redundant to me, but I think the purpose is to provide a link you can refer to outside of this post. You can't see the directive but it says:
.. listing:: get_started.js javascript
  • Another thing I forgot to mention in the other post was that nikola looks for the javascript (when it includes it in the html, not when it wants to show it as in the first note) in the files/posts/<post-name-slugified>/ directory, so in this case, besides putting the javascript in the listings folder, I also had to put another copy of it in files/posts/testing-with-rst/. There must be an easier way. Well, I guess it's not hard, it just seems inefficient.