xref: /haiku/src/kits/shared/ShakeTrackingFilter.cpp (revision 569634e95b33bac519419f1efa77f95b0a16cae9)
18e43b9e3SAlexandre Deckner /*
28e43b9e3SAlexandre Deckner  * Copyright 2009, Alexandre Deckner, alex@zappotek.com
38e43b9e3SAlexandre Deckner  * Distributed under the terms of the MIT License.
48e43b9e3SAlexandre Deckner  */
58e43b9e3SAlexandre Deckner 
68e43b9e3SAlexandre Deckner /*!
78e43b9e3SAlexandre Deckner 	\class ShakeTrackingFilter
88e43b9e3SAlexandre Deckner 	\brief A simple mouse shake detection filter
98e43b9e3SAlexandre Deckner 	*
108e43b9e3SAlexandre Deckner 	* A simple mouse filter that detects quick mouse shakes.
118e43b9e3SAlexandre Deckner 	*
128e43b9e3SAlexandre Deckner 	* It's detecting rough edges (u-turns) in the mouse movement
138e43b9e3SAlexandre Deckner 	* and counts them within a time window.
148e43b9e3SAlexandre Deckner 	* You can configure the message sent, the u-turn count threshold
158e43b9e3SAlexandre Deckner 	* and the time threshold.
168e43b9e3SAlexandre Deckner 	* It sends the count along with the message.
178e43b9e3SAlexandre Deckner 	* For now, detection is limited within the view bounds, but
188e43b9e3SAlexandre Deckner 	* it might be modified to accept a BRegion mask.
198e43b9e3SAlexandre Deckner 	*
208e43b9e3SAlexandre Deckner */
218e43b9e3SAlexandre Deckner 
22*569634e9SAlexandre Deckner 
238e43b9e3SAlexandre Deckner #include <ShakeTrackingFilter.h>
248e43b9e3SAlexandre Deckner 
258e43b9e3SAlexandre Deckner #include <Message.h>
268e43b9e3SAlexandre Deckner #include <Messenger.h>
278e43b9e3SAlexandre Deckner #include <MessageRunner.h>
288e43b9e3SAlexandre Deckner #include <View.h>
298e43b9e3SAlexandre Deckner 
30*569634e9SAlexandre Deckner 
31abd3d52bSAlexandre Deckner const uint32 kMsgCancel = 'Canc';
328e43b9e3SAlexandre Deckner 
33*569634e9SAlexandre Deckner 
ShakeTrackingFilter(BView * targetView,uint32 messageWhat,uint32 countThreshold,bigtime_t timeThreshold)348e43b9e3SAlexandre Deckner ShakeTrackingFilter::ShakeTrackingFilter(BView* targetView, uint32 messageWhat,
358e43b9e3SAlexandre Deckner 	uint32 countThreshold, bigtime_t timeThreshold)
368e43b9e3SAlexandre Deckner 	:
378e43b9e3SAlexandre Deckner 	BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
388e43b9e3SAlexandre Deckner 	fTargetView(targetView),
398e43b9e3SAlexandre Deckner 	fMessageWhat(messageWhat),
408e43b9e3SAlexandre Deckner 	fCancelRunner(NULL),
418e43b9e3SAlexandre Deckner 	fLowPass(8),
428e43b9e3SAlexandre Deckner 	fLastDelta(0, 0),
438e43b9e3SAlexandre Deckner 	fCounter(0),
448e43b9e3SAlexandre Deckner 	fCountThreshold(countThreshold),
458e43b9e3SAlexandre Deckner 	fTimeThreshold(timeThreshold)
468e43b9e3SAlexandre Deckner {
478e43b9e3SAlexandre Deckner }
488e43b9e3SAlexandre Deckner 
498e43b9e3SAlexandre Deckner 
~ShakeTrackingFilter()508e43b9e3SAlexandre Deckner ShakeTrackingFilter::~ShakeTrackingFilter()
518e43b9e3SAlexandre Deckner {
528e43b9e3SAlexandre Deckner 	delete fCancelRunner;
538e43b9e3SAlexandre Deckner }
548e43b9e3SAlexandre Deckner 
558e43b9e3SAlexandre Deckner 
568e43b9e3SAlexandre Deckner filter_result
Filter(BMessage * message,BHandler **)578e43b9e3SAlexandre Deckner ShakeTrackingFilter::Filter(BMessage* message, BHandler** /*_target*/)
588e43b9e3SAlexandre Deckner {
598e43b9e3SAlexandre Deckner 	if (fTargetView == NULL)
608e43b9e3SAlexandre Deckner 		return B_DISPATCH_MESSAGE;
618e43b9e3SAlexandre Deckner 
628e43b9e3SAlexandre Deckner 	switch (message->what) {
638e43b9e3SAlexandre Deckner 		case B_MOUSE_MOVED:
648e43b9e3SAlexandre Deckner 		{
658e43b9e3SAlexandre Deckner 			BPoint position;
668e43b9e3SAlexandre Deckner 			message->FindPoint("be:view_where", &position);
678e43b9e3SAlexandre Deckner 
688e43b9e3SAlexandre Deckner 			// TODO: allow using BRegion masks
698e43b9e3SAlexandre Deckner 			if (!fTargetView->Bounds().Contains(position))
708e43b9e3SAlexandre Deckner 				return B_DISPATCH_MESSAGE;
718e43b9e3SAlexandre Deckner 
728e43b9e3SAlexandre Deckner 			fLowPass.Input(position - fLastPosition);
738e43b9e3SAlexandre Deckner 
748e43b9e3SAlexandre Deckner 			BPoint delta = fLowPass.Output();
758e43b9e3SAlexandre Deckner 
768e43b9e3SAlexandre Deckner 			// normalized dot product
778e43b9e3SAlexandre Deckner 			float norm = delta.x * delta.x + delta.y * delta.y;
788e43b9e3SAlexandre Deckner 			if (norm > 0.01) {
798e43b9e3SAlexandre Deckner 				delta.x /= norm;
808e43b9e3SAlexandre Deckner 				delta.y /= norm;
818e43b9e3SAlexandre Deckner 			}
828e43b9e3SAlexandre Deckner 
838e43b9e3SAlexandre Deckner 			norm = fLastDelta.x * fLastDelta.x + fLastDelta.y * fLastDelta.y;
848e43b9e3SAlexandre Deckner 			if (norm > 0.01) {
858e43b9e3SAlexandre Deckner 				fLastDelta.x /= norm;
868e43b9e3SAlexandre Deckner 				fLastDelta.y /= norm;
878e43b9e3SAlexandre Deckner 			}
888e43b9e3SAlexandre Deckner 
898e43b9e3SAlexandre Deckner 			float dot = delta.x * fLastDelta.x + delta.y * fLastDelta.y;
908e43b9e3SAlexandre Deckner 
918e43b9e3SAlexandre Deckner 			if (dot < 0.0) {
928e43b9e3SAlexandre Deckner 				if (fCounter == 0) {
938e43b9e3SAlexandre Deckner 					BMessage * cancelMessage = new BMessage(kMsgCancel);
948e43b9e3SAlexandre Deckner 		 			fCancelRunner = new BMessageRunner(BMessenger(fTargetView),
958e43b9e3SAlexandre Deckner 		 				cancelMessage, fTimeThreshold, 1);
968e43b9e3SAlexandre Deckner 				}
978e43b9e3SAlexandre Deckner 
988e43b9e3SAlexandre Deckner 				fCounter++;
998e43b9e3SAlexandre Deckner 
1008e43b9e3SAlexandre Deckner 				if (fCounter >= fCountThreshold) {
1018e43b9e3SAlexandre Deckner 					BMessage shakeMessage(fMessageWhat);
1028e43b9e3SAlexandre Deckner 					shakeMessage.AddUInt32("count", fCounter);
1038e43b9e3SAlexandre Deckner 					BMessenger messenger(fTargetView);
1048e43b9e3SAlexandre Deckner 					messenger.SendMessage(&shakeMessage);
1058e43b9e3SAlexandre Deckner 				}
1068e43b9e3SAlexandre Deckner 			}
1078e43b9e3SAlexandre Deckner 
1088e43b9e3SAlexandre Deckner 			fLastDelta = fLowPass.Output();
1098e43b9e3SAlexandre Deckner 			fLastPosition = position;
1108e43b9e3SAlexandre Deckner 
1118e43b9e3SAlexandre Deckner 			return B_DISPATCH_MESSAGE;
1128e43b9e3SAlexandre Deckner 		}
1138e43b9e3SAlexandre Deckner 
1148e43b9e3SAlexandre Deckner 		case kMsgCancel:
1158e43b9e3SAlexandre Deckner 			delete fCancelRunner;
1168e43b9e3SAlexandre Deckner 			fCancelRunner = NULL;
1178e43b9e3SAlexandre Deckner 			fCounter = 0;
1188e43b9e3SAlexandre Deckner 			return B_SKIP_MESSAGE;
1198e43b9e3SAlexandre Deckner 
1208e43b9e3SAlexandre Deckner 		default:
1218e43b9e3SAlexandre Deckner 			break;
1228e43b9e3SAlexandre Deckner 	}
1238e43b9e3SAlexandre Deckner 
1248e43b9e3SAlexandre Deckner 	return B_DISPATCH_MESSAGE;
1258e43b9e3SAlexandre Deckner }
1268e43b9e3SAlexandre Deckner 
1278e43b9e3SAlexandre Deckner 
1288e43b9e3SAlexandre Deckner //	#pragma mark -
1298e43b9e3SAlexandre Deckner 
1308e43b9e3SAlexandre Deckner 
LowPassFilter(uint32 size)1318e43b9e3SAlexandre Deckner LowPassFilter::LowPassFilter(uint32 size)
1328e43b9e3SAlexandre Deckner 	:
1338e43b9e3SAlexandre Deckner 	fSize(size)
1348e43b9e3SAlexandre Deckner {
1358e43b9e3SAlexandre Deckner 	fPoints = new BPoint[fSize];
1368e43b9e3SAlexandre Deckner }
1378e43b9e3SAlexandre Deckner 
1388e43b9e3SAlexandre Deckner 
~LowPassFilter()1398e43b9e3SAlexandre Deckner LowPassFilter::~LowPassFilter()
1408e43b9e3SAlexandre Deckner {
1418e43b9e3SAlexandre Deckner 	delete [] fPoints;
1428e43b9e3SAlexandre Deckner }
1438e43b9e3SAlexandre Deckner 
1448e43b9e3SAlexandre Deckner 
1458e43b9e3SAlexandre Deckner void
Input(const BPoint & p)1468e43b9e3SAlexandre Deckner LowPassFilter::Input(const BPoint& p)
1478e43b9e3SAlexandre Deckner {
1488e43b9e3SAlexandre Deckner 	// A fifo buffer that maintains a sum of its elements
1498e43b9e3SAlexandre Deckner 	fSum -= fPoints[0];
1508e43b9e3SAlexandre Deckner 	for (uint32 i = 0; i < fSize - 1; i++)
1518e43b9e3SAlexandre Deckner 		fPoints[i] = fPoints[i + 1];
1528e43b9e3SAlexandre Deckner 	fPoints[fSize - 1] = p;
1538e43b9e3SAlexandre Deckner 	fSum += p;
1548e43b9e3SAlexandre Deckner }
1558e43b9e3SAlexandre Deckner 
1568e43b9e3SAlexandre Deckner 
1578e43b9e3SAlexandre Deckner BPoint
Output() const1588e43b9e3SAlexandre Deckner LowPassFilter::Output() const
1598e43b9e3SAlexandre Deckner {
1608e43b9e3SAlexandre Deckner 	return BPoint(fSum.x / (float) fSize, fSum.y / (float) fSize);
1618e43b9e3SAlexandre Deckner }
162