Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for p5 instance mode #22

Merged
merged 28 commits into from
Feb 24, 2016

Conversation

toolness
Copy link
Contributor

This adds support for p5 instance mode in p5.play, which fixes #12.

Global Sketch Properties

A number of variables, like allSprites, were formerly global, but now need to be bound to the lifetime of a particular sketch instance. I found doing this a bit tricky because there seems to be no easy way to initialize such properties upon sketch initialization; I've documented this limitation and my workaround for it at processing/p5.js#1263.

Constructor Changes

One particular challenge of this feature is the fact that almost all of the p5.play classes, with the notable exception of Group, now need to be bound to a p5 instance; I've changed their constructors to take this instance as their first argument. However, this makes it a bit tricky to support legacy code.

Ideally, we should simply expose the instantiation of these classes via factory functions on p5.prototype, which is already being done with createSprite, loadAnimation, and loadSpriteSheet--however, the constructors of all these classes are also publicly documented, which means that it's entirely possible for legacy code to be instantiating them directly--for example, the examples/sprites_with_sheet.js example directly calls new SpriteSheet() at one point instead of using loadSpriteSheet().

An alternative here is to simply not support legacy code when it's problematic, and increment the major version number in the next release as per semantic versioning to indicate backwards-incompatible API changes.

As per @islemaster's suggestion, we're exposing curried versions of the constructors on all created p5 instances, so legacy code still works!

Formerly Global Variables

In order for this to work properly, I had to enclose the entire library in an IIFE. Previously, only about half of the library was enclosed in one, which meant that some of its private implementation functions, like construct, were actually in global scope (see below for a full list). While all the examples in the examples/ directory still work fine, I hope that third-party code still works. Might be a good idea to QA this somehow.

The following were formerly globals but are now enclosed in an IIFE:

  • Sprite (now exposed via p5.prototype.Sprite)
  • Camera (now exposed via p5.prototype.Camera)
  • cameraPush
  • cameraPop
  • Group (now exposed via p5.prototype.Group)
  • CircleCollider
  • AABB
  • Animation (now exposed via p5.prototype.Animation)
  • SpriteSheet (now exposed via p5.prototype.SpriteSheet)
  • Quadtree
  • updateTree
  • construct

Note that even for the items that are now exposed via p5.prototype, they won't become globals until and unless p5 enters global mode on page load.

Examples Changes

Nothing was changed in the examples, because I wanted to make sure p5.play still works with legacy code. However, I did get rid of the extra copy of p5.play.js in f1363d1 so that we don't need to pollute the repository's commit history by constantly updating two separate copies of the library. This is still true, but has been spun out into #36.

Other Notes

Of great help here was:

jshint lib/p5.play.js | grep "is not defined"

This logs any undefined globals, which was useful in finding p5 global methods/properties that needed to be bound to p5 instances.

To Do

  • Add more tests.
  • Consider exposing Animation, at least in a deprecated way, as its constructor is currently publicly documented.
  • Consider exposing Camera, at least in a deprecated way, as its constructor is currently publicly documented.
  • Consider exposing Sprite, at least in a deprecated way, as its constructor is currently publicly documented.
  • Add/remove/update documentation to reflect any changes in API (deprecated methods, pInst as first argument for constructors, etc).

@toolness
Copy link
Contributor Author

Hey @islemaster, would you be interested in code reviewing this at all? It'd be really great to have a second pair of eyes on this, but no worries if not.

@islemaster
Copy link
Contributor

I'll be happy to. I can take a look later today.
On Feb 17, 2016 5:21 AM, "Atul Varma" notifications@github.com wrote:

Hey @islemaster https://github.com/islemaster, would you be interested
in code reviewing this at all? It'd be really great to have a second pair
of eyes on this, but no worries if not.


Reply to this email directly or view it on GitHub
https://github.com/molleindustria/p5.play/pull/22#issuecomment-185202620
.

@toolness
Copy link
Contributor Author

Awesome, thanks!!! I just updated the description of this PR to reflect some API design considerations too.

// The above will create functions createVector and loadImage, which can be
// used similar to p5 global mode--however, they're bound to specific p5
// instances, and can thus be used outside of global mode.
function createPInstBinder(pInst) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how I feel about this createPInstBinder thing--I admit that I did it partly out of laziness, but also because I didn't want to hamper readability by having a million things be prefixed with pInst. or this..

