12.2. Creating a Gesture Recognizer

This section describes creating a new gesture recognizer. It describes the steps to release drag gesture event.

Creating the new gesture event requires these steps:

  1. Create new gesture recognizer class and derive it from SoGestureRecognizer.

  2. Define a constructor and destructor.

  3. Implement SoEvent* recognize(SoEvent* touchEvent)

In our case, SoDragGestureEvent contains the dragged distance. The recognizer has to analyze every touch event and:

  1. Begin with a BEGIN gesture event when two fingers are touching the screen.

  2. Release DELTA event when the two fingers move.

  3. Release END gesture event when one finger, at least, is removed from the monitor.

Computed distance is the length of the line defined by the center between two fingers.

Example 12.2, “ DragGestureRecognizer.h shows the code for the drag gesture recognizer include file.

Example 12.2.  DragGestureRecognizer.h

#include <Inventor/gestures/recognizers/SoGestureRecognizer.h>
#include <Inventor/gestures/events/SoDragGestureEvent.h>
#include <Inventor/touch/SoTouchManager.h>

class INVENTOR_API SoDragGestureRecognizer : public SoGestureRecognizer {

public :
  SoDragGestureRecognizer();
  virtual ~SoDragGestureRecognizer();
  SoEvent* recognize(SoEvent* touchEvent); 
  
private:
  int m_finger1ID;
  int m_finger2ID;
  bool m_recoInProgress;

  SoDragGestureEvent m_returnedEvent;
};


Example 12.3, “ DracGestureRecognizer.cpp shows the complete source code for the drag gesture recognizer.

Example 12.3.  DracGestureRecognizer.cpp

#include <Inventor/gestures/recognizers/SoDragGestureRecognizer.h>
#include <Inventor/touch/events/SoTouchEvent.h>

SoDragGestureRecognizer::SoDragGestureRecognizer()
{
  m_finger1ID = -1;
  m_finger2ID = -1;
  m_recoInProgress = false;
}

SoDragGestureRecognizer::~SoDragGestureRecognizer()
{
}

SoEvent* SoDragGestureRecognizer::recognize(SoEvent* touchEvent) 
{
  SoTouchEvent* dragTouchEvent = dynamic_cast <SoTouchEvent*>(touchEvent);
  SbVec2f otherFingerPosition;
  SbVec2f otherFingerFirstPosition;
  SbVec2f position = dragTouchEvent->getPositionFloat();

  SoTouchManager* touchManager = dragTouchEvent->getTouchManager();

  if (dragTouchEvent==0)
    return NULL;

  // If the recognizer is turned on
  if (m_recoInProgress)
  {
    // If you put up one of your finger, the recognition is endeed and the recognizer return
    // an event with the END attribute.
    if(dragTouchEvent->getState() == SoTouchEvent::UP)
    {
      if(dragTouchEvent->getFingerId() == m_finger1ID)
      {
        m_finger1ID = -1;
      }
      else if (dragTouchEvent->getFingerId() == m_finger2ID)
      {
        m_finger2ID = -1;
      }
      m_returnedEvent.setGestureState(SoGestureEvent::END);
      m_recoInProgress = false;
      return &m_returnedEvent;
    }
    // If it's not a UP, two fingers are moving on the screen. The position of the other finger is recovered.
    // and the distance between fingers is updated to calculate the new distance.
    else 
    {
      if(dragTouchEvent->getFingerId() == m_finger1ID)
      {
        otherFingerPosition = touchManager->getEventById(m_finger2ID)->getPositionFloat();
        otherFingerFirstPosition = touchManager->getEventById(m_finger2ID)->getFirstPosition();
      }
      else if (dragTouchEvent->getFingerId() == m_finger2ID) 
      {
        otherFingerPosition = touchManager->getEventById(m_finger1ID)->getPositionFloat();
        otherFingerFirstPosition = touchManager->getEventById(m_finger1ID)->getFirstPosition();
      }
      SbVec2f pos = (position + otherFingerPosition )/2;
      m_returnedEvent.setPosition(pos);
      m_returnedEvent.setDraggedDistance(pos - (dragTouchEvent->getFirstPosition() +  otherFingerFirstPosition)/2 );
      m_returnedEvent.setGestureState(SoGestureEvent::DELTA);
      return &m_returnedEvent;
    }
  }
  // If the recognizer is turned off.
  else
  {
    if(dragTouchEvent->getState() == SoTouchEvent::UP)
    {
      if (dragTouchEvent->getFingerId() == m_finger1ID)
      {
        m_finger1ID = -1;				
      }
    }
    // if one finger is put down the sreen
    else if(dragTouchEvent->getState() == SoTouchEvent::DOWN)
    {
      if(m_finger1ID == -1) 
      {
        m_finger1ID = dragTouchEvent->getFingerId();
      }
      // and an other finger is touch the screen too.
      else if (dragTouchEvent->getFingerId() != m_finger1ID) {
        m_finger2ID = dragTouchEvent->getFingerId();
			
        otherFingerPosition = touchManager->getEventById(m_finger1ID)->getPositionFloat();
        otherFingerFirstPosition = touchManager->getEventById(m_finger1ID)->getFirstPosition();
        SbVec2f pos = (position + otherFingerPosition )/2;
        m_returnedEvent.setDraggedDistance(pos - (dragTouchEvent->getFirstPosition() +  otherFingerFirstPosition)/2 );

        m_returnedEvent.setGestureState(SoGestureEvent::BEGIN);
        m_recoInProgress = true;
        return &m_returnedEvent;
      }
    }
  }
  return NULL;
}