leapmotion / leapjs-rigged-hand

Control hand models with the Leap Motion
Apache License 2.0
95 stars 33 forks source link

Socket.io problem #17

Closed nakulcr7 closed 9 years ago

nakulcr7 commented 9 years ago

I am working on making an interface for a Leap motion controlled robot. From the server side node.js, I am emitting an event 'action' which contains the data to be displayed on a HTML page. I am using leapjs-rigged-hand for visualization (https://github.com/leapmotion/leapjs-rigged-hand)

  <body>
      <div id="display"><span id="direction">undefined</span></div>
  </body>

  <script type="text/javascript">
        var socket = io();
        socket.on('action', function(data) {
              $('$display').html(data);
        });

        var controller = new Leap.Controller;
        (controller).use('riggedHand', {
              scale: 1.5
        })
        .connect();

        controller.on('riggedHand.meshAdded', function(handMesh, leapHand){
              handMesh.material.opacity = 1;
              handMesh.material.color.set('#7b4b2a');
              handMesh.material.ambient = handMesh.material.color;
        });
  </script>

Shown above is the relevant part of the code. The problem is that socket callback works only before the first "riggedHand.meshAdded" event is fired.

How do I make them work simultaneously?

Thank you

raimo commented 9 years ago

The riggedHand.meshAdded can occasionally take quite a lot of time. If you want your application to not do anything before it's fired, maybe you should wait for it to happen first (and present a loading animation in the meanwhile). You should probably wait until both of them are fired and then execute a callback.

Since JavaScript is single-threaded, this can easily be achieved by setting initialization flags in both the callbacks and then running the final callback that just checks if both of the flags are set.

    // Store all the initialization data that we want to use in one callback.
    var inits = {actionData: false, meshAdded: false};

    // Finalization callback that won't execute finalization until we have all the data.
    function attemptFinishingInitialization() {
      if (inits.actionData && inits.meshAdded) {
          $('$display').html(inits.actionData);
      }
    }

    // In callbacks, store the init state and call attemptFinishingInitialization:

    socket.on('action', function(data) {
        inits.actionData = data;
        attemptFinishingInitialization();
    });

    controller.on('riggedHand.meshAdded', function(handMesh, leapHand){
        handMesh.material.opacity = 1;
        handMesh.material.color.set('#7b4b2a');
        handMesh.material.ambient = handMesh.material.color;
        inits.meshAdded = true;
        attemptFinishingInitialization();
    });
nakulcr7 commented 9 years ago

Thank you for your reply. I tried your suggestion, it still isn't working. The jquery part and the hand visualization aren't working independently of each other. For example, if I comment out the visualization part(seen below), data is displayed in the

. However, if I uncomment, only the riggedHand part of the code works.

<body>
      <div id="display"><span id="direction">undefined</span></div>
  </body>

  <script type="text/javascript">
        var socket = io();
        socket.on('action', function(data) {
              $('$display').html(data);
        });

        /*var controller = new Leap.Controller;
        (controller).use('riggedHand', {
              scale: 1.5
        })
        .connect();

        controller.on('riggedHand.meshAdded', function(handMesh, leapHand){
              handMesh.material.opacity = 1;
              handMesh.material.color.set('#7b4b2a');
              handMesh.material.ambient = handMesh.material.color;
        });*/
  </script>
nakulcr7 commented 9 years ago

This is the server-side code.

var express = require('express'),
    app = express(),
    http = require('http').Server(app),
    io = require('socket.io')(http),
    Leap = require('leapjs');

var action = 'Stop';

app.use(express.static('public'));

app.get('/', function(req, res) {
    res.sendFile('./public/index.html');    
});

http.listen(4000, function() {
                  console.log('listening on port 4000');
});

var controller = new Leap.Controller();
controller.connect();

io.on('connection', function(socket) {
      controller.on('frame', function(frame) {
           socket.emit('action', action);
            if(frame.hands.length < 1) {
                  action = 'Stop';
            }
            else {
                  getCommand(frame.hands[0]);
            }
      });
});

function getCommand(hand){
      var x_axis = hand.palmPosition[0];
      var y_axis = hand.palmPosition[1];
      var z_axis = hand.palmPosition[2];

      var power = 0;
      if( y_axis <= 100 ) {
            power = 0;
      }
      else {
            power = 1;
      }

      if( Math.abs(x_axis) > Math.abs(z_axis) ) {
            if( x_axis > 100 ) 
                  action = 'Right';

            else if( x_axis < -100 ) 
                  action = 'Left';

            else 
                  action = 'Stop'; 
      }
      else {
            if( z_axis > 50 ) 
                  action = 'Reverse';

            else if( z_axis < -50 ) 
                  action = 'Forward';

            else 
                  action = 'Stop';
      }
      console.log(action);
}
raimo commented 9 years ago

If that's the case, I wonder if there's anything printed in the console and/or network panel in dev tools? If you comment one part out, does the other work fine? If so, we need to dig deeper with the help of a debugger. 

nakulcr7 commented 9 years ago

Below are the screenshots.

  1. My original code (please see my first post): 1
  2. After commenting out the visualization part (please see my second post ): 2
pehrlich commented 9 years ago

So if my understanding is correct: you are maintaining two LeapJS connections simultaneously, one on in Node and one on the client. If so, be sure to connect with background mode on, so that both work even without focus. (new Leap.Controller({background: true})).

Besides that, I might suggest simplifying your set up (what happens if you only use one or the other independently?) Or piping the commands the other direction (from the browser through sockerIO to your robot).

nakulcr7 commented 9 years ago

Hey pehrlich! {background:true} worked. You're right, piping the commands in the other direction simplifies my setup. Thank you :)