Versions Matter. Finding Bugs and Updating CLJSJS ClojureScript Packages
Written by Bunkers on April 5, 2017
TL;DR
There's a big spoiler in the title of this post! I was developing the logo project and thought I'd discovered a bug. Turns out I was using an old version of Matter.JS and I set about upgrading the CLJS JS package.
Constraints
Last time I'd reached the point of having a world defined using a ClojureScript data structure. Running the code would have a couple of circle bodies drop in and bounce around.
To start building the structure of the logo I need to join objects together. In Matter.JS, bodies you join bodies together using constraints. I'm going to cover constraints more in another post, but they join bodies together using a kind of invisible thread, that can be hard like a metal rod or stretchy like elastic.
Similarly to my first experiments with Matter.JS, the Coding Train has a tutorial covering all the techniques needed to define and use them.
I started in the same vain as that tutorial and created a constraint to join my two circular bodies together. You could imagine how I could build up the letters of the logo this way.
In future I'll convert each letter to something Matter.JS calls a composite, but as the 'world' is a composite itself that shouldn't be a big deal. A composite is just a body that is a collection of other bodies, constraints and composites, so it creates a hierarchical structure.
Mouse Trap
My experiment was working and the two bodies fell as if attached. When they bounced it looked like a spring was joining them together and it was satisfying to watch! But I wanted to interact with it. Pick up one of the balls with my mouse and fling it around the screen.
Fortunately Matter.JS provides a MouseConstraint
for just that purpose. Setting one up involves creating a Mouse
object which captures all the mouse events. You pass the Mouse
object to the MouseConstraint
when you create it. Add the constraint to your World
and you can start clicking on bodies and dragging them around.
Except mine didn't work! No matter what I did I couldn't get it to fire the right events. The MouseConstraint
works by having your mouse pointer at one end of your string/rod/elastic and attaching any body you click on to the other.
Bug Trap
I started debugging it. The tools for this are excellent. Having source maps for the ClojureScript code so you can set breakpoints is fantastic. Add in the BinaryAge tools for logging objects and the tools are pretty advanced.
To being with I experimented with the pixel ratio as is a problem in the tutorial I was following. However, it seems like Processing.JS doesn't support that idea and scales the coordinate system instead. This has made me wonder further about creating a version of Quil backed by P5.js. It's fast becoming the JavaScript Processing-like library of choice, and seems to be in more active development.
Back to debugging! I found that I could attach to the events on the Mouse
object and they fired correctly. However the MouseConstraint
's update
method, where the meat of the logic happens, never got called. A bit more digging and I could see that it wasn't called via a mouse event but an event called tick
. This event gets triggered by Matter.JS's Runner
each time it updates the World
. I wasn't using that and manually calling update
on my Engine
object in my Quil
sketch's update-state
function.
First I tried a work around and triggered the tick
event myself. Suddenly it all worked! "I've found a bug too", I thought, but then I realised that Daniel Schiffman had stopped using the Runner
in the tutorial, and his worked.
Searching forums, GitHub and Stack Overflow for related issues, I came across this issue, identified as a bug and now closed. So why wasn't my code working? The user reporting the bug even posted the same workaround I was using.
I'd noticed previously that the version of Matter.JS in the CLJSJS package was old, and looking at the releases I could see a latest version of 0.10.0. The version in the package was 0.9.1 and so didn't include this fix.
External Repairs
I took a deep breath and decided I would update the package. The guide for creating new packages is well written, and made me realise that this wasn't going to be the horrendous task I first thought.
Installing boot
was simple via Homebrew
brew install boot-clj |
I created a fork of the repository, cloned it and started by updating the build.boot
file to grab the 0.10.0 version. This was as simple as updating the version numbers, and creating a checksum by downloading the zip myself and running:
openssl md5 matter-js-0.10.0.zip |
The bulk of the work was with updating the externs. I know there are tools to do the generation of this for you, but I decided to first run a diff of the two versions and see what objects and methods were new.
It turned out there wasn't that many! I added them in to the matter.ext.js
file, and installed my new package. This meant I could update my package version number in my project.clj
file and it would start using my new version.
Feeling proud of myself I restarted everything and found it didn't work! I'd successfully got the new version of Matter.JS included, but the events still weren't firing.
It was at this point that I discovered a little link at the top of the Matter.JS releases page on GitHub.
Hidden away were two more releases 0.11.0 and 0.12.0. Slightly despondent I setup about the process again, but this time using the latest version 0.12.0.
It wasn't as straightforward this time and there were a number of new objects and methods to update. A global change involving the handling of calls to require
also meant that I had a lot of changes to wade through.
However, this time I updated everything and restarted my project to find I'd fixed it! It was an immensely satisfying feeling, and made playing around with those two yellow circles all the more enjoyable.
Pull Request
There's some tidying up work to do before I submit a pull request with these updates. First of all I've manually added all the new objects and methods for handling things like plugins. I've not tested any of this yet, so that would be a good start! I also made a couple of commits before fixing it, so I'll need to tidy up the history too.
In the meantime, if you want to install it yourself you can clone my fork. You will have to do things like setup boot
yourself and to be honest it probably isn't worth the hassle. I'd just use the workaround which is:
(.trigger js/Matter.Events engine "tick") |
It works - as long as you've got your engine
object to pass in of course - so why fix it?
I'll update this post with a link to the pull request when I submit it. That way, if you're interested, you can track it. Incidentally you can just get an alert when it closes via Tell Me When It Closes. It's a useful tool that means you don't have to watch issues and get all the updates before someone fixes it.