Rave glove: stabilization of Leap hands

This commit is contained in:
Eric Johnston 2013-07-19 19:47:07 -07:00
parent 262851346c
commit b8cf4a9775
3 changed files with 108 additions and 89 deletions

View file

@ -54,15 +54,6 @@ void LeapManager::nextFrame(Avatar& avatar) {
// If we actually get valid Leap data, this will be set to true; // If we actually get valid Leap data, this will be set to true;
bool gotRealData = false; bool gotRealData = false;
// First, deactivate everything.
for (size_t i = 0; i < hand.getNumPalms(); ++i) {
PalmData& palm = hand.getPalms()[i];
palm.setActive(false);
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
finger.setActive(false);
}
}
if (controllersExist()) { if (controllersExist()) {
_listener->onFrame(*_controller); _listener->onFrame(*_controller);
@ -70,57 +61,70 @@ void LeapManager::nextFrame(Avatar& avatar) {
#ifndef LEAP_STUBS #ifndef LEAP_STUBS
if (controllersExist()) { if (controllersExist()) {
// Performance note: gotRealData = true;
// This is a first pass.
// Once all of this is stable, perfoamance may be improved ysing std::map or another
// associative container to match serialized Leap ID's with available fingers.
// That will make this code shorter and more efficient.
// First, see which palms and fingers are still valid. // First, see which palms and fingers are still valid.
Leap::Frame& frame = _listener->lastFrame; Leap::Frame& frame = _listener->lastFrame;
// Note that this is O(n^2) at worst, but n is very small. // Note that this is O(n^2) at worst, but n is very small.
// After this many frames of no data, assume the digit is lost.
const int assumeLostAfterFrameCount = 10;
// Increment our frame data counters
for (size_t i = 0; i < hand.getNumPalms(); ++i) {
PalmData& palm = hand.getPalms()[i];
palm.incrementFramesWithoutData();
if (palm.getFramesWithoutData() > assumeLostAfterFrameCount) {
palm.setActive(false);
}
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
finger.incrementFramesWithoutData();
if (finger.getFramesWithoutData() > assumeLostAfterFrameCount) {
finger.setActive(false);
}
}
}
size_t numLeapHands = frame.hands().count(); size_t numLeapHands = frame.hands().count();
std::vector<PalmData*> palmAssignment(numLeapHands); std::vector<PalmData*> palmAssignment(numLeapHands);
// Look for matches // Look for matches
for (size_t index = 0; index < numLeapHands; ++index) { for (size_t index = 0; index < numLeapHands; ++index) {
PalmData* takeoverCandidate = NULL;
palmAssignment[index] = NULL; palmAssignment[index] = NULL;
Leap::Hand leapHand = frame.hands()[index]; Leap::Hand leapHand = frame.hands()[index];
int id = leapHand.id(); int id = leapHand.id();
if (leapHand.isValid()) { if (leapHand.isValid()) {
for (size_t i = 0; i < hand.getNumPalms(); ++i) { for (size_t i = 0; i < hand.getNumPalms() && palmAssignment[index] == NULL; ++i) {
PalmData& palm = hand.getPalms()[i]; PalmData& palm = hand.getPalms()[i];
if (palm.getLeapID() == id) { if (palm.getLeapID() == id) {
// Found hand with the same ID. We're set! // Found hand with the same ID. We're set!
palmAssignment[index] = &palm; palmAssignment[index] = &palm;
palm.setActive(true); palm.resetFramesWithoutData();
}
}
}
}
// Fill empty slots
for (size_t index = 0; index < numLeapHands; ++index) {
if (palmAssignment[index] == NULL) {
Leap::Hand leapHand = frame.hands()[index];
if (leapHand.isValid()) {
for (size_t i = 0; i < hand.getNumPalms() && palmAssignment[index] == NULL; ++i) {
PalmData& palm = hand.getPalms()[i];
if (!palm.isActive()) {
// Found a free hand to use.
palmAssignment[index] = &palm;
palm.setActive(true);
palm.setLeapID(leapHand.id());
}
} }
else if (palm.getFramesWithoutData() > assumeLostAfterFrameCount) {
takeoverCandidate = &palm;
}
}
if (palmAssignment[index] == NULL) {
palmAssignment[index] = takeoverCandidate;
}
if (palmAssignment[index] == NULL) {
palmAssignment[index] = &hand.addNewPalm();
} }
} }
} }
// Apply the assignments // Apply the assignments
for (size_t index = 0; index < numLeapHands; ++index) { for (size_t index = 0; index < numLeapHands; ++index) {
if (palmAssignment[index]) { if (palmAssignment[index]) {
Leap::Hand leapHand = frame.hands()[index]; Leap::Hand leapHand = frame.hands()[index];
PalmData& palm = *(palmAssignment[index]); PalmData& palm = *(palmAssignment[index]);
palm.resetFramesWithoutData();
palm.setLeapID(leapHand.id());
palm.setActive(true);
const Leap::Vector pos = leapHand.palmPosition(); const Leap::Vector pos = leapHand.palmPosition();
const Leap::Vector normal = leapHand.palmNormal(); const Leap::Vector normal = leapHand.palmNormal();
palm.setRawPosition(glm::vec3(pos.x, pos.y, pos.z)); palm.setRawPosition(glm::vec3(pos.x, pos.y, pos.z));
@ -128,59 +132,58 @@ void LeapManager::nextFrame(Avatar& avatar) {
} }
} }
size_t numLeapFingers = frame.fingers().count(); // Look for fingers per palm
std::vector<FingerData*> fingerAssignment(numLeapFingers); for (size_t i = 0; i < hand.getNumPalms(); ++i) {
// Look for matches PalmData& palm = hand.getPalms()[i];
for (size_t index = 0; index < numLeapFingers; ++index) { if (palm.isActive()) {
fingerAssignment[index] = NULL; Leap::Hand leapHand = frame.hand(palm.getLeapID());
Leap::Finger leapFinger = frame.fingers()[index]; if (leapHand.isValid()) {
int id = leapFinger.id(); int numLeapFingers = leapHand.fingers().count();
if (leapFinger.isValid()) { std::vector<FingerData*> fingerAssignment(numLeapFingers);
for (size_t i = 0; i < hand.getNumPalms(); ++i) {
PalmData& palm = hand.getPalms()[i];
for (size_t f = 0; f < palm.getNumFingers(); ++f) { // Look for matches
FingerData& finger = palm.getFingers()[f]; for (size_t index = 0; index < numLeapFingers; ++index) {
if (finger.getLeapID() == id) { FingerData* takeoverCandidate = NULL;
// Found finger with the same ID. We're set! fingerAssignment[index] = NULL;
fingerAssignment[index] = &finger; Leap::Finger leapFinger = leapHand.fingers()[index];
finger.setActive(true); int id = leapFinger.id();
} if (leapFinger.isValid()) {
} for (size_t f = 0; f < palm.getNumFingers() && fingerAssignment[index] == NULL; ++f) {
} FingerData& finger = palm.getFingers()[f];
} if (finger.getLeapID() == id) {
} // Found hand with the same ID. We're set!
// Fill empty slots fingerAssignment[index] = &finger;
for (size_t index = 0; index < numLeapFingers; ++index) { }
if (fingerAssignment[index] == NULL) { else if (finger.getFramesWithoutData() > assumeLostAfterFrameCount) {
Leap::Finger leapFinger = frame.fingers()[index]; takeoverCandidate = &finger;
if (leapFinger.isValid()) { }
for (size_t i = 0; i < hand.getNumPalms() && fingerAssignment[index] == NULL; ++i) { }
PalmData& palm = hand.getPalms()[i]; // If we didn't find a match, but we found an unused finger, us it.
for (size_t f = 0; f < palm.getNumFingers() && fingerAssignment[index] == NULL; ++f) { if (fingerAssignment[index] == NULL) {
FingerData& finger = palm.getFingers()[f]; fingerAssignment[index] = takeoverCandidate;
if (!finger.isActive()) {
// Found a free finger to use.
fingerAssignment[index] = &finger;
finger.setActive(true);
finger.setLeapID(leapFinger.id());
} }
} }
} }
// Apply the assignments
for (size_t index = 0; index < numLeapFingers; ++index) {
if (fingerAssignment[index]) {
Leap::Finger leapFinger = leapHand.fingers()[index];
FingerData& finger = *(fingerAssignment[index]);
finger.resetFramesWithoutData();
finger.setLeapID(leapFinger.id());
finger.setActive(true);
const Leap::Vector tip = leapFinger.stabilizedTipPosition();
const Leap::Vector root = tip - leapFinger.direction() * leapFinger.length();
finger.setRawTipPosition(glm::vec3(tip.x, tip.y, tip.z));
finger.setRawRootPosition(glm::vec3(root.x, root.y, root.z));
}
}
} }
} }
} }
// Apply the assignments
for (size_t index = 0; index < numLeapFingers; ++index) {
if (fingerAssignment[index]) {
Leap::Finger leapFinger = frame.fingers()[index];
FingerData& finger = *(fingerAssignment[index]);
const Leap::Vector tip = leapFinger.stabilizedTipPosition();
const Leap::Vector root = tip - leapFinger.direction() * leapFinger.length();
finger.setRawTipPosition(glm::vec3(tip.x, tip.y, tip.z));
finger.setRawRootPosition(glm::vec3(root.x, root.y, root.z));
}
}
gotRealData = true;
} }
#endif #endif
if (!gotRealData) { if (!gotRealData) {

View file

@ -13,18 +13,19 @@ HandData::HandData(AvatarData* owningAvatar) :
_baseOrientation(0.0f, 0.0f, 0.0f, 1.0f), _baseOrientation(0.0f, 0.0f, 0.0f, 1.0f),
_owningAvatarData(owningAvatar) _owningAvatarData(owningAvatar)
{ {
for (int i = 0; i < 2; ++i) { }
_palms.push_back(PalmData(this));
} PalmData& HandData::addNewPalm() {
const int standardTrailLength = 30; _palms.push_back(PalmData(this));
setFingerTrailLength(standardTrailLength); return _palms.back();
} }
PalmData::PalmData(HandData* owningHandData) : PalmData::PalmData(HandData* owningHandData) :
_rawPosition(0, 0, 0), _rawPosition(0, 0, 0),
_rawNormal(0, 1, 0), _rawNormal(0, 1, 0),
_isActive(false), _isActive(false),
_leapID(-1), _leapID(LEAPID_INVALID),
_numFramesWithoutData(0),
_owningHandData(owningHandData) _owningHandData(owningHandData)
{ {
for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) { for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) {
@ -36,10 +37,13 @@ FingerData::FingerData(PalmData* owningPalmData, HandData* owningHandData) :
_tipRawPosition(0, 0, 0), _tipRawPosition(0, 0, 0),
_rootRawPosition(0, 0, 0), _rootRawPosition(0, 0, 0),
_isActive(false), _isActive(false),
_leapID(-1), _leapID(LEAPID_INVALID),
_numFramesWithoutData(0),
_owningPalmData(owningPalmData), _owningPalmData(owningPalmData),
_owningHandData(owningHandData) _owningHandData(owningHandData)
{ {
const int standardTrailLength = 30;
setTrailLength(standardTrailLength);
} }
void HandData::encodeRemoteData(std::vector<glm::vec3>& fingerVectors) { void HandData::encodeRemoteData(std::vector<glm::vec3>& fingerVectors) {

View file

@ -20,6 +20,7 @@ class FingerData;
class PalmData; class PalmData;
const int NUM_FINGERS_PER_HAND = 5; const int NUM_FINGERS_PER_HAND = 5;
const int LEAPID_INVALID = -1;
class HandData { class HandData {
public: public:
@ -39,6 +40,7 @@ public:
std::vector<PalmData>& getPalms() { return _palms; } std::vector<PalmData>& getPalms() { return _palms; }
size_t getNumPalms() { return _palms.size(); } size_t getNumPalms() { return _palms.size(); }
PalmData& addNewPalm();
void setFingerTrailLength(unsigned int length); void setFingerTrailLength(unsigned int length);
void updateFingerTrails(); void updateFingerTrails();
@ -79,12 +81,17 @@ public:
int getTrailNumPositions(); int getTrailNumPositions();
const glm::vec3& getTrailPosition(int index); const glm::vec3& getTrailPosition(int index);
void incrementFramesWithoutData() { _numFramesWithoutData++; }
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
int getFramesWithoutData() const { return _numFramesWithoutData; }
private: private:
glm::vec3 _tipRawPosition; glm::vec3 _tipRawPosition;
glm::vec3 _rootRawPosition; glm::vec3 _rootRawPosition;
bool _isActive; // This has current valid data bool _isActive; // This has current valid data
int _leapID; // the Leap's serial id for this tracked object int _leapID; // the Leap's serial id for this tracked object
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
std::vector<glm::vec3> _tipTrailPositions; std::vector<glm::vec3> _tipTrailPositions;
int _tipTrailCurrentStartIndex; int _tipTrailCurrentStartIndex;
int _tipTrailCurrentValidLength; int _tipTrailCurrentValidLength;
@ -110,12 +117,17 @@ public:
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; } void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; }
void incrementFramesWithoutData() { _numFramesWithoutData++; }
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
int getFramesWithoutData() const { return _numFramesWithoutData; }
private: private:
std::vector<FingerData> _fingers; std::vector<FingerData> _fingers;
glm::vec3 _rawPosition; glm::vec3 _rawPosition;
glm::vec3 _rawNormal; glm::vec3 _rawNormal;
bool _isActive; // This has current valid data bool _isActive; // This has current valid data
int _leapID; // the Leap's serial id for this tracked object int _leapID; // the Leap's serial id for this tracked object
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
HandData* _owningHandData; HandData* _owningHandData;
}; };