August 2014

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            








« A new version of DesignScript on Autodesk Labs | Main | Using an Arduino with an arcade joystick to control LEDs »

July 22, 2013

Integrating Paper.js with Leap Motion using JavaScript

Today marks the public release of the Leap Motion controller: people who pre-ordered devices will start to receive them today, so it seemed a good time to post something related to it.

Last week I came across a really interesting JavaScript library called Paper.js. According to its website, it’s “an open source vector graphics scripting framework that runs on top of the HTML5 Canvas. It offers a clean Scene Graph / Document Object Model and a lot of powerful functionality to create and work with vector graphics and bezier curves, all neatly wrapped up in a well designed, consistent and clean programming interface.

If you want to validate this assertion, just take a look at these examples, clicking down through the list and checking out the source code using the link in the top-right of each page. Very cool stuff.

For instance, here’s a really neat example – especially relevant to people who care about CAD – of intersections between “paths” calculated dynamically:

Intersecting paths courtesy of Paper.js

Given the fact there’s a JavaScript library available for the Leap Motion controller called LeapJS, it seemed like a fun idea to hook the two together. I’m fairly familiar with the LeapJS implementation – having created a few (as yet unpublished) prototypes to make use of AutoCAD 2014’s JavaScript API – so I just needed to take a look at what needed to be done with Paper.js.

Paper.js provides a scripting environment known as PaperScript. It’s based on JavaScript but adds additional capabilities to the language and abstracts away boilerplate code. Most of the posted Paper.js examples are in PaperScript, so there’s a bit more work to do if we’re going to adapt any to work from plain old JavaScript.

The example I chose to hook up to Leap Motion is called “Lines” and is part of the Paper.js download (I’m using v0.9.8). Any of the samples in the “examples\Animated” folder would probably be good candidates for integrating with Leap Motion, though.

The first step was to get the PaperScript code working in JavaScript, as per the instructions in this tutorial. This mostly involved adding a few manual calls to the Paper.js framework and changing the PaperScript code to replace its (very convenient) arithmetic operations between points (etc.) with more manual approaches such as adding respective X and Y values together and using trigonometry to calculate the 2D vector from a distance at a certain angle. Not that hard to do, but it needed doing.

Here’s the HTML page with embedded JavaScript code that integrates LeapJS with Paper.js:

<!DOCTYPE html>

<html>

  <head>

    <!-- Reference the Paper.js and Leap.js libraries (minified

         versions of each also exist -->

    <script type="text/javascript" src="js/paper.js"></script>

    <script type="text/javascript" src="js/leap.js"></script>

    <script type="text/javascript">

 

    // Global variables

 

    var amount = 45; // The number of lines that make up the ball

    var ballSize = 200; // The initial size of the ball

 

    var position; // The last position of the ball

    var cursor;   // The position of the cursor (also set by Leap)

    var children; // The lines that make up the ball

 

    // As we're in JavaScript (rather than PaperScript), we need to

    // jump through a few extra hoops to get Paper.js working

 

    paper.install(window);

 

    // Our iterate function that does the bulk of the work

 

    function iterate(count) {

 

      // We need to calculate deltas between points manually

      // (PaperScript provides arithmetic operators on - for

      // instance - points, which eases this pain)

 

      // Find out how the cursor has moved since the last

      // iteration (and move it a tenth of the distance along

      // that path, presumably for smoother animation)

 

      var deltaX = cursor.x - position.x;

      var deltaY = cursor.y - position.y;

      position.x += deltaX / 10;

      position.y += deltaY / 10;

 

      // Loop through the lines in the ball and adjust them

 

      for (var i = 1; i < amount; i++) {

        var path = children[i];

        var length = Math.abs(Math.sin(i + count / 40) * ballSize);

        var am2pi = 2 * Math.PI / amount; // Common value

 

        // Trigonometric operation needs to be done manually

        // (PaperScript allows you to add vectors defined by

        // angle and length)

 

        path.segments = [

          new Point(

            position.x + deltaX / 1.5, position.y + deltaY / 1.5

          ),

          new Point(

            position.x + length * Math.sin(i * am2pi),

            position.y + length * Math.cos(i * am2pi)

          ),

          new Point(

            position.x + length * Math.sin((i + 1) * am2pi),

            position.y + length * Math.cos((i + 1) * am2pi)

          )

        ];

 

        // The colour varies based on the size of the line

        // (but shouldn't depend on the size of the ball)

 

        path.fillColor.hue = count - (length * 200 / ballSize);

      }

    }

 

    // Our main startup function

 

    window.onload = function () {

 

      // Define the Canvas element as our target

 

      paper.setup('myCanvas');

 

      // Assign (initial) values to our globals

 

      position = view.center;

      cursor = view.center;

      children = project.activeLayer.children;

 

      // Create the lines in our ball

 

      for (var i = 0; i < amount; i++) {

        var path = new Path({

          fillColor: {

            hue: 1,

            saturation: 1,

            brightness: Math.random()

          },

          closed: true

        });

      }

 

      // We need a tool to intercept our mouse movements

 

      var tool = new Tool();

      tool.onMouseMove = function (event) {

        iterate();

        cursor = event.point;

      }

 

      // We also iterate when the mouse isn't moving

 

      view.onFrame = function (event) {

        iterate(event.count);

      }

    }

 

    // If the Leap Motion library is available, set up our

    // Leap loop. Will only be called if a controller is

    // available, though

 

    if (typeof Leap !== "undefined")

    {

      // Setup Leap loop with frame callback function

 

      Leap.loop(function (frame) {

 

        // We just want to get the first hand

 

        if (frame.hands.length > 0) {

          var hand = frame.hands[0];

 

          // Define the size of the ball based on the height of

          // the hand

 

          ballSize = hand.sphereCenter[1] * 3;

 

          // And we set the location of the ball - relative to

          // the center of the view - based on the position of the

          // hand in Leap space

 

          cursor =

            new Point(

              hand.sphereCenter[0] * 4 + view.size.width / 2,

              hand.sphereCenter[2] * 4 + view.size.height / 2

            );

        }

      })

    }

    </script>

  </head>

  <body style='background-color:black'>

    <canvas id="myCanvas" resize></canvas>

  </body>

</html>

You should be able to see it in action below or by opening this link (assuming you have a compatible browser, which means it won't work with IE8 and below… if using IE9 or above and don’t see anything, make sure you don’t have “compatibility mode” enabled for this site). The page doesn’t actually require a Leap Motion controller to work – if you move your mouse cursor over the window, the ball of lines should react. Using Leap Motion you can not only move the ball around but also change its size by adjusting the elevation of your hand.




It was a fun mini-project to get these two JavaScript libraries working in the same HTML page. I’m definitely planning to do more with Paper.js as the opportunity arises (and will inevitably work more with Leap Motion, too: they seem to have made solid progress with their runtime in recent weeks/months, and it’ll certainly be interesting to see how people respond to the product’s release).

[A reminder that I'm on holiday for the whole of this week. I have a couple of posts queued up for the rest of the week about an Arduino project I've been fooling around with.]

Update:

See this post. It turns out you can make use of JavaScript libraries directly from PaperScript, which means a lot of the work performed for this post is redundant. Ho hum.

blog comments powered by Disqus

Feed/Share

10 Random Posts