@islemaster
Copy link
Contributor

A couple of initial thoughts, more on the way.

  1. There's a lot going on here, and a few improvements that look like they could be extracted and merged as separate PRs first. It could help us reason cleanly about this change. Candidates:
  2. For all the constructors you're deprecating, did you consider exposing curried versions on the instance that use the original API? That way client code could just update from new Group() to new p5instance.Group() (instead of new Group(p5Instance)).

Thanks for tackling this big change!

@toolness
Copy link
Contributor Author

Great ideas! I've just filed those PRs, and will implement your suggestion for the curried constructors.

@toolness
Copy link
Contributor Author

Oh, do you have time to take a look at the new PRs? It's ok if not, just let me know.

@islemaster
Copy link
Contributor

I do ; sorry, I'm on Pacific time and can review more in about 90 minutes.
On Feb 17, 2016 7:59 AM, "Atul Varma" notifications@github.com wrote:

Oh, do you have time to take a look at the new PRs? It's ok if not, just
let me know.


Reply to this email directly or view it on GitHub
https://github.com/molleindustria/p5.play/pull/22#issuecomment-185270926
.

@toolness
Copy link
Contributor Author

Awesome, thanks!

So in 768b414 I added the currying you suggested.

Now I think all that's left is to decide whether we also want to expose the non-documented constructors CircleCollider, AABB, and Quadtree, and possibly add more unit tests.

// This returns a factory function, suitable for passing to
// defineLazyP5Property, that returns a subclass of the given
// constructor that is always bound to a particular p5 instance.
function boundConstructorFactory(constructor) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ug, it's a factory for creating constructors... Not a huge fan of the multiple levels of indirection but it's all I could think of for now.


defineLazyP5Property('allSprites', function() { return new Group(); });

p5.prototype.spriteUpdate = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a problem - the whole reason you wrote defineLazyP5Property? I assume the value of spriteUpdate isn't shared across all p5 instances. If this is working, why can't you just

p5.prototype.allSprites = new Group():

since Group has no dependencies on p5 either? (Probably because you interact with allSprites by modifying it, never overwriting the reference, right?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be easiest to answer these questions by writing some tests that verify these properties are not shared when you make multiple instances.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good idea about the tests! I'll do that.

So the main reason I wrote defineLazyP5Property was actually so that mutable properties, like allSprites, would be per-instance, rather than global. e.g. so that calling sketch1.allSprites.add(mySprite) wouldn't also add the sprite to sketch2.allSprites. Since booleans like spriteUpdate are immutable, and since assigning to the property will actually set it on a particular p5 instance rather than the prototype, I thought it would be OK to put the "starting" value on the prototype. Does that make sense?

In any case, I totally agree that tests would serve both as good executable documentation and as protection against regressions, so I'll do that!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that make sense now. Thanks for clarifying!

@islemaster
Copy link
Contributor

Recommendation: Also break the following commit out into its own PR:

@toolness
Copy link
Contributor Author

Good idea about splitting f1363d1 into its own PR, I will do that!

Update: Just filed #36 for this.

@islemaster
Copy link
Contributor

After all that, I don't think I have anything else to add. Thanks for your patience with my gradual understanding of the initialization challenges around p5 instance properties!

@toolness
Copy link
Contributor Author

Awesome, thanks @islemaster! And no worries, I'm actually learning how this code works myself too, hehe. It's been very helpful to have a second set of eyes reading over this.

@toolness toolness changed the title [WIP] Add support for p5 instance mode Add support for p5 instance mode Feb 24, 2016
toolness added a commit that referenced this pull request Feb 24, 2016
Add support for p5 instance mode
@toolness toolness merged commit e3e2375 into quinton-ashley:master Feb 24, 2016
islemaster referenced this pull request Jul 29, 2016
Added a global !mouseIsPressed in addition to !this.mouseIsPressed because when you attempt to drag a sprite you are likely to roll out of its collider setting this.mouseIsPressed to false and triggering the release function. I hope it doesn't break anything.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Instance mode with p5.play
3 participants