We are going to implement two of the gestures from the previous table in this section: holding and swiping. They are actually not "multi" gestures because they can be finished with only one finger on the surface, or one hand cursor from Kinect, but both are very useful for developing Kinect-based applications. The holding gesture can be used to trigger a button on the screen, and the swiping gesture can be used to select the menu item, by scrolling the item list and finding the required one.
Let's start now.
GLfloat cursors[6]; // Left hand: 0, 1, 2; Right: 3, 4, 5 GLfloat lastCursors[6]; GLfloat cursorColors[8]; // Left hand: 0-3; Right: 4-7 unsigned int holdGestureCount[2] = {0}; unsigned int swipeGestureCount[2] = {0};
updateSkeletonData()
function, we declare and call a new function named guessGesture()
. It will check possible gestures of both cursors. The locations will then be recorded to the variable lastCursors
for the next frame use.guessGesture( 0, (yMin<cursors[1] && cursors[2]<zMax) ); guessGesture( 1, (yMin<cursors[4] && cursors[5]<zMax) ); for ( int i=0; i<6; ++i ) lastCursors[i] = cursors[i];
guessGesture()
function has two parameters: the cursor index (0 is the left hand, and 1 is the right hand), and a Boolean value to tell if the cursor is in the available range.void guessGesture( unsigned int index, bool inRange ) { ... }
lastCursors
records the cursor locations of the previous frame, we can obtain the velocities of both cursors between two frames and use them for instantaneous judgment.if ( !inRange ) { // If the hand is not in range, reset all counters and // the cursor color (turn to translucence) holdGestureCount[index] = 0; swipeGestureCount[index] = 0; cursorColors[3 + index*4] = 0.2f; } else { // Compute the distance of this and last cursor, which // is actually the instantaneous velocity of the cursor float distance = sqrt( powf(cursors[index*3]-lastCursors[index*3], 2.0f) + powf(cursors[1+index*3]-lastCursors[1+index*3], 2.0f)); if ( distance<0.02 ) { // If the cursor is nearly unmoved, increase holding holdGestureCount[index]++; swipeGestureCount[index] = 0; } else if ( distance>0.05 ) { // If the cursor changes fast, increase swiping holdGestureCount[index] = 0; swipeGestureCount[index]++; } else { // Otherwise, reset the counters holdGestureCount[index] = 0; swipeGestureCount[index] = 0; } cursorColors[3 + index*4] = 1.0f; }
render()
function. If the counters for holding gesture are increased to a large enough value (in this case, to 30), it means we are holding the cursor for a long time, so "hold" is triggered. And if the swiping counters are set, it means we have already swiped the hands and the "swipe" gesture is triggered.std::string text = "Gestures (L/R): "; for ( int i=0; i<2; ++i ) { if ( holdGestureCount[i]>30 ) text += "Hold;"; else if ( swipeGestureCount[i]>1 ) text += "Swipe;"; else text += "None;"; } glRasterPos2f( 0.01f, 0.01f ); glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); glutBitmapString( GLUT_BITMAP_TIMES_ROMAN_24, (const unsigned char*)text.c_str() );
Determining gestures always requires some mathematics, as well as some user-engineering knowledge. An instantaneous state may not be used to decide if a gesture happens or not. For example, the holding gesture requires the user to keep his/her cursor at a specified place for a while, and double tapping means we must detect the "tap" gesture at least twice in a short period of time. Thus, we have to keep a historical list of the cursors, which is similar to the implementation of a linetrail effect in the previous chapter.
Here are some hints for implementing different gestures, including the two gestures we have already done:
Maybe you will have some other ideas and solutions, so don't hesitate to replace any in the previous list with your own, and see if it can make your customers feel better.
18.225.11.98