[dasher] Implement new two-push dynamic mode which reduces the up-and-down motion



commit 07ca9fd0bedceb489f8a774b9536a8d5676ffbae
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Wed Jun 17 19:20:13 2009 +0100

    Implement new two-push dynamic mode which reduces the up-and-down motion
    of one button dynamic mode by measuring between-click times.
    Also added dynamic button lag to two-button dynamic mode.

 ChangeLog                                 |    5 +
 NEWS                                      |    6 +
 Src/DasherCore/DasherInterfaceBase.cpp    |    2 +
 Src/DasherCore/DasherModel.cpp            |    3 +-
 Src/DasherCore/DasherModel.h              |    9 -
 Src/DasherCore/Makefile.am                |    2 +
 Src/DasherCore/OneButtonDynamicFilter.h   |    4 +-
 Src/DasherCore/Parameters.h               |   16 ++-
 Src/DasherCore/TwoButtonDynamicFilter.cpp |   30 +++-
 Src/DasherCore/TwoButtonDynamicFilter.h   |    3 +
 Src/DasherCore/TwoPushDynamicFilter.cpp   |  263 +++++++++++++++++++++++++++++
 Src/DasherCore/TwoPushDynamicFilter.h     |   56 ++++++
 12 files changed, 380 insertions(+), 19 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index bf8a90b..af3eb1f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2009-06-17  Alan Lawrence <acl33 inf phy cam ac uk>
 
+	* Implement new two-push dynamic mode (TwoPushDynamicFilter.{h,cpp}).
+	Also added dynamic button lag to two-button dynamic mode.
+
+2009-06-17  Alan Lawrence <acl33 inf phy cam ac uk>
+
 	* Re-implement dynamic mode speed control:
 	Periodically increase speed as long as in a 'running' state;
 	decrease every time we start to reverse.
diff --git a/NEWS b/NEWS
index 330726c..f390983 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+==========
+Dasher 5.0
+==========
+  * New two-push, one button, dynamic mode (which reduces
+  the up-and-down motion of one button dynamic mode).
+
 =============
 Dasher 4.10.2
 =============
diff --git a/Src/DasherCore/DasherInterfaceBase.cpp b/Src/DasherCore/DasherInterfaceBase.cpp
index dde675c..f5c99bc 100644
--- a/Src/DasherCore/DasherInterfaceBase.cpp
+++ b/Src/DasherCore/DasherInterfaceBase.cpp
@@ -48,6 +48,7 @@
 #include "OneDimensionalFilter.h"
 #include "StylusFilter.h"
 #include "TwoButtonDynamicFilter.h"
+#include "TwoPushDynamicFilter.h"
 
 // STL headers
 #include <cstdio>
@@ -946,6 +947,7 @@ void CDasherInterfaceBase::CreateModules() {
 #endif
   RegisterModule(new COneButtonDynamicFilter(m_pEventHandler, m_pSettingsStore, this));
   RegisterModule(new CTwoButtonDynamicFilter(m_pEventHandler, m_pSettingsStore, this));
+  RegisterModule(new CTwoPushDynamicFilter(m_pEventHandler, m_pSettingsStore, this));
   // TODO: specialist factory for button mode
   RegisterModule(new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 5, 1, true,8, _("Menu Mode")));
   RegisterModule(new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 3, 0, false,10, _("Direct Mode")));
diff --git a/Src/DasherCore/DasherModel.cpp b/Src/DasherCore/DasherModel.cpp
index b64ef23..41d66df 100644
--- a/Src/DasherCore/DasherModel.cpp
+++ b/Src/DasherCore/DasherModel.cpp
@@ -380,7 +380,8 @@ void CDasherModel::Get_new_root_coords(dasherint X, dasherint Y, dasherint &r1,
 
   double dFactor;
 
-  if(IsSlowdown(iTime))
+  if(GetBoolParameter(BP_SLOW_START) &&
+     ((iTime - m_iStartTime) < GetLongParameter(LP_SLOW_START_TIME)))
     dFactor = 0.1 * (1 + 9 * ((iTime - m_iStartTime) / static_cast<double>(GetLongParameter(LP_SLOW_START_TIME))));
   else 
     dFactor = 1.0;
diff --git a/Src/DasherCore/DasherModel.h b/Src/DasherCore/DasherModel.h
index f5c0e03..74c0618 100644
--- a/Src/DasherCore/DasherModel.h
+++ b/Src/DasherCore/DasherModel.h
@@ -166,15 +166,6 @@ class Dasher::CDasherModel:public CFrameRate, private NoClones
   };
 
   ///
