162 lines
3.2 KiB
C++
162 lines
3.2 KiB
C++
/*
|
|
* Copyright 2009, Alexandre Deckner, alex@zappotek.com
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
/*!
|
|
\class ShakeTrackingFilter
|
|
\brief A simple mouse shake detection filter
|
|
*
|
|
* A simple mouse filter that detects quick mouse shakes.
|
|
*
|
|
* It's detecting rough edges (u-turns) in the mouse movement
|
|
* and counts them within a time window.
|
|
* You can configure the message sent, the u-turn count threshold
|
|
* and the time threshold.
|
|
* It sends the count along with the message.
|
|
* For now, detection is limited within the view bounds, but
|
|
* it might be modified to accept a BRegion mask.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <ShakeTrackingFilter.h>
|
|
|
|
#include <Message.h>
|
|
#include <Messenger.h>
|
|
#include <MessageRunner.h>
|
|
#include <View.h>
|
|
|
|
|
|
const uint32 kMsgCancel = 'Canc';
|
|
|
|
|
|
ShakeTrackingFilter::ShakeTrackingFilter(BView* targetView, uint32 messageWhat,
|
|
uint32 countThreshold, bigtime_t timeThreshold)
|
|
:
|
|
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
|
|
fTargetView(targetView),
|
|
fMessageWhat(messageWhat),
|
|
fCancelRunner(NULL),
|
|
fLowPass(8),
|
|
fLastDelta(0, 0),
|
|
fCounter(0),
|
|
fCountThreshold(countThreshold),
|
|
fTimeThreshold(timeThreshold)
|
|
{
|
|
}
|
|
|
|
|
|
ShakeTrackingFilter::~ShakeTrackingFilter()
|
|
{
|
|
delete fCancelRunner;
|
|
}
|
|
|
|
|
|
filter_result
|
|
ShakeTrackingFilter::Filter(BMessage* message, BHandler** /*_target*/)
|
|
{
|
|
if (fTargetView == NULL)
|
|
return B_DISPATCH_MESSAGE;
|
|
|
|
switch (message->what) {
|
|
case B_MOUSE_MOVED:
|
|
{
|
|
BPoint position;
|
|
message->FindPoint("be:view_where", &position);
|
|
|
|
// TODO: allow using BRegion masks
|
|
if (!fTargetView->Bounds().Contains(position))
|
|
return B_DISPATCH_MESSAGE;
|
|
|
|
fLowPass.Input(position - fLastPosition);
|
|
|
|
BPoint delta = fLowPass.Output();
|
|
|
|
// normalized dot product
|
|
float norm = delta.x * delta.x + delta.y * delta.y;
|
|
if (norm > 0.01) {
|
|
delta.x /= norm;
|
|
delta.y /= norm;
|
|
}
|
|
|
|
norm = fLastDelta.x * fLastDelta.x + fLastDelta.y * fLastDelta.y;
|
|
if (norm > 0.01) {
|
|
fLastDelta.x /= norm;
|
|
fLastDelta.y /= norm;
|
|
}
|
|
|
|
float dot = delta.x * fLastDelta.x + delta.y * fLastDelta.y;
|
|
|
|
if (dot < 0.0) {
|
|
if (fCounter == 0) {
|
|
BMessage * cancelMessage = new BMessage(kMsgCancel);
|
|
fCancelRunner = new BMessageRunner(BMessenger(fTargetView),
|
|
cancelMessage, fTimeThreshold, 1);
|
|
}
|
|
|
|
fCounter++;
|
|
|
|
if (fCounter >= fCountThreshold) {
|
|
BMessage shakeMessage(fMessageWhat);
|
|
shakeMessage.AddUInt32("count", fCounter);
|
|
BMessenger messenger(fTargetView);
|
|
messenger.SendMessage(&shakeMessage);
|
|
}
|
|
}
|
|
|
|
fLastDelta = fLowPass.Output();
|
|
fLastPosition = position;
|
|
|
|
return B_DISPATCH_MESSAGE;
|
|
}
|
|
|
|
case kMsgCancel:
|
|
delete fCancelRunner;
|
|
fCancelRunner = NULL;
|
|
fCounter = 0;
|
|
return B_SKIP_MESSAGE;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return B_DISPATCH_MESSAGE;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
LowPassFilter::LowPassFilter(uint32 size)
|
|
:
|
|
fSize(size)
|
|
{
|
|
fPoints = new BPoint[fSize];
|
|
}
|
|
|
|
|
|
LowPassFilter::~LowPassFilter()
|
|
{
|
|
delete [] fPoints;
|
|
}
|
|
|
|
|
|
void
|
|
LowPassFilter::Input(const BPoint& p)
|
|
{
|
|
// A fifo buffer that maintains a sum of its elements
|
|
fSum -= fPoints[0];
|
|
for (uint32 i = 0; i < fSize - 1; i++)
|
|
fPoints[i] = fPoints[i + 1];
|
|
fPoints[fSize - 1] = p;
|
|
fSum += p;
|
|
}
|
|
|
|
|
|
BPoint
|
|
LowPassFilter::Output() const
|
|
{
|
|
return BPoint(fSum.x / (float) fSize, fSum.y / (float) fSize);
|
|
}
|