LeapMotion Scol plugin
sLeapMotion.cpp
1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OpenSpace3D
4 For the latest info, see http://www.openspace3d.com
5 
6 Copyright (c) 2013 I-maginer
7 
8 This program is free software; you can redistribute it and/or modify it under
9 the terms of the GNU Lesser General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
11 version.
12 
13 This program is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
20 http://www.gnu.org/copyleft/lesser.txt
21 
22 -----------------------------------------------------------------------------
23 */
24 
25 #include "sLeapMotion.h"
26 
27 extern int LEAPMOTION_CONNECTED_CB;
28 extern int LEAPMOTION_DISCONNECTED_CB;
29 
30 extern int LEAPMOTION_CIRCLE_CB;
31 extern int LEAPMOTION_SWIPE_CB;
32 extern int LEAPMOTION_KEYTAP_CB;
33 extern int LEAPMOTION_SCREENTAP_CB;
34 
36 {
37  mConnected = false;
38  mTerminate = false;
39  mTopTracking = false;
40  mInit = false;
41  std::vector<int> fids;
42  mLastPointableIds.resize(16, fids);
43  mLastHandIds.resize(2, -1);
44  mLastToolsIds.resize(2, -1);
45  //mThread = boost::thread(boost::bind(&sLeapMotion::UpdateThread, this));
46  mLastGestureTick = 0;
47 }
48 
50 {
51  mTerminate = true;
52  //mThread.join();
53 
54  if (mInit)
55  mController.removeListener(*this);
56 
57  mGestureList.clear();
58  mConnected = false;
59 }
60 
61 void sLeapMotion::Init()
62 {
63  mController.addListener(*this);
64  mInit = true;
65 }
66 
67 void sLeapMotion::onInit(const Controller& controller)
68 {
69  //std::cout << "Initialized" << std::endl;
70 }
71 
72 void sLeapMotion::onConnect(const Controller& controller)
73 {
74  controller.enableGesture(Gesture::TYPE_CIRCLE);
75  controller.enableGesture(Gesture::TYPE_KEY_TAP);
76  controller.enableGesture(Gesture::TYPE_SCREEN_TAP);
77  controller.enableGesture(Gesture::TYPE_SWIPE);
78  Leap::Controller::PolicyFlag flags = (mTopTracking) ? Controller::POLICY_OPTIMIZE_HMD : Controller::POLICY_DEFAULT;
79  controller.setPolicy(flags);
80 
81  if (!mConnected)
82  {
83  OBJpostEvent(LEAPMOTION_CONNECTED_CB, SCOL_PTR this, 0);
84  mConnected = true;
85  }
86 }
87 
88 void sLeapMotion::onDisconnect(const Controller& controller)
89 {
90  mConnected = false;
91 
92  //Note: not dispatched when running in a debugger.
93  OBJpostEvent(LEAPMOTION_DISCONNECTED_CB, SCOL_PTR this, 0);
94 }
95 
96 void sLeapMotion::onExit(const Controller& controller)
97 {
98  //std::cout << "Exited" << std::endl;
99 }
100 
101 void sLeapMotion::DetectHands(const HandList hands)
102 {
103  //TODO check found / lost hands
104  for (int i = 0; i < hands.count(); ++i)
105  {
106  // Get the first hand
107  const Hand hand = hands[i];
108 
109  DetectFingers(hand);
110 
111  // Get the hand's sphere radius and palm position
112  /*std::cout << "Hand sphere radius: " << hand.sphereRadius()
113  << " mm, palm position: " << hand.palmPosition() << std::endl;
114 
115  // Get the hand's normal vector and direction
116  const Vector normal = hand.palmNormal();
117  const Vector direction = hand.direction();
118 
119  // Calculate the hand's pitch, roll, and yaw angles
120  std::cout << "Hand pitch: " << direction.pitch() * RAD_TO_DEG << " degrees, "
121  << "roll: " << normal.roll() * RAD_TO_DEG << " degrees, "
122  << "yaw: " << direction.yaw() * RAD_TO_DEG << " degrees" << std::endl;
123  */
124  }
125 }
126 
127 void sLeapMotion::DetectFingers(const Hand hand)
128 {
129  // Check if the hand has any fingers
130  const FingerList fingers = hand.fingers();
131  if (!fingers.isEmpty())
132  {
133  // Calculate the hand's average finger tip position
134  Vector avgPos;
135  for (int i = 0; i < fingers.count(); ++i)
136  {
137  avgPos += fingers[i].tipPosition();
138  }
139  avgPos /= (float)fingers.count();
140  }
141 }
142 
143 void sLeapMotion::DetectTools(const ToolList tools)
144 {
145 }
146 
147 void sLeapMotion::DetectGestures(const GestureList gestures)
148 {
149  int ltick = GetTickCount() - mLastGestureTick;
150  for (int g = 0; g < gestures.count(); ++g)
151  {
152  Gesture gesture = gestures[g];
153  HandList hands = gesture.hands();
154  SGesture sgesture;
155 
156  if (!gesture.isValid())
157  continue;
158 
159  //get hand and pointable index
160  if (gesture.state() == Gesture::STATE_START || (gesture.type() == Gesture::TYPE_KEY_TAP) || (gesture.type() == Gesture::TYPE_SCREEN_TAP))
161  {
162  int handIndex = -1;
163  int pointableId = -1;
164 
165  if (hands.isEmpty() || !hands[0].isValid())
166  continue;
167 
168  if (std::find(mLastHandIds.begin(), mLastHandIds.end(), hands[0].id()) == mLastHandIds.end())
169  continue;
170 
171  for (unsigned int i = 0; i < mLastHandIds.size(); ++i)
172  {
173  if (mLastHandIds[i] == hands[0].id())
174  handIndex = i;
175  }
176 
177  if (hands[0].id() >= (int)mLastPointableIds.size())
178  continue;
179 
180  PointableList pointables = gesture.pointables();
181 
182  for (unsigned int j = 0; j < mLastPointableIds[hands[0].id()].size(); j++)
183  {
184  if (mLastPointableIds[hands[0].id()][j] == pointables[0].id())
185  pointableId = j;
186  }
187  sgesture = SGesture(gesture.id(), handIndex, pointableId);
188 
189  if ((gesture.type() == Gesture::TYPE_CIRCLE) || (gesture.type() == Gesture::TYPE_SWIPE))
190  mGestureList.push_back(sgesture);
191  }
192 
193  int sgesturepos = 0;
194  if (sgesture.id == -1)
195  {
196  for (unsigned int i = 0; (i < mGestureList.size()) && (sgesture.id == -1); i++)
197  {
198  if (mGestureList[i].id == gesture.id())
199  {
200  sgesture = mGestureList[i];
201  sgesturepos = i;
202  }
203  }
204  }
205 
206  // no previous or start gesture found
207  if (sgesture.id == -1)
208  continue;
209 
210  // get gesture from list
211  if (gesture.state() == Gesture::STATE_STOP && ((gesture.type() == Gesture::TYPE_CIRCLE) || (gesture.type() == Gesture::TYPE_SWIPE)))
212  {
213  mGestureList.erase(mGestureList.begin() + sgesturepos);
214  }
215 
216  //avoid to fast calls
217  if ((ltick < 16) && gesture.state() == Gesture::STATE_UPDATE)
218  continue;
219 
220  switch (gesture.type())
221  {
222  case Gesture::TYPE_CIRCLE:
223  {
224  SCbData* data = new SCbData();
225  CircleGesture circle = gesture;
226 
227  if (circle.pointable().direction().angleTo(circle.normal()) <= PI/4)
228  {
229  data->dir = 1.0f;
230  }
231  else
232  {
233  data->dir = -1.0f;
234  }
235 
236  // Calculate angle swept since last frame
237  float sweptAngle = 0;
238  if (circle.state() != Gesture::STATE_START)
239  {
240  CircleGesture previousUpdate = CircleGesture(mController.frame(1).gesture(circle.id()));
241  sweptAngle = (circle.progress() - previousUpdate.progress()) * 2 * PI;
242  }
243 
244  data->progress = circle.progress();
245 
246  data->handId = sgesture.handId;
247  data->id = sgesture.pointableId;
248 
249  data->fparam = circle.radius();
250 
251  data->state = (int)gesture.state();
252 
253  if (gesture.state() != Gesture::STATE_INVALID)
254  OBJpostEvent(LEAPMOTION_CIRCLE_CB, SCOL_PTR this, (int)data);
255  else
256  delete data;
257  break;
258  }
259  case Gesture::TYPE_SWIPE:
260  {
261  SCbData* data = new SCbData();
262  SwipeGesture swipe = gesture;
263 
264  data->progress = ((float)swipe.duration() / 1000);
265 
266  data->handId = sgesture.handId;
267  data->id = sgesture.pointableId;
268 
269  data->vDir = swipe.direction();
270  data->fparam = swipe.speed();
271  data->svec = swipe.startPosition();
272 
273  data->state = (int)gesture.state();
274 
275  if (gesture.state() != Gesture::STATE_INVALID)
276  OBJpostEvent(LEAPMOTION_SWIPE_CB, SCOL_PTR this, (int)data);
277  else
278  delete data;
279  break;
280  }
281  case Gesture::TYPE_KEY_TAP:
282  {
283  SCbData* data = new SCbData();
284  KeyTapGesture tap = gesture;
285 
286  data->progress = tap.progress();
287  data->vDir = tap.direction();
288  data->svec = tap.position();
289 
290  data->handId = sgesture.handId;
291  data->id = sgesture.pointableId;
292 
293  data->state = (int)gesture.state();
294 
295  if (gesture.state() == Gesture::STATE_STOP)
296  OBJpostEvent(LEAPMOTION_KEYTAP_CB, SCOL_PTR this, (int)data);
297  else
298  delete data;
299  break;
300  }
301  case Gesture::TYPE_SCREEN_TAP:
302  {
303  ScreenTapGesture screentap = gesture;
304 
305  SCbData* data = new SCbData();
306  data->progress = ((float)screentap.duration() / 1000);
307  data->vDir = screentap.direction();
308  data->svec = screentap.position();
309 
310  data->handId = sgesture.handId;
311  data->id = sgesture.pointableId;
312 
313  data->state = (int)gesture.state();
314 
315  if (gesture.state() == Gesture::STATE_STOP)
316  OBJpostEvent(LEAPMOTION_SCREENTAP_CB, SCOL_PTR this, (int)data);
317  else
318  delete data;
319  break;
320  }
321  default:
322  break;
323  }
324  }
325  mLastGestureTick = GetTickCount();
326 }
327 
328 void sLeapMotion::onFrame(const Controller& controller)
329 {
330  Frame lframe(controller.frame());
331  DetectGestures(lframe.gestures());
332 
333  {
334  //all leap motion listener callbacks are in a foreign thread
335  boost::mutex::scoped_lock l(mMutex);
336  // Get the most recent frame
337  mLastFrame = lframe;
338  }
339  //DetectHands(mLastFrame.hands());
340  //DetectTools(mLastFrame.tools());
341 }
342 
343 void sLeapMotion::onFocusGained(const Controller& controller)
344 {
345  //std::cout << "Focus Gained" << std::endl;
346 }
347 
348 void sLeapMotion::onFocusLost(const Controller& controller)
349 {
350  //std::cout << "Focus Lost" << std::endl;
351 }
352 
353 // Hands
354 bool sLeapMotion::GetHand(int id, Hand &hand)
355 {
356  HandList hands;
357  {
358  boost::mutex::scoped_lock l(mMutex);
359  if (!mConnected || !mLastFrame.isValid() || mLastFrame.hands().count() < id)
360  return false;
361 
362  hands = mLastFrame.hands();
363  }
364 
365  if (id >= (int)mLastHandIds.size())
366  mLastHandIds.resize(id+1, -1);
367 
368  if (mLastHandIds[id] != -1)
369  {
370  for (int i = 0; i < hands.count(); ++i)
371  {
372  Hand fhand = hands[i];
373 
374  if (mLastHandIds[id] == fhand.id())
375  hand = fhand;
376  }
377  }
378 
379  //previous id not found, search for a new hand id
380  if (!hand.isValid())
381  {
382  bool foundnew = false;
383  for (int i = 0; i < hands.count() && !foundnew; ++i)
384  {
385  hand = hands[i];
386  if (hand.isValid() && (((id == 0) && hand.isLeft()) || ((id == 1) && (hand.isRight()))))
387  {
388  std::vector<int>::iterator vit = std::find(mLastHandIds.begin(), mLastHandIds.end(), hand.id());
389  if (vit != mLastHandIds.end())
390  mLastHandIds[vit-mLastHandIds.begin()] = -1;
391 
392  foundnew = true;
393  }
394  else if (((id > 1)/* || hands.count() < 2*/) && hand.isValid() && std::find(mLastHandIds.begin(), mLastHandIds.end(), hand.id()) == mLastHandIds.end())
395  foundnew = true;
396  }
397 
398  if (!foundnew)
399  hand = Hand();
400 
401  mLastHandIds[id] = hand.id();
402  }
403 
404  //resize pointables list
405  if (hand.id() >= (int)mLastPointableIds.size())
406  {
407  std::vector<int> fids;
408  mLastPointableIds.resize(hand.id()+1, fids);
409  }
410 
411  if (hand.isValid())
412  return true;
413  else
414  return false;
415 }
416 
417 bool sLeapMotion::GetLeftHand(Hand &hand)
418 {
419  boost::mutex::scoped_lock l(mMutex);
420  if (!mConnected || !mLastFrame.isValid())
421  return false;
422 
423  hand = mLastFrame.hands().leftmost();
424  if (hand.isValid())
425  return true;
426  else
427  return false;
428 }
429 
430 bool sLeapMotion::GetRightHand(Hand &hand)
431 {
432  boost::mutex::scoped_lock l(mMutex);
433  if (!mConnected || !mLastFrame.isValid())
434  return false;
435 
436  hand = mLastFrame.hands().rightmost();
437  if (hand.isValid())
438  return true;
439  else
440  return false;
441 }
442 
443 bool sLeapMotion::GetHandPosition(const Hand hand, bool optim, Vector &vec)
444 {
445  if (optim)
446  vec = hand.stabilizedPalmPosition();
447  else
448  vec = hand.palmPosition();
449 
450  if (vec.isValid())
451  {
452  vec -= hand.direction();
453  return true;
454  }
455  else
456  return false;
457 }
458 
459 bool sLeapMotion::GetHandOrientation(const Hand hand, Vector &vec)
460 {
461  const Vector normal = hand.palmNormal();
462  const Vector direction = hand.direction();
463 
464  vec.x = direction.pitch();
465  vec.y = direction.yaw();
466  vec.z = normal.roll();
467 
468  if (vec.isValid())
469  return true;
470  else
471  return false;
472 }
473 
474 bool sLeapMotion::GetHandPalmNormal(const Hand hand, Vector &vec)
475 {
476  vec = hand.palmNormal();
477 
478  if (vec.isValid())
479  return true;
480  else
481  return false;
482 }
483 
484 bool sLeapMotion::GetHandDirection(const Hand hand, Vector &vec)
485 {
486  vec = hand.direction();
487 
488  if (vec.isValid())
489  return true;
490  else
491  return false;
492 }
493 
494 bool sLeapMotion::GetHandVelocity(const Hand hand, Vector &vec)
495 {
496  vec = hand.palmVelocity();
497 
498  if (vec.isValid())
499  return true;
500  else
501  return false;
502 }
503 
504 std::vector<SCbData> sLeapMotion::GetHandFingers(const Hand hand, bool optim)
505 {
506  boost::mutex::scoped_lock l(mMutex);
507  std::vector<SCbData> lfingers;
508  lfingers.resize(5);
509 
510  //resize pointables list
511  if (hand.id() >= (int)mLastPointableIds.size())
512  {
513  std::vector<int> fids;
514  mLastPointableIds.resize(hand.id()+1, fids);
515  }
516 
517  if (mLastPointableIds[hand.id()].empty())
518  {
519  std::vector<int> fids;
520  fids.resize(5, -1);
521  mLastPointableIds[hand.id()] = fids;
522  }
523 
524  const FingerList fingers = hand.fingers();
525 
526  for (int i = 0; i < 5; ++i)
527  {
528  Finger finger = fingers[i];
529  SCbData data;
530 
531  mLastPointableIds[hand.id()][i] = finger.id();
532 
533  Vector screenPos(0.0f, 0.0f, 0.0f);
534 
535  if (finger.isValid())
536  {
537  ScreenList screens = mController.locatedScreens();
538  Screen screen = screens.closestScreenHit(finger);
539  if (screen.isValid() && finger.isExtended())
540  screenPos = screen.intersect(finger, true);
541 
542  data.svec = optim ? finger.stabilizedTipPosition() : finger.tipPosition();
543  data.evec = data.svec - finger.direction() * -0.25f;
544  data.screenPos = screenPos;
545  Vector dir = finger.direction();
546  data.pyr.x = dir.pitch();
547  data.pyr.y = dir.yaw();
548  data.pyr.z = dir.roll();
549 
550  for (unsigned int joint = 0; joint < 4; joint++)
551  data.subVecs.push_back(finger.jointPosition((Leap::Finger::Joint)joint));
552 
553  data.id = finger.id();
554  }
555 
556  lfingers[i] = data;
557  }
558 
559  return lfingers;
560 }
561 
562 // Tools
563 bool sLeapMotion::GetTool(int id, Tool &tool)
564 {
565  boost::mutex::scoped_lock l(mMutex);
566  if (!mConnected || !mLastFrame.isValid() || mLastFrame.tools().count() < id)
567  return false;
568 
569  ToolList tools = mLastFrame.tools();
570 
571  if (id >= (int)mLastToolsIds.size())
572  mLastToolsIds.resize(id+1, -1);
573 
574  if (mLastToolsIds[id] != -1)
575  {
576  for (int i = 0; i < tools.count(); ++i)
577  {
578  Tool ftool = tools[i];
579 
580  if (mLastToolsIds[id] == ftool.id())
581  tool = ftool;
582  }
583  }
584 
585  //previous id not found, search for a new hand id
586  if (!tool.isValid())
587  {
588  bool foundnew = false;
589  for (int i = 0; i < tools.count() && !foundnew; ++i)
590  {
591  tool = tools[i];
592  if (tool.isValid() && std::find(mLastToolsIds.begin(), mLastToolsIds.end(), tool.id()) == mLastToolsIds.end())
593  foundnew = true;
594  }
595 
596  if (!foundnew)
597  tool = Tool();
598 
599  mLastToolsIds[id] = tool.id();
600  }
601 
602  if (tool.isValid())
603  return true;
604  else
605  return false;
606 }
607 
608 bool sLeapMotion::GetToolPosition(const Tool tool, bool optim, Vector &vec)
609 {
610  if (optim)
611  vec = tool.stabilizedTipPosition();
612  else
613  vec = tool.tipPosition();
614 
615  if (vec.isValid())
616  return true;
617  else
618  return false;
619 }
620 
621 bool sLeapMotion::GetToolOrientation(const Tool tool, Vector &vec)
622 {
623  const Vector direction = tool.direction();
624 
625  vec.x = direction.pitch();
626  vec.y = direction.yaw();
627  vec.z = direction.roll();
628 
629  return true;
630 }
631 
632 bool sLeapMotion::GetToolVelocity(const Tool tool, Vector &vec)
633 {
634  vec = tool.tipVelocity();
635 
636  if (vec.isValid())
637  return true;
638  else
639  return false;
640 }
641 
642 void sLeapMotion::SetTopTrackingEnable(bool state)
643 {
644  mTopTracking = state;
645 
646  if (mTopTracking)
647  mController.setPolicy(Controller::POLICY_OPTIMIZE_HMD);
648  else
649  mController.clearPolicy(Controller::POLICY_OPTIMIZE_HMD);
650 }
651 
652 bool sLeapMotion::GetTopTrackingEnable()
653 {
654  return mTopTracking;
655 }