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
This commit is contained in:
John Toman
2009-09-16 03:51:40 +00:00
committed by virtuoussin13
parent 85dfc3ee90
commit e7f4dfa748
5 changed files with 143 additions and 44 deletions

View File

@ -2,6 +2,9 @@
#include "event.h" #include "event.h"
#include "time.h" #include "time.h"
#define sqr(a) ((a)*(a)) #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 ) { Axis::Axis( int i ) {
@ -13,9 +16,6 @@ Axis::Axis( int i ) {
toDefault(); toDefault();
tick = 0; tick = 0;
timer = new QTimer(this); timer = new QTimer(this);
a = 0;
b = 0;
c = 0;
} }
@ -36,7 +36,7 @@ bool Axis::read( QTextStream* stream ) {
bool ok; bool ok;
//int to store values derived from strings //int to store values derived from strings
int val; int val;
float fval;
//step through each word, check if it's a token we recognize //step through each word, check if it's a token we recognize
for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) { for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) {
if (*it == "maxspeed") { if (*it == "maxspeed") {
@ -66,6 +66,21 @@ bool Axis::read( QTextStream* stream ) {
if (ok && val >= 0 && val <= JOYMAX) xZone = val; if (ok && val >= 0 && val <= JOYMAX) xZone = val;
else return false; 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, //and for the positive keycode,
else if (*it == "+key") { else if (*it == "+key") {
++it; ++it;
@ -134,6 +149,10 @@ void Axis::write( QTextStream* stream ) {
} }
else { else {
if (gradient) *stream << "maxSpeed " << maxSpeed << ", "; if (gradient) *stream << "maxSpeed " << maxSpeed << ", ";
if (transferCurve != quadratic)
*stream << "tCurve " << transferCurve << ", ";
if (sensitivity != 1.0F)
*stream << "sens " << sensitivity << ", ";
*stream << "mouse"; *stream << "mouse";
if (mode == mousepv) if (mode == mousepv)
*stream << "+v\n"; *stream << "+v\n";
@ -199,6 +218,8 @@ void Axis::toDefault() {
gradient = false; gradient = false;
throttle = 0; throttle = 0;
maxSpeed = 100; maxSpeed = 100;
transferCurve = quadratic;
sensitivity = 1.0F;
dZone = DZONE; dZone = DZONE;
tick = 0; tick = 0;
xZone = XZONE; xZone = XZONE;
@ -275,13 +296,10 @@ void Axis::timerTick( int tick ) {
} }
void Axis::adjustGradient() { void Axis::adjustGradient() {
//create a nice quadratic curve fitting it to the points inverseRange = 1.0F / (xZone - dZone);
//(dZone,0) and (xZone,MaxSpeed) // This is also the convenient spot to initialize the dithering
a = (double) (maxSpeed) / sqr(xZone - dZone); // accmulator.
b = -2 * a * dZone; sumDist = 0;
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.
} }
void Axis::move( bool press ) { void Axis::move( bool press ) {
@ -305,23 +323,51 @@ void Axis::move( bool press ) {
//if using the mouse //if using the mouse
else if (press) { else if (press) {
int dist; 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 if (gradient) {
//squaring and make distance negative. const int absState = abs(state);
if (state < 0) dist = -dist; float fdist; // Floating point movement distance
e.type = WARP;
if (mode == mousepv) { if (absState >= xZone) fdist = 1.0F;
e.value1 = 0; 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<int>(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; e.value2 = dist;
} }
else if (mode == mousenv) { else if (mode == mousenv) {

View File

@ -3,6 +3,7 @@
//abs() //abs()
#include <stdlib.h> #include <stdlib.h>
#include <math.h>
#include <QTimer> #include <QTimer>
#include <QTextStream> #include <QTextStream>
@ -16,6 +17,8 @@
//each axis can create a key press or move the mouse in one of four directions. //each axis can create a key press or move the mouse in one of four directions.
enum AxisMode {keybd, mousepv, mousenv, mouseph, mousenh}; enum AxisMode {keybd, mousepv, mousenv, mouseph, mousenh};
enum TransferCurve {linear, quadratic, cubic, quadratic_extreme,
power_function};
//represents one joystick axis //represents one joystick axis
class Axis : public QObject { class Axis : public QObject {
@ -65,14 +68,17 @@ class Axis : public QObject {
bool isDown; bool isDown;
//variables for calculating quadratic used for gradient mouse axes //variables for calculating quadratic used for gradient mouse axes
double a,b,c; float inverseRange;
//actual axis settings: //actual axis settings:
bool gradient; bool gradient;
int maxSpeed; //0..MAXMOUSESPEED int maxSpeed; //0..MAXMOUSESPEED
int throttle; //-1 (nkey), 0 (no throttle), 1 (pkey) unsigned int transferCurve;
int dZone;//-32767 .. 32767 float sensitivity;
int xZone;//-32767 .. 32767 int throttle; //-1 (nkey), 0 (no throttle), 1 (pkey)
int dZone;//-32767 .. 32767
int xZone;//-32767 .. 32767
double sumDist;
AxisMode mode; AxisMode mode;
//positive keycode //positive keycode
int pkeycode; int pkeycode;

View File

@ -24,6 +24,7 @@ AxisEdit::AxisEdit( Axis* ax )
v2->setSpacing(5); v2->setSpacing(5);
CGradient = new QCheckBox("Gradient", this); CGradient = new QCheckBox("Gradient", this);
CGradient->setChecked(axis->gradient); CGradient->setChecked(axis->gradient);
connect(CGradient, SIGNAL(toggled(bool)), this, SLOT( CGradientChanged( bool )));
v2->addWidget(CGradient); v2->addWidget(CGradient);
CMode = new QComboBox(this); CMode = new QComboBox(this);
@ -35,6 +36,16 @@ AxisEdit::AxisEdit( Axis* ax )
CMode->setCurrentIndex( axis->mode ); CMode->setCurrentIndex( axis->mode );
connect(CMode, SIGNAL(activated(int)), this, SLOT( CModeChanged( int ))); connect(CMode, SIGNAL(activated(int)), this, SLOT( CModeChanged( int )));
v2->addWidget(CMode); 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); h->addLayout(v2);
MouseBox = new QFrame(this); MouseBox = new QFrame(this);
@ -50,6 +61,13 @@ AxisEdit::AxisEdit( Axis* ax )
SSpeed->setSingleStep(1); SSpeed->setSingleStep(1);
SSpeed->setValue(axis->maxSpeed); SSpeed->setValue(axis->maxSpeed);
v2->addWidget(SSpeed); 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); h->addWidget(MouseBox);
v->addLayout(h); v->addLayout(h);
@ -87,6 +105,7 @@ AxisEdit::AxisEdit( Axis* ax )
v->addLayout(h); v->addLayout(h);
CModeChanged( axis->mode ); CModeChanged( axis->mode );
CTransferCurveChanged( axis->transferCurve );
CThrottleChanged( axis->throttle + 1 ); CThrottleChanged( axis->throttle + 1 );
} }
@ -98,6 +117,16 @@ void AxisEdit::show() {
void AxisEdit::setState( int val ) { void AxisEdit::setState( int val ) {
Slider->setValue( 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 ) { void AxisEdit::CModeChanged( int index ) {
if (index == keybd) { if (index == keybd) {
@ -107,6 +136,21 @@ void AxisEdit::CModeChanged( int index ) {
else { else {
MouseBox->setEnabled(true); MouseBox->setEnabled(true);
KeyBox->setEnabled(false); 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() { 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->gradient = CGradient->isChecked();
axis->maxSpeed = SSpeed->value(); axis->maxSpeed = SSpeed->value();
axis->transferCurve = (TransferCurve)CTransferCurve->currentIndex();
axis->sensitivity = SSensitivity->value();
axis->throttle = CThrottle->currentIndex() - 1; axis->throttle = CThrottle->currentIndex() - 1;
axis->dZone = Slider->dZone(); axis->dZone = Slider->dZone();
axis->xZone = Slider->xZone(); axis->xZone = Slider->xZone();

View File

@ -6,6 +6,7 @@
#include <QComboBox> #include <QComboBox>
#include <QSpinBox> #include <QSpinBox>
#include <QCheckBox> #include <QCheckBox>
#include <QDoubleSpinBox>
#include <QLabel> #include <QLabel>
//for my home-brewed widgets //for my home-brewed widgets
#include "joyslider.h" #include "joyslider.h"
@ -22,7 +23,9 @@ class AxisEdit : public QDialog {
void setState( int val ); void setState( int val );
protected slots: protected slots:
//slots for GUI events //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 CThrottleChanged( int index );
void accept(); void accept();
protected: protected:
@ -30,9 +33,11 @@ class AxisEdit : public QDialog {
Axis *axis; Axis *axis;
//the important parts of the dialog: //the important parts of the dialog:
QCheckBox *CGradient; QCheckBox *CGradient;
QComboBox *CMode, *CThrottle; QComboBox *CMode, *CThrottle, *CTransferCurve;
QFrame *MouseBox, *KeyBox; QFrame *MouseBox, *KeyBox;
QSpinBox *SSpeed; QSpinBox *SSpeed;
QLabel *LSensitivity;
QDoubleSpinBox *SSensitivity;
KeyButton *BNeg, *BPos; KeyButton *BNeg, *BPos;
JoySlider *Slider; JoySlider *Slider;
QPushButton *BOkay, *BCancel; QPushButton *BOkay, *BCancel;

View File

@ -22,6 +22,9 @@
//fastest the mouse can go. Completely arbitrary. //fastest the mouse can go. Completely arbitrary.
#define MAXMOUSESPEED 5000 #define MAXMOUSESPEED 5000
#define SENSITIVITY_MIN 1e-8F
#define SENSITIVITY_MAX 1e+8F
#define NAME "QJoyPad 4.0" #define NAME "QJoyPad 4.0"
#define MOUSE_OFFSET 400 #define MOUSE_OFFSET 400