-  /// Return whether Dasher is currently being temporarily slowed or
-  /// not.
-  ///
-
-  bool IsSlowdown(unsigned long iTime) {
-    return ((iTime - m_iStartTime) < static_cast<unsigned long>(GetLongParameter(LP_SLOW_START_TIME)));
-  };
-
-  ///
   /// Check whether a change of root node is needed, and perform the
   /// update if so
   /// TODO: Could be done in UpdateBounds?
diff --git a/Src/DasherCore/Makefile.am b/Src/DasherCore/Makefile.am
index 5266d78..547d4a5 100644
--- a/Src/DasherCore/Makefile.am
+++ b/Src/DasherCore/Makefile.am
@@ -119,6 +119,8 @@ libdashercore_a_SOURCES = \
 		TwoBoxStartHandler.h \
 		TwoButtonDynamicFilter.cpp \
 		TwoButtonDynamicFilter.h \
+		TwoPushDynamicFilter.cpp \
+		TwoPushDynamicFilter.h \
 		UserButton.cpp \
 		UserButton.h \
 		UserLocation.cpp \
diff --git a/Src/DasherCore/OneButtonDynamicFilter.h b/Src/DasherCore/OneButtonDynamicFilter.h
index 6c7a3fb..f79bcd9 100644
--- a/Src/DasherCore/OneButtonDynamicFilter.h
+++ b/Src/DasherCore/OneButtonDynamicFilter.h
@@ -18,8 +18,8 @@
 // along with Dasher; if not, write to the Free Software 
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
-#ifndef __DYNAMIC_FILTER_H__
-#define __DYNAMIC_FILTER_H__
+#ifndef __ONE_BUTTON_DYNAMIC_FILTER_H__
+#define __ONE_BUTTON_DYNAMIC_FILTER_H__
 
 #include "ButtonMultiPress.h"
 
