From e7f4dfa74857229528fd149c845fc7d4f2bd3be4 Mon Sep 17 00:00:00 2001 From: John Toman Date: Wed, 16 Sep 2009 03:51:40 +0000 Subject: [PATCH] adds in advanced gradient control to axis code (thanks to Yue Shi Lai) git-svn-id: svn://svn.code.sf.net/p/qjoypad/code/trunk@116 c05e91a0-76c8-4ec0-b377-ef19ce7cc080 --- src/axis.cpp | 100 +++++++++++++++++++++++++++++++++------------- src/axis.h | 22 ++++++---- src/axis_edit.cpp | 53 ++++++++++++++++++++---- src/axis_edit.h | 9 ++++- src/constant.h | 3 ++ 5 files changed, 143 insertions(+), 44 deletions(-) diff --git a/src/axis.cpp b/src/axis.cpp index 6d8d354..e1b4349 100644 --- a/src/axis.cpp +++ b/src/axis.cpp @@ -2,6 +2,9 @@ #include "event.h" #include "time.h" #define sqr(a) ((a)*(a)) +#define cub(a) ((a)*(a)*(a)) +#define clamp(a, a_low, a_high) \ + ((a) < (a_low) ? (a_low) : (a) > (a_high) ? (a_high) : (a)) Axis::Axis( int i ) { @@ -13,9 +16,6 @@ Axis::Axis( int i ) { toDefault(); tick = 0; timer = new QTimer(this); - a = 0; - b = 0; - c = 0; } @@ -36,7 +36,7 @@ bool Axis::read( QTextStream* stream ) { bool ok; //int to store values derived from strings int val; - + float fval; //step through each word, check if it's a token we recognize for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) { if (*it == "maxspeed") { @@ -66,6 +66,21 @@ bool Axis::read( QTextStream* stream ) { if (ok && val >= 0 && val <= JOYMAX) xZone = val; else return false; } + else if (*it == "tcurve") { + ++it; + if (it == words.end()) return false; + val = (*it).toInt(&ok); + if (ok && val >= 0 && val <= power_function) transferCurve = val; + else return false; + } + else if (*it == "sens") { + ++it; + if (it == words.end()) return false; + fval = (*it).toFloat(&ok); + if (ok && fval >= SENSITIVITY_MIN && fval <= SENSITIVITY_MAX) + sensitivity = fval; + else return false; + } //and for the positive keycode, else if (*it == "+key") { ++it; @@ -134,6 +149,10 @@ void Axis::write( QTextStream* stream ) { } else { if (gradient) *stream << "maxSpeed " << maxSpeed << ", "; + if (transferCurve != quadratic) + *stream << "tCurve " << transferCurve << ", "; + if (sensitivity != 1.0F) + *stream << "sens " << sensitivity << ", "; *stream << "mouse"; if (mode == mousepv) *stream << "+v\n"; @@ -199,6 +218,8 @@ void Axis::toDefault() { gradient = false; throttle = 0; maxSpeed = 100; + transferCurve = quadratic; + sensitivity = 1.0F; dZone = DZONE; tick = 0; xZone = XZONE; @@ -275,13 +296,10 @@ void Axis::timerTick( int tick ) { } void Axis::adjustGradient() { - //create a nice quadratic curve fitting it to the points - //(dZone,0) and (xZone,MaxSpeed) - a = (double) (maxSpeed) / sqr(xZone - dZone); - b = -2 * a * dZone; - c = a * sqr(dZone); - //actual equation for curve is: y = ax^2 + b - //where x is the state of the axis and y is the distance the mouse should move. + inverseRange = 1.0F / (xZone - dZone); + // This is also the convenient spot to initialize the dithering + // accmulator. + sumDist = 0; } void Axis::move( bool press ) { @@ -305,23 +323,51 @@ void Axis::move( bool press ) { //if using the mouse else if (press) { int dist; - if (gradient) { - //calculate our mouse speed curve based on calculations made in - //adjustGradient() - int absState = abs(state); - if (absState >= xZone) dist = maxSpeed; - else if (absState <= dZone) dist = 0; - else dist = (int) (a*sqr(absState) + b*absState + c); - } - //if not gradient, always go full speed. - else dist = maxSpeed; - //if we're on the negative side of the axis, must compensate for - //squaring and make distance negative. - if (state < 0) dist = -dist; - e.type = WARP; - if (mode == mousepv) { - e.value1 = 0; + if (gradient) { + const int absState = abs(state); + float fdist; // Floating point movement distance + + if (absState >= xZone) fdist = 1.0F; + else if (absState <= dZone) fdist = 0.0F; + else { + const float u = inverseRange * (absState - dZone); + + switch(transferCurve) { + case quadratic: + fdist = sqr(u); + break; + case cubic: + fdist = cub(u); + break; + case quadratic_extreme: + fdist = sqr(u); + if(u >= 0.95F) { + fdist *= 1.5F; + } + break; + case power_function: + fdist = clamp(powf(u, 1.0F / clamp( + sensitivity, 1e-8F, 1e+3F)), 0.0F, 1.0F); + break; + default: + fdist = u; + } + } + fdist *= maxSpeed; + if (state < 0) fdist = -fdist; + // Accumulate the floating point distance and shift the + // mouse by the rounded magnitude + sumDist += fdist; + dist = static_cast(rint(sumDist)); + sumDist -= dist; + } + //if not gradient, always go full speed. + else dist = maxSpeed; + + e.type = WARP; + if (mode == mousepv) { + e.value1 = 0; e.value2 = dist; } else if (mode == mousenv) { diff --git a/src/axis.h b/src/axis.h index 760f34e..b76e57a 100644 --- a/src/axis.h +++ b/src/axis.h @@ -3,6 +3,7 @@ //abs() #include +#include #include #include @@ -16,6 +17,8 @@ //each axis can create a key press or move the mouse in one of four directions. enum AxisMode {keybd, mousepv, mousenv, mouseph, mousenh}; +enum TransferCurve {linear, quadratic, cubic, quadratic_extreme, + power_function}; //represents one joystick axis class Axis : public QObject { @@ -65,14 +68,17 @@ class Axis : public QObject { bool isDown; //variables for calculating quadratic used for gradient mouse axes - double a,b,c; - - //actual axis settings: - bool gradient; - int maxSpeed; //0..MAXMOUSESPEED - int throttle; //-1 (nkey), 0 (no throttle), 1 (pkey) - int dZone;//-32767 .. 32767 - int xZone;//-32767 .. 32767 + float inverseRange; + + //actual axis settings: + bool gradient; + int maxSpeed; //0..MAXMOUSESPEED + unsigned int transferCurve; + float sensitivity; + int throttle; //-1 (nkey), 0 (no throttle), 1 (pkey) + int dZone;//-32767 .. 32767 + int xZone;//-32767 .. 32767 + double sumDist; AxisMode mode; //positive keycode int pkeycode; diff --git a/src/axis_edit.cpp b/src/axis_edit.cpp index 25f2196..c462263 100644 --- a/src/axis_edit.cpp +++ b/src/axis_edit.cpp @@ -24,6 +24,7 @@ AxisEdit::AxisEdit( Axis* ax ) v2->setSpacing(5); CGradient = new QCheckBox("Gradient", this); CGradient->setChecked(axis->gradient); + connect(CGradient, SIGNAL(toggled(bool)), this, SLOT( CGradientChanged( bool ))); v2->addWidget(CGradient); CMode = new QComboBox(this); @@ -35,6 +36,16 @@ AxisEdit::AxisEdit( Axis* ax ) CMode->setCurrentIndex( axis->mode ); connect(CMode, SIGNAL(activated(int)), this, SLOT( CModeChanged( int ))); v2->addWidget(CMode); + CTransferCurve = new QComboBox(this); + CTransferCurve->insertItem(linear, QString("Linear"), Qt::DisplayRole); + CTransferCurve->insertItem(quadratic, QString("Quadratic"),Qt::DisplayRole ); + CTransferCurve->insertItem(cubic, QString("Cubic"),Qt::DisplayRole ); + CTransferCurve->insertItem(quadratic_extreme, QString("Quadratic Extreme"), Qt::DisplayRole); + CTransferCurve->insertItem(power_function, QString("Power Function"), Qt::DisplayRole); + CTransferCurve->setCurrentIndex( axis->transferCurve ); + CTransferCurve->setEnabled(axis->gradient); + connect(CTransferCurve, SIGNAL(activated(int)), this, SLOT( CTransferCurveChanged( int ))); + v2->addWidget(CTransferCurve); h->addLayout(v2); MouseBox = new QFrame(this); @@ -50,6 +61,13 @@ AxisEdit::AxisEdit( Axis* ax ) SSpeed->setSingleStep(1); SSpeed->setValue(axis->maxSpeed); v2->addWidget(SSpeed); + LSensitivity = new QLabel("Sensitivity", MouseBox); + v2->addWidget(LSensitivity); + SSensitivity = new QDoubleSpinBox(MouseBox); + SSensitivity->setRange(1e-3F, 1e+3F); + SSensitivity->setSingleStep(0.10); + SSensitivity->setValue(axis->sensitivity); + v2->addWidget(SSensitivity); h->addWidget(MouseBox); v->addLayout(h); @@ -87,6 +105,7 @@ AxisEdit::AxisEdit( Axis* ax ) v->addLayout(h); CModeChanged( axis->mode ); + CTransferCurveChanged( axis->transferCurve ); CThrottleChanged( axis->throttle + 1 ); } @@ -98,6 +117,16 @@ void AxisEdit::show() { void AxisEdit::setState( int val ) { Slider->setValue( val ); } +void AxisEdit::CGradientChanged( bool on ) { + CTransferCurve->setEnabled(on); + if (on) { + CTransferCurveChanged( axis->transferCurve ); + } + else { + LSensitivity->setEnabled(false); + SSensitivity->setEnabled(false); + } +} void AxisEdit::CModeChanged( int index ) { if (index == keybd) { @@ -107,6 +136,21 @@ void AxisEdit::CModeChanged( int index ) { else { MouseBox->setEnabled(true); KeyBox->setEnabled(false); + if (CGradient->isChecked()) { + CTransferCurve->setEnabled(true); + CTransferCurveChanged( axis->transferCurve ); + } + } +} + +void AxisEdit::CTransferCurveChanged( int index ) { + if (index == power_function) { + LSensitivity->setEnabled(true); + SSensitivity->setEnabled(true); + } + else { + LSensitivity->setEnabled(false); + SSensitivity->setEnabled(false); } } @@ -129,15 +173,10 @@ void AxisEdit::CThrottleChanged( int index ) { } void AxisEdit::accept() { -//if the gradient status has changed, either request a timer or turn it down. - /*if (axis->gradient) { - if (!CGradient->isChecked()) tossTimer(axis); - } - else { - if (CGradient->isChecked()) takeTimer(axis); - }*/ axis->gradient = CGradient->isChecked(); axis->maxSpeed = SSpeed->value(); + axis->transferCurve = (TransferCurve)CTransferCurve->currentIndex(); + axis->sensitivity = SSensitivity->value(); axis->throttle = CThrottle->currentIndex() - 1; axis->dZone = Slider->dZone(); axis->xZone = Slider->xZone(); diff --git a/src/axis_edit.h b/src/axis_edit.h index 8a8b56c..a875a4c 100644 --- a/src/axis_edit.h +++ b/src/axis_edit.h @@ -6,6 +6,7 @@ #include #include #include +#include #include //for my home-brewed widgets #include "joyslider.h" @@ -22,7 +23,9 @@ class AxisEdit : public QDialog { void setState( int val ); protected slots: //slots for GUI events - void CModeChanged( int index ); + void CGradientChanged( bool on ); + void CModeChanged( int index ); + void CTransferCurveChanged( int index ); void CThrottleChanged( int index ); void accept(); protected: @@ -30,9 +33,11 @@ class AxisEdit : public QDialog { Axis *axis; //the important parts of the dialog: QCheckBox *CGradient; - QComboBox *CMode, *CThrottle; + QComboBox *CMode, *CThrottle, *CTransferCurve; QFrame *MouseBox, *KeyBox; QSpinBox *SSpeed; + QLabel *LSensitivity; + QDoubleSpinBox *SSensitivity; KeyButton *BNeg, *BPos; JoySlider *Slider; QPushButton *BOkay, *BCancel; diff --git a/src/constant.h b/src/constant.h index 3cea77a..2469fbb 100644 --- a/src/constant.h +++ b/src/constant.h @@ -22,6 +22,9 @@ //fastest the mouse can go. Completely arbitrary. #define MAXMOUSESPEED 5000 +#define SENSITIVITY_MIN 1e-8F +#define SENSITIVITY_MAX 1e+8F + #define NAME "QJoyPad 4.0" #define MOUSE_OFFSET 400