diff --git a/Src/DasherCore/Parameters.h b/Src/DasherCore/Parameters.h
index 9bc5a34..ce2965c 100644
--- a/Src/DasherCore/Parameters.h
+++ b/Src/DasherCore/Parameters.h
@@ -44,7 +44,7 @@ enum {
   BP_COMPASSMODE, BP_SOCKET_INPUT_ENABLE, BP_SOCKET_DEBUG, 
   BP_OLD_STYLE_PUSH, BP_CIRCLE_START, BP_GLOBAL_KEYBOARD, 
   BP_DELAY_VIEW, BP_CONVERSION_MODE, BP_PAUSE_OUTSIDE, BP_BACKOFF_BUTTON, 
-  BP_TWOBUTTON_REVERSE, BP_SLOW_START, END_OF_BPS
+  BP_TWOBUTTON_REVERSE, BP_SLOW_START, BP_FIXED_MARKERS, END_OF_BPS
 };
 
 enum { 
@@ -60,6 +60,8 @@ enum {
   LP_SOCKET_INPUT_Y_MIN, LP_SOCKET_INPUT_Y_MAX, LP_OX, LP_OY, LP_MAX_Y, LP_INPUT_FILTER, 
   LP_CIRCLE_PERCENT, LP_TWO_BUTTON_OFFSET, LP_HOLD_TIME, LP_MULTIPRESS_TIME, LP_MULTIPRESS_COUNT, 
   LP_SLOW_START_TIME, LP_CONVERSION_ORDER, LP_CONVERSION_TYPE,
+  LP_TWO_PUSH_OUTER, LP_TWO_PUSH_UP, LP_TWO_PUSH_DOWN, LP_TWO_PUSH_TOLERANCE,
+  LP_DYNAMIC_BUTTON_LAG,
   LP_DEMO_SPRING, LP_DEMO_NOISE_MEM, LP_DEMO_NOISE_MAG, LP_MAXZOOM, 
   LP_DYNAMIC_SPEED_INC, LP_DYNAMIC_SPEED_FREQ, LP_DYNAMIC_SPEED_DEC, END_OF_LPS
 };
@@ -160,7 +162,8 @@ static bp_table boolparamtable[] = {
   {BP_PAUSE_OUTSIDE, "PauseOutside", PERS, false, "Whether to pause when pointer leaves canvas area"},
   {BP_BACKOFF_BUTTON, "BackoffButton", PERS, true, "Whether to enable the extra backoff button in dynamic mode"},
   {BP_TWOBUTTON_REVERSE, "TwoButtonReverse", PERS, false, "Reverse the up/down buttons in two button mode"},
-  {BP_SLOW_START, "SlowStart", PERS, false, "Start at low speed and insrease"},
+  {BP_SLOW_START, "SlowStart", PERS, false, "Start at low speed and increase"},
+  {BP_FIXED_MARKERS, "TwoPushMarkersFixed", PERS, false, "Two-push Dynamic Mode markers fixed to canvas"},
 };
 
 static lp_table longparamtable[] = {
@@ -213,13 +216,18 @@ static lp_table longparamtable[] = {
   {LP_SLOW_START_TIME, "SlowStartTime", PERS, 1000, "Time over which slow start occurs"},
   {LP_CONVERSION_ORDER, "ConversionOrder", PERS, 0, "Conversion ordering"},
   {LP_CONVERSION_TYPE, "ConversionType", PERS, 0, "Conversion type"},
+  {LP_TWO_PUSH_OUTER, "TwoPushOuter", PERS, 1792, "Offset for one button dynamic mode outer marker"},
+  {LP_TWO_PUSH_UP, "TwoPushUp", PERS, 1536, "Offset to up marker in one button dynamic"},
+  {LP_TWO_PUSH_DOWN, "TwoPushDown", PERS, 1280, "Offset to down marker in one button dynamic"},
+  {LP_TWO_PUSH_TOLERANCE, "TwoPushTolerance", PERS, 100, "Tolerance of two-push-mode pushes, in ms"},
+  {LP_DYNAMIC_BUTTON_LAG, "DynamicButtonLag", PERS, 50, "Lag of pushes in dynamic button mode (ms)"},
   {LP_DEMO_SPRING, "DemoSpring", PERS, 100, "Springyness in Demo-mode"},
   {LP_DEMO_NOISE_MEM, "DemoNoiseMem", PERS, 100, "Memory parameter for noise in Demo-mode"},
   {LP_DEMO_NOISE_MAG, "DemoNoiseMag", PERS, 325, "Magnitude of noise in Demo-mode"},
   {LP_MAXZOOM, "ClickMaxZoom", PERS, 200, "Maximum zoom possible in click mode (times 10)"},
-  {LP_DYNAMIC_SPEED_INC, "DynamicSpeedInc", PERS, 1, "%age by which dynamic mode auto speed control increases speed"},
+  {LP_DYNAMIC_SPEED_INC, "DynamicSpeedInc", PERS, 3, "%age by which dynamic mode auto speed control increases speed"},
   {LP_DYNAMIC_SPEED_FREQ, "DynamicSpeedFreq", PERS, 10, "Seconds after which dynamic mode auto speed control increases speed"},
-  {LP_DYNAMIC_SPEED_DEC, "DynamicSpeedDec", PERS, 10, "%age by which dynamic mode auto speed control decreases speed on reverse"},
+  {LP_DYNAMIC_SPEED_DEC, "DynamicSpeedDec", PERS, 8, "%age by which dynamic mode auto speed control decreases speed on reverse"},
 };
 
 static sp_table stringparamtable[] = {
diff --git a/Src/DasherCore/TwoButtonDynamicFilter.cpp b/Src/DasherCore/TwoButtonDynamicFilter.cpp
index a7d490f..beee743 100644
--- a/Src/DasherCore/TwoButtonDynamicFilter.cpp
+++ b/Src/DasherCore/TwoButtonDynamicFilter.cpp
@@ -38,13 +38,19 @@ static SModuleSettings sSettings[] = {
   {BP_TWOBUTTON_REVERSE,T_BOOL, -1, -1, -1, -1, _("Reverse up and down buttons")},
   {BP_SLOW_START,T_BOOL, -1, -1, -1, -1, _("Slow startup")},
   {LP_SLOW_START_TIME, T_LONG, 0, 10000, 1000, 100, _("Startup time")},
+  {LP_DYNAMIC_BUTTON_LAG, T_LONG, 0, 1000, 1, 25, _("Lag before user actually pushes button (ms)")}, 
   {LP_DYNAMIC_SPEED_INC, T_LONG, 1, 100, 1, 1, _("%age by which to automatically increase speed")},
   {LP_DYNAMIC_SPEED_FREQ, T_LONG, 1, 1000, 1, 1, _("Time after which to automatically increase speed (secs)")},
   {LP_DYNAMIC_SPEED_DEC, T_LONG, 1, 99, 1, 1, _("%age by which to decrease speed upon reverse")}
 };
 
 CTwoButtonDynamicFilter::CTwoButtonDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface)
-  : CButtonMultiPress(pEventHandler, pSettingsStore, pInterface, 14, 1, _("Two Button Dynamic Mode")) { }
+  : CButtonMultiPress(pEventHandler, pSettingsStore, pInterface, 14, 1, _("Two Button Dynamic Mode"))
+{
+  //ensure that m_dLagMul is properly initialised
+  Dasher::CParameterNotificationEvent oEvent(LP_DYNAMIC_BUTTON_LAG);
+  HandleEvent(&oEvent);
+}
 
 bool CTwoButtonDynamicFilter::DecorateView(CDasherView *pView) {
   CDasherScreen *pScreen(pView->Screen());
@@ -102,12 +108,12 @@ void CTwoButtonDynamicFilter::ActionButton(int iTime, int iButton, int iType, CD
     iFactor = -1;
 
   if(iButton == 2) {
-    pModel->Offset(iFactor * GetLongParameter(LP_TWO_BUTTON_OFFSET));
+    pModel->Offset(iFactor * GetLongParameter(LP_TWO_BUTTON_OFFSET) * m_dLagMul);
     if(pUserLog)
       pUserLog->KeyDown(iButton, iType, 3);
   }
   else if((iButton == 3) || (iButton == 4)) {
-    pModel->Offset(iFactor * -GetLongParameter(LP_TWO_BUTTON_OFFSET));
+    pModel->Offset(iFactor * -GetLongParameter(LP_TWO_BUTTON_OFFSET) * m_dLagMul);
     if(pUserLog)
       pUserLog->KeyDown(iButton, iType, 4);
   }
@@ -128,3 +134,21 @@ bool CTwoButtonDynamicFilter::GetMinWidth(int &iMinWidth) {
   iMinWidth = 1024;
   return true;
 }
+
+void CTwoButtonDynamicFilter::HandleEvent(Dasher::CEvent *pEvent)
+{
+  if (pEvent->m_iEventType == EV_PARAM_NOTIFY)
+  {
+    Dasher::CParameterNotificationEvent *pEvt = static_cast<Dasher::CParameterNotificationEvent *>(pEvent);
+    switch (pEvt->m_iParameter)
+    {
+    case LP_MAX_BITRATE:
+    case LP_BOOSTFACTOR: // Deliberate fallthrough
+    case LP_DYNAMIC_BUTTON_LAG:
+      {
+        double dMaxRate = GetLongParameter(LP_MAX_BITRATE) * GetLongParameter(LP_BOOSTFACTOR) / 10000.0;
+        m_dLagMul = exp(dMaxRate * GetLongParameter(LP_DYNAMIC_BUTTON_LAG)/1000.0);
+      }
+    }
+  }
+}
diff --git a/Src/DasherCore/TwoButtonDynamicFilter.h b/Src/DasherCore/TwoButtonDynamicFilter.h
index 5219657..3c7cb8d 100644
--- a/Src/DasherCore/TwoButtonDynamicFilter.h
+++ b/Src/DasherCore/TwoButtonDynamicFilter.h
@@ -40,10 +40,13 @@ class CTwoButtonDynamicFilter : public CButtonMultiPress {
   virtual bool GetSettings(SModuleSettings **pSettings, int *iCount);
 
   virtual bool GetMinWidth(int &iMinWidth);
+
+  virtual void HandleEvent(Dasher::CEvent *pEvent);
   
  private:
   virtual bool TimerImpl(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted);
   virtual void ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
+  double m_dLagMul;
 };
 /// @}
 
diff --git a/Src/DasherCore/TwoPushDynamicFilter.cpp b/Src/DasherCore/TwoPushDynamicFilter.cpp
new file mode 100644
index 0000000..df30642
--- /dev/null
+++ b/Src/DasherCore/TwoPushDynamicFilter.cpp
@@ -0,0 +1,263 @@
+// TwoPushDynamicFilter.cpp
+//
+// Copyright (c) 2007 The Dasher Team
+//
+// This file is part of Dasher.
+//
+// Dasher is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Dasher is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Dasher; if not, write to the Free Software 
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+#include "../Common/Common.h"
+
+#include "TwoPushDynamicFilter.h"
+#include "DasherInterfaceBase.h"
+#include "Event.h"
+
+static SModuleSettings sSettings[] = {
+  {LP_TWO_PUSH_OUTER, T_LONG, 1024, 2048, 2048, 128, _("Offset for outer (second) button")},
+  {LP_TWO_PUSH_UP, T_LONG, 256, 2048, 2048/*divisor*/, 128/*step*/, _("Distance for 1st button UP")},
+  {LP_TWO_PUSH_DOWN, T_LONG, 256, 2048, 2048, 128, _("Distance for 1st button DOWN")},
+  {LP_TWO_PUSH_TOLERANCE, T_LONG, 50, 1000, 1, 10, _("Tolerance for inaccurate timing of button pushes (in ms)")},
+  /* TRANSLATORS: The time for which a button must be held before it counts as a 'long' (rather than short) press. */
+  {LP_HOLD_TIME, T_LONG, 100, 10000, 1000, 100, _("Long press time")},
+  /* TRANSLATORS: Backoff = reversing in Dasher to correct mistakes. This allows a single button to be dedicated to activating backoff, rather than using multiple presses of other buttons, and another to be dedicated to starting and stopping. 'Button' in this context is a physical hardware device, not a UI element.*/
+  {BP_BACKOFF_BUTTON,T_BOOL, -1, -1, -1, -1, _("Enable backoff and start/stop buttons")},
+  {BP_FIXED_MARKERS, T_BOOL, -1, -1, -1, -1, _("Markers fixed to canvas")},
+  {BP_SLOW_START,T_BOOL, -1, -1, -1, -1, _("Slow startup")},
+  {LP_SLOW_START_TIME, T_LONG, 0, 10000, 1000, 100, _("Slow startup time")},
+  {LP_DYNAMIC_SPEED_INC, T_LONG, 1, 100, 1, 1, _("%age by which to automatically increase speed")},
+  {LP_DYNAMIC_SPEED_FREQ, T_LONG, 1, 1000, 1, 1, _("Time after which to automatically increase speed (secs)")},
+  {LP_DYNAMIC_SPEED_DEC, T_LONG, 1, 99, 1, 1, _("%age by which to decrease speed upon reverse")},
+  {LP_DYNAMIC_BUTTON_LAG, T_LONG, 0, 1000, 1, 25, _("Lag before user actually pushes button (ms)")}, 
+};
+
+CTwoPushDynamicFilter::CTwoPushDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface)
+  : CDynamicFilter(pEventHandler, pSettingsStore, pInterface, 14, 1, _("Two-push Dynamic Mode (New One Button)")) {
+  
+  Dasher::CParameterNotificationEvent oEvent(LP_TWO_PUSH_OUTER);//and all the others too!
+  HandleEvent(&oEvent);
+}
+
+void GuideLine(CDasherView *pView, const myint iDasherY, const int iColour)
+{
+  myint iDasherX = -100;
+  CDasherScreen::point p[2];
+  CDasherScreen *pScreen(pView->Screen());
+  
+  pView->Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
+
+  iDasherX = -1000;
+
+  pView->Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
+
+  pScreen->Polyline(p, 2, 3, iColour);
+}  
+
+bool CTwoPushDynamicFilter::DecorateView(CDasherView *pView)
+{  
+  //outer guides (yellow rects)
+  for (int i=0; i<2; i++)
+  {
+    screenint x1, y1, x2, y2;
+    CDasherScreen *pScreen(pView->Screen());
+  
+    pView->Dasher2Screen(-100, m_aaiGuideAreas[i][0], x1, y1);
+    pView->Dasher2Screen(-1000, m_aaiGuideAreas[i][1], x2, y2);
+  
+    pScreen->DrawRectangle(x1, y1, x2, y2, 62/*pale yellow*/, -1, Opts::Nodes1, false, true, 0);
+  }
+
+  //inner guides (red lines)
+  GuideLine(pView, 2048 - GetLongParameter(LP_TWO_PUSH_UP), 1);
+  GuideLine(pView, 2048 + GetLongParameter(LP_TWO_PUSH_DOWN), 1);
+
+  if (GetBoolParameter(BP_FIXED_MARKERS))
+  { //outer guides (at center of rects) - red lines
+    GuideLine(pView, 2048 - GetLongParameter(LP_TWO_PUSH_OUTER), 1);
+    GuideLine(pView, 2048 + GetLongParameter(LP_TWO_PUSH_OUTER), 1);
+  }
+
+  //moving markers - green if active, else yellow
+  int myState;
+  if (m_bDecorationChanged && isRunning(myState) && myState == 1)
+  {
+    for (int i = 0; i < 2; i++)
+    {
+      GuideLine(pView, m_aiMarker[i], (i == m_iActiveMarker) ? 240 : 61/*orange*/);
+    }
+  }
+  bool bRV(m_bDecorationChanged);
+  m_bDecorationChanged = false;
+  return bRV;
+}
+
+void CTwoPushDynamicFilter::HandleEvent(Dasher::CEvent * pEvent)
+{
+  if(pEvent->m_iEventType == EV_PARAM_NOTIFY)
+  {
+    Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
+    switch (pEvt->m_iParameter)
+    {
+      case LP_TWO_PUSH_OUTER: //deliberate fallthrough
+      case LP_TWO_PUSH_UP: //deliberate fallthrough
+      case LP_TWO_PUSH_DOWN:
+      {
+//cout << "Initializing - outer " << GetLongParameter(LP_TWO_PUSH_OUTER) << " up " << GetLongParameter(LP_TWO_PUSH_UP) << " down " << GetLongParameter(LP_TWO_PUSH_DOWN) << "\n";
+	DASHER_ASSERT (GetLongParameter(LP_TWO_PUSH_UP) < GetLongParameter(LP_TWO_PUSH_OUTER));
+	DASHER_ASSERT (GetLongParameter(LP_TWO_PUSH_DOWN) < GetLongParameter(LP_TWO_PUSH_OUTER));
+	DASHER_ASSERT (GetLongParameter(LP_TWO_PUSH_UP) > GetLongParameter(LP_TWO_PUSH_DOWN));
+		//TODO, that means short gap at the top - allow other way around also?
+
+	double dOuter = GetLongParameter(LP_TWO_PUSH_OUTER);
+	m_dLogUpMul = log(dOuter / (double)GetLongParameter(LP_TWO_PUSH_UP));
+	m_dLogDownMul = log(dOuter / (double)GetLongParameter(LP_TWO_PUSH_DOWN));
+	m_dSqrtUpDist = exp(m_dLogUpMul / 2.0);
+	m_dSqrtDownDist = exp(m_dLogDownMul / 2.0);
+//cout << "bitsUp " << m_dLogUpMul << " bitsDown " << m_dLogDownMul << " upDist " << m_dSqrtUpDist << " downDist " << m_dSqrtDownDist << "\n";
+      } //and fallthrough
+      case LP_TWO_PUSH_TOLERANCE:
+      case LP_MAX_BITRATE:
+      case LP_BOOSTFACTOR: // Deliberate fallthrough
+      {
+	double dMaxRate = GetLongParameter(LP_MAX_BITRATE) * GetLongParameter(LP_BOOSTFACTOR) / 10000.0;
+	double dPressBits = dMaxRate * (double) GetLongParameter(LP_TWO_PUSH_TOLERANCE) / 1000.0;
+//cout << "Max Bitrate changed - now " << dMaxRate << " user accuracy " << dPressBits;
+	m_dMinShortTwoPushTime = m_dLogUpMul - dPressBits;
+//cout << "bits; minShort " << m_dMinShortTwoPushTime;
+	m_dMaxShortTwoPushTime = m_dLogUpMul + dPressBits;
+	m_dMinLongTwoPushTime = m_dLogDownMul - dPressBits;
+	if (m_dMaxShortTwoPushTime > m_dMinLongTwoPushTime)
+          m_dMaxShortTwoPushTime = m_dMinLongTwoPushTime = (m_dLogUpMul + m_dLogDownMul)/2.0;
+	m_dMaxLongTwoPushTime = m_dLogDownMul + dPressBits;
+//TODO, what requirements do we actually need to make to ensure sanity (specifically, that computed m_aiTarget's are in range)?
+//cout << " maxShort " << m_dMaxShortTwoPushTime << " minLong " << m_dMinLongTwoPushTime << " maxLong " << m_dMaxLongTwoPushTime << "\n";
+	m_bDecorationChanged = true;
+     }  //and fallthrough again
+     case LP_DYNAMIC_BUTTON_LAG:
+     {
+       double dMaxRate = GetLongParameter(LP_MAX_BITRATE) * GetLongParameter(LP_BOOSTFACTOR) / 10000.0;
+       m_dLagBits = dMaxRate * GetLongParameter(LP_DYNAMIC_BUTTON_LAG)/1000.0;
+  //cout << " lag (" << m_dLagBits[0] << ", " << m_dLagBits[1] << ", " << m_dLagBits[2] << ", " << m_dLagBits[3] << ")";
+     } //and fallthrough
+     case BP_FIXED_MARKERS:
+        if (GetBoolParameter(BP_FIXED_MARKERS))
+        {
+           m_aaiGuideAreas[0][0] = 2048 - GetLongParameter(LP_TWO_PUSH_UP)*exp(m_dMaxShortTwoPushTime);
+           m_aaiGuideAreas[0][1] = 2048 - GetLongParameter(LP_TWO_PUSH_UP)*exp(m_dMinShortTwoPushTime);
+           m_aaiGuideAreas[1][0] = 2048 + GetLongParameter(LP_TWO_PUSH_DOWN)*exp(m_dMinLongTwoPushTime);
+           m_aaiGuideAreas[1][1] = 2048 + GetLongParameter(LP_TWO_PUSH_DOWN)*exp(m_dMaxLongTwoPushTime);
+        }
+        else
+        {
+          m_aaiGuideAreas[0][0] = 2048 - GetLongParameter(LP_TWO_PUSH_UP) * exp(m_dMaxShortTwoPushTime / 2.0) * m_dSqrtUpDist;
+          m_aaiGuideAreas[0][1] = 2048 - GetLongParameter(LP_TWO_PUSH_UP) * exp(m_dMinShortTwoPushTime / 2.0) * m_dSqrtUpDist;
+          m_aaiGuideAreas[1][0] = 2048 + GetLongParameter(LP_TWO_PUSH_DOWN) * exp(m_dMinLongTwoPushTime / 2.0) * m_dSqrtDownDist;
+          m_aaiGuideAreas[1][1] = 2048 + GetLongParameter(LP_TWO_PUSH_DOWN) * exp(m_dMaxLongTwoPushTime / 2.0) * m_dSqrtDownDist;
+        }
+	break;
+    }
+  }
+
+};
+
+void CTwoPushDynamicFilter::ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog) {
+  // Types:
+  // 0 = ordinary click
+  // 1 = long click  
+  int myState;
+  if (!isRunning(myState)) DASHER_ASSERT(false);
+
+  if (myState == 0) //no button pushed (recently)
+  {
+//cout << "First push - event type " << iType << " \n";
+    pModel->GetNats();
+    run(1);
+    pModel->ResetNats();
+  }
+  else
+  {
+    DASHER_ASSERT (myState == 1);
+//cout << "Second push - event type " << iType << " logGrowth " << pModel->GetNats() << "\n";
+    if (m_iActiveMarker == -1)
+      reverse();
+    else
+    {
+      pModel->Offset(m_aiTarget[m_iActiveMarker]);
+      pModel->ResetNats();
+      run(0);
+    }
+  }
+}
+bool doSet(int &var, const int val)
+{
+  if (var == val) return false;
+  var = val;
+  return true;
+}
+
+bool CTwoPushDynamicFilter::TimerImpl(int iTime, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted)
+{
+  int myState;
+  if (!isRunning(myState)) DASHER_ASSERT(false);
+  if (myState == 1) // button pushed
+  {
+    double dLogGrowth(m_pDasherModel->GetNats());
+    double dUpDist = exp(dLogGrowth/2.0) * m_dSqrtUpDist * GetLongParameter(LP_TWO_PUSH_UP);
+    double dDownDist = exp(dLogGrowth/2.0)*m_dSqrtDownDist*GetLongParameter(LP_TWO_PUSH_DOWN);
+    m_aiTarget[0] = dUpDist * exp(m_dLagBits);
+    m_aiTarget[1] = -dDownDist * exp(m_dLagBits);
+    if (GetBoolParameter(BP_FIXED_MARKERS))
+    {
+      m_bDecorationChanged |= doSet(m_aiMarker[0], 2048 - GetLongParameter(LP_TWO_PUSH_UP)*exp(m_dLagBits + dLogGrowth));
+      m_bDecorationChanged |= doSet(m_aiMarker[1], 2048 + GetLongParameter(LP_TWO_PUSH_DOWN)*exp(m_dLagBits + dLogGrowth));
+    }
+    else
+    {
+      m_bDecorationChanged |= doSet(m_aiMarker[0], 2048 - dUpDist * exp(m_dLagBits/2.0));
+      m_bDecorationChanged |= doSet(m_aiMarker[1], 2048 + dDownDist * exp(m_dLagBits/2.0));
+    }
+    if (dLogGrowth > m_dMaxLongTwoPushTime)
+    {
+//cout << " growth " << dLogGrowth << " - reversing\n";
+      //button pushed, but then waited too long.
+      reverse();
+    }
+    else if (dLogGrowth >= m_dMinShortTwoPushTime && dLogGrowth <= m_dMaxShortTwoPushTime)
+      m_bDecorationChanged |= doSet(m_iActiveMarker, 0 /*up*/);
+    else if (dLogGrowth >= m_dMinLongTwoPushTime)
+      m_bDecorationChanged |= doSet(m_iActiveMarker, 1 /*down*/);
+    else m_bDecorationChanged |= doSet(m_iActiveMarker, -1 /*in middle (neither/both) or too short*/);
+  }
+  m_pDasherModel->OneStepTowards(100, 2048, iTime, pAdded, pNumDeleted);
+}
+
+void CTwoPushDynamicFilter::Activate() {
+  SetBoolParameter(BP_DELAY_VIEW, true);
+}
+
+void CTwoPushDynamicFilter::Deactivate() {
+  SetBoolParameter(BP_DELAY_VIEW, false);
+}
+
+bool CTwoPushDynamicFilter::GetSettings(SModuleSettings **pSettings, int *iCount) {
+  *pSettings = sSettings;
+  *iCount = sizeof(sSettings) / sizeof(SModuleSettings);
+
+  return true;
+};
+
+bool CTwoPushDynamicFilter::GetMinWidth(int &iMinWidth) {
+  iMinWidth = 1024;
+  return true;
+}
diff --git a/Src/DasherCore/TwoPushDynamicFilter.h b/Src/DasherCore/TwoPushDynamicFilter.h
new file mode 100644
index 0000000..38b027c
--- /dev/null
+++ b/Src/DasherCore/TwoPushDynamicFilter.h
@@ -0,0 +1,56 @@
+// TwoPushDynamicFilter.h
+//
+// Copyright (c) 2007 The Dasher Team
+//
+// This file is part of Dasher.
+//
+// Dasher is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Dasher is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Dasher; if not, write to the Free Software 
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+#ifndef __TWO_PUSH_DYNAMIC_FILTER_H__
+#define __TWO_PUSH_DYNAMIC_FILTER_H__
+
+#include "DynamicFilter.h"
+
+/// \ingroup InputFilter
+/// @{
+class CTwoPushDynamicFilter : public CDynamicFilter /*long push, but do our own "multi-push" detection*/ {
+ public:
+  CTwoPushDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface);
+  
+  // Inherited methods
+  virtual bool DecorateView(CDasherView *pView);
+ 
+  virtual void Activate();
+  virtual void Deactivate();
+  virtual bool GetMinWidth(int &iMinWidth);
+  virtual bool GetSettings(SModuleSettings **pSettings, int *iCount);
+ protected:
+  virtual bool TimerImpl(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted);
+  virtual void ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
+
+  virtual void HandleEvent(Dasher::CEvent * pEvent);
+
+ private:
+  double m_dLogUpMul, m_dLogDownMul, m_dSqrtUpDist, m_dSqrtDownDist, m_dLagBits;
+  double m_dMinShortTwoPushTime, m_dMaxShortTwoPushTime,
+    m_dMinLongTwoPushTime, m_dMaxLongTwoPushTime;
+  int m_aiMarker[2];
+  int m_iActiveMarker;
+  int m_aiTarget[2];
+  int m_aaiGuideAreas[2][2];
+};
+/// @}
+
+#endif
\ No newline at end of file



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]