diff options
-rw-r--r-- | tracker-tobii-eyex/CMakeLists.txt | 6 | ||||
-rw-r--r-- | tracker-tobii-eyex/tobii-eyex-dialog.cpp | 25 | ||||
-rw-r--r-- | tracker-tobii-eyex/tobii-eyex-dialog.ui | 210 | ||||
-rw-r--r-- | tracker-tobii-eyex/tobii-eyex.cpp | 173 | ||||
-rw-r--r-- | tracker-tobii-eyex/tobii-eyex.hpp | 22 |
5 files changed, 228 insertions, 208 deletions
diff --git a/tracker-tobii-eyex/CMakeLists.txt b/tracker-tobii-eyex/CMakeLists.txt index cfbe0160..20f4badd 100644 --- a/tracker-tobii-eyex/CMakeLists.txt +++ b/tracker-tobii-eyex/CMakeLists.txt @@ -1,7 +1,7 @@ if(WIN32) set(SDK_TOBII_EYEX "" CACHE PATH "") if(SDK_TOBII_EYEX) - opentrack_boilerplate(opentrack-tracker-tobii-eyex NO-INSTALL) + opentrack_boilerplate(opentrack-tracker-tobii-eyex) target_link_libraries(opentrack-tracker-tobii-eyex opentrack-spline-widget) set(tobii-libdir ${SDK_TOBII_EYEX}/lib/x86/) set(tobii-dll ${tobii-libdir}/Tobii.EyeX.Client.dll) @@ -13,9 +13,7 @@ if(WIN32) set(tobii-incdir ${SDK_TOBII_EYEX}/include/eyex) target_include_directories(opentrack-tracker-tobii-eyex SYSTEM PUBLIC ${tobii-incdir}) - if(FALSE) - install(FILES ${tobii-dll} DESTINATION ${opentrack-hier-pfx} ${opentrack-perms}) - endif() + install(FILES ${tobii-dll} DESTINATION ${opentrack-hier-pfx} ${opentrack-perms}) if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") file(TO_CMAKE_PATH "$ENV{SystemRoot}" sysroot) if (IS_DIRECTORY "${sysroot}/SysWOW64") diff --git a/tracker-tobii-eyex/tobii-eyex-dialog.cpp b/tracker-tobii-eyex/tobii-eyex-dialog.cpp index bb9ffcea..7f69fa16 100644 --- a/tracker-tobii-eyex/tobii-eyex-dialog.cpp +++ b/tracker-tobii-eyex/tobii-eyex-dialog.cpp @@ -11,6 +11,31 @@ tobii_eyex_dialog::tobii_eyex_dialog() ui.tracking_mode->addItem("Absolute", tobii_absolute); tie_setting(s.mode, ui.tracking_mode); + + ui.relative_mode_gain->setConfig(&rs.acc_mode_spline); + ui.relative_mode_gain->set_preview_only(true); + + tie_setting(rs.dz_len, ui.deadzone); + tie_setting(rs.expt_slope, ui.exponent); + tie_setting(rs.expt_len, ui.exponent_len); + tie_setting(rs.expt_norm, ui.exponent_norm); + + tie_setting(rs.log_slope, ui.log_base); + tie_setting(rs.log_len, ui.log_len); + tie_setting(rs.log_norm, ui.log_norm); + + connect(rs.b.get(), &bundle_::changed, this, [this]() { rs.make_spline(); }, Qt::QueuedConnection); + + // todo add specialization for label with traits +#if 0 + tie_setting(rs.dz_len, ui.deadzone_label); + tie_setting(rs.expt_slope, ui.exponent_label); + tie_setting(rs.expt_len, ui.exponent_len_label); + tie_setting(rs.expt_norm, ui.exponent_norm_label); + tie_setting(rs.log_slope, ui.log_base_label); + tie_setting(rs.log_len, ui.log_len_label); + tie_setting(rs.log_norm, ui.log_norm_label); +#endif } void tobii_eyex_dialog::do_ok() diff --git a/tracker-tobii-eyex/tobii-eyex-dialog.ui b/tracker-tobii-eyex/tobii-eyex-dialog.ui index 1a85f417..7a8c92aa 100644 --- a/tracker-tobii-eyex/tobii-eyex-dialog.ui +++ b/tracker-tobii-eyex/tobii-eyex-dialog.ui @@ -179,8 +179,8 @@ <property name="verticalSpacing"> <number>3</number> </property> - <item row="0" column="0"> - <widget class="QLabel" name="label_11"> + <item row="1" column="0"> + <widget class="QLabel" name="label_4"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -188,7 +188,7 @@ </sizepolicy> </property> <property name="text"> - <string>Speed</string> + <string>Deadzone</string> </property> </widget> </item> @@ -208,21 +208,8 @@ </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_4"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Deadzone</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="deadzone_label"> + <item row="2" column="1"> + <widget class="QLabel" name="exponent_label"> <property name="minimumSize"> <size> <width>24</width> @@ -256,21 +243,27 @@ </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_10"> + <item row="2" column="2"> + <widget class="QSlider" name="exponent"> <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>Exponent</string> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + <property name="tickInterval"> + <number>25</number> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QLabel" name="exponent_label"> + <item row="1" column="1"> + <widget class="QLabel" name="deadzone_label"> <property name="minimumSize"> <size> <width>24</width> @@ -285,27 +278,34 @@ </property> </widget> </item> - <item row="2" column="2"> - <widget class="QSlider" name="exponent"> + <item row="0" column="0"> + <widget class="QLabel" name="label_11"> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <property name="text"> + <string>Speed</string> </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_8"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <property name="tickInterval"> - <number>25</number> + <property name="text"> + <string>Log segment length</string> </property> </widget> </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_5"> + <item row="2" column="0"> + <widget class="QLabel" name="label_10"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -313,7 +313,7 @@ </sizepolicy> </property> <property name="text"> - <string>Exponential length</string> + <string>Exponent</string> </property> </widget> </item> @@ -333,8 +333,21 @@ </property> </widget> </item> - <item row="3" column="2"> - <widget class="QSlider" name="exponent_len"> + <item row="3" column="0"> + <widget class="QLabel" name="label_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Exponent segment length</string> + </property> + </widget> + </item> + <item row="5" column="2"> + <widget class="QSlider" name="log_len"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -352,8 +365,8 @@ </property> </widget> </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_7"> + <item row="6" column="0"> + <widget class="QLabel" name="label_9"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -361,12 +374,12 @@ </sizepolicy> </property> <property name="text"> - <string>Linear coefficient</string> + <string>Log slope</string> </property> </widget> </item> - <item row="4" column="1"> - <widget class="QLabel" name="linear_c_label"> + <item row="6" column="1"> + <widget class="QLabel" name="log_base_label"> <property name="minimumSize"> <size> <width>24</width> @@ -381,8 +394,8 @@ </property> </widget> </item> - <item row="4" column="2"> - <widget class="QSlider" name="linear_c"> + <item row="6" column="2"> + <widget class="QSlider" name="log_base"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -400,21 +413,27 @@ </property> </widget> </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_6"> + <item row="0" column="2"> + <widget class="QSlider" name="speed"> <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>Linear length</string> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + <property name="tickInterval"> + <number>25</number> </property> </widget> </item> <item row="5" column="1"> - <widget class="QLabel" name="linear_len_label"> + <widget class="QLabel" name="log_len_label"> <property name="minimumSize"> <size> <width>24</width> @@ -429,56 +448,40 @@ </property> </widget> </item> - <item row="5" column="2"> - <widget class="QSlider" name="linear_len"> + <item row="4" column="0"> + <widget class="QLabel" name="label_6"> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> - </property> - <property name="tickInterval"> - <number>25</number> + <property name="text"> + <string>Exponent norm</string> </property> </widget> </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_8"> + <item row="4" column="2"> + <widget class="QSlider" name="exponent_norm"> <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>Logarithm base</string> - </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="QLabel" name="log_base_label"> - <property name="minimumSize"> - <size> - <width>24</width> - <height>0</height> - </size> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - <property name="text"> - <string>0</string> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + <property name="tickInterval"> + <number>25</number> </property> </widget> </item> - <item row="6" column="2"> - <widget class="QSlider" name="log_base"> + <item row="3" column="2"> + <widget class="QSlider" name="exponent_len"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -496,21 +499,24 @@ </property> </widget> </item> - <item row="7" column="0"> - <widget class="QLabel" name="label_9"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <item row="4" column="1"> + <widget class="QLabel" name="exponent_norm_label"> + <property name="minimumSize"> + <size> + <width>24</width> + <height>0</height> + </size> </property> <property name="text"> - <string>Logarithm coefficient</string> + <string>0</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="7" column="1"> - <widget class="QLabel" name="log_c_label"> + <widget class="QLabel" name="log_norm_label"> <property name="minimumSize"> <size> <width>24</width> @@ -526,7 +532,7 @@ </widget> </item> <item row="7" column="2"> - <widget class="QSlider" name="log_c"> + <widget class="QSlider" name="log_norm"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -544,22 +550,16 @@ </property> </widget> </item> - <item row="0" column="2"> - <widget class="QSlider" name="speed"> + <item row="7" column="0"> + <widget class="QLabel" name="label_12"> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> - </property> - <property name="tickInterval"> - <number>25</number> + <property name="text"> + <string>Log norm</string> </property> </widget> </item> diff --git a/tracker-tobii-eyex/tobii-eyex.cpp b/tracker-tobii-eyex/tobii-eyex.cpp index 74c31240..1a2f922b 100644 --- a/tracker-tobii-eyex/tobii-eyex.cpp +++ b/tracker-tobii-eyex/tobii-eyex.cpp @@ -6,6 +6,8 @@ #include <vector> #include <algorithm> #include <iterator> +#include <utility> +#include <numeric> #include <QDebug> #include <QMutexLocker> #include <QMessageBox> @@ -36,51 +38,80 @@ static inline tobii_eyex_tracker& to_self(TX_USERPARAM param) return *reinterpret_cast<tobii_eyex_tracker*>(param); } -template<typename t> -static constexpr t clamp(t datum, t min, t max) +// there's an underflow in spline code, can't use 1e0 +static constexpr const double spline_max = 1e2; + +void rel_settings::make_spline_(part* functors, unsigned len) { - return ((datum > max) ? max : ((datum < min) ? min : datum)); + acc_mode_spline.removeAllPoints(); + + double lastx = 0, lasty = 0; + + using std::accumulate; + + const double inv_norm_y = 1./accumulate(functors, functors + len, 1e-4, [](double acc, const part& functor) { return acc + functor.norm; }); + const double inv_norm_x = 1./accumulate(functors, functors + len, 1e-4, [](double acc, const part& functor) { return acc + functor.len; }); + + for (unsigned k = 0; k < len; k++) + { + part& fun = functors[k]; + + const double xscale = fun.len * spline_max * inv_norm_x; + const double maxx = fun.f(1); + const double yscale = fun.norm * spline_max * inv_norm_y * (maxx < 1e-3 ? 0 : 1./maxx); + + for (unsigned i = 0; i <= fun.nparts; i++) + { + const double x = lastx + (fun.nparts == 0 ? 1 : i) / (1.+fun.nparts) * xscale; + const double y = lasty + clamp(fun.f(x) * yscale, 0, spline_max); + qDebug() << k << i << x << y; + acc_mode_spline.addPoint(x, y); + } + + lastx += xscale; + lasty += yscale; + } } -void rel_settings::draw_spline() +/* + def plot(f): + rng = arange(-1 + .01, 1, 1e-4) + plt.plot(rng, map(f, rng)) +*/ + +double rel_settings::gain(double value) { - spline& spline = acc_mode_spline; + return acc_mode_spline.get_value_no_save(value * spline_max) / spline_max; +} - spline.removeAllPoints(); +void rel_settings::make_spline() +{ + const double log_c = 1./std::log(log_slope()); - static constexpr float std_norm_expt = 1.f/3; - const float norm_expt = std_norm_expt * float(expt_norm->cur()); - static constexpr float std_norm_lin = 2.f/3; - const float norm_lin = clamp((1-norm_expt) * lin_norm->cur() * std_norm_lin, 0., 1.); + part functors[] + { + { 1, dz_len(), 0, [](double) { return 0; } }, + { 5, expt_len(), expt_norm(), [=](double x) { return std::pow(x, expt_slope()); } }, + { 7, 1 - dz_len() - expt_len() - log_len(), std::max(0., 1 - expt_norm() - log_norm()), [](double x) { return x; } }, + { 7, log_len(), log_norm(), [=](double x) { return std::log(1+x)*log_c; } }, + }; + make_spline_(functors, std::distance(std::begin(functors), std::end(functors))); } rel_settings::rel_settings() : opts("tobii-eyex-relative-mode"), - speed(b, "speed", s(5, .1, 10)), - dz_end_pt(b, "deadzone-length", s(4, 0, 15)), - expt_slope(b, "exponent-slope", s(1.5, 1.25, 3)), - expt_norm(b, "exponent-norm", s(1, .25, 4)), - lin_norm(b, "linear-norm", s(1, .25, 4)), + speed(b, "speed", s(3, .1, 10)), + dz_len(b, "deadzone-length", s(.04, 0, .2)), + expt_slope(b, "exponent-slope", s(1.75, 1.25, 3)), + expt_len(b, "exponent-length", s(.25, 0, .5)), + expt_norm(b, "exponent-norm", s(.3, .1, .5)), + log_slope(b, "log-slope", s(2.75, 1.25, 10)), + log_len(b, "log-len", s(.1, 0, .2)), + log_norm(b, "log-norm", s(.1, .05, .3)), acc_mode_spline(100, 100, "") { - QObject::connect(&dz_end_pt, - static_cast<void(base_value::*)(const slider_value&) const>(&base_value::valueChanged), - this, - &rel_settings::draw_spline); - QObject::connect(&expt_slope, - static_cast<void(base_value::*)(const slider_value&) const>(&base_value::valueChanged), - this, - &rel_settings::draw_spline); - QObject::connect(&expt_norm, - static_cast<void(base_value::*)(const slider_value&) const>(&base_value::valueChanged), - this, - &rel_settings::draw_spline); - QObject::connect(&lin_norm, - static_cast<void(base_value::*)(const slider_value&) const>(&base_value::valueChanged), - this, - &rel_settings::draw_spline); - draw_spline(); + make_spline(); } tobii_eyex_tracker::tobii_eyex_tracker() : @@ -287,67 +318,16 @@ void tobii_eyex_tracker::start_tracker(QFrame*) dbg_verbose("api initialized"); } -// the gain function was prototyped in python with jupyter qtconsole. -// you can use qtconsole's inline matplotlib support to see the gain function. -// the `piecewise' function assumes monotonic growth or constant value for all functions. -/* - -from math import * -from itertools import izip -import matplotlib -import matplotlib.pyplot as plt - -try: - import IPython - IPython.get_ipython().magic(u'matplotlib inline') -except: - pass - -def frange(from_, to_, step_=1e-4): - i = from_ - while True: - yield i - i += step_ - if i >= to_: - break - -def plot_fn(fn, from_=0., to_=1., step=None): - if step is None: - step = max(1e-4, (to_-from_)*1e-4) - xs = [i for i in frange(from_, to_, step)] - plt.plot(xs, map(fn, xs)) - -def piecewise(x, funs, bounds): - y = 0. - last_bound = 0. - norm = 0. - for fun in funs: - norm += fun(1.) - for fun, bound in izip(funs, bounds): - if x > bound: - y += fun(1.) - else: - b = bound - last_bound - x_ = (x - last_bound) / b - y += fun(x_) - break - last_bound = bound - return y / norm - -def f(x): return x**1.75 -def g(x): return 1.75*1.75*x -def h(x): return log(1+x)/log(2.5) -def zero(x): return 0. - -plot_fn(lambda x: piecewise(x, [zero, f, g, h], [.05, .25, .7, 1.])) - -*/ - tobii_eyex_tracker::num tobii_eyex_tracker::gain(num x_) { return 1; } +static inline double signum(double x) +{ + return !(x < 0) - (x < 0); +} + void tobii_eyex_tracker::data(double* data) { TX_REAL px, py, dw, dh, x_, y_; @@ -378,20 +358,21 @@ void tobii_eyex_tracker::data(double* data) { const double dt = t.elapsed_seconds(); t.start(); - // XXX TODO make slider - static constexpr double v = 300; - const double x = gain(x_); - const double y = gain(y_); + using std::fabs; + + static constexpr double max_yaw = 45, max_pitch = 30; + static constexpr double c_yaw = 3; + static constexpr double c_pitch = c_yaw * max_pitch / max_yaw; - const double yaw_delta = (x * v) * dt; - const double pitch_delta = (y * -v) * dt; + const double yaw_delta = gain(fabs(x_)) * signum(x_) * c_yaw * dt; + const double pitch_delta = gain(fabs(y_)) * signum(y_) * c_pitch * dt; yaw += yaw_delta; pitch += pitch_delta; - yaw = clamp(yaw, -180., 180.); - pitch = clamp(pitch, -60., 60.); + yaw = clamp(yaw, -max_yaw, max_yaw); + pitch = clamp(pitch, -max_pitch, max_pitch); } if (do_center) diff --git a/tracker-tobii-eyex/tobii-eyex.hpp b/tracker-tobii-eyex/tobii-eyex.hpp index aeac4d89..68acb25c 100644 --- a/tracker-tobii-eyex/tobii-eyex.hpp +++ b/tracker-tobii-eyex/tobii-eyex.hpp @@ -18,9 +18,11 @@ using namespace options; #include "spline-widget/spline.hpp" #include "spline-widget/spline-widget.hpp" +#include <functional> #include <atomic> #include <QObject> #include <QMutex> +#include <QTimer> enum tobii_mode { @@ -31,13 +33,27 @@ enum tobii_mode class rel_settings final : public QObject, public opts { Q_OBJECT + + using functor = std::function<double(double)>; + + struct part + { + int nparts; + double len, norm; + functor f; + }; + + void make_spline_(part* functors, unsigned len); + public: using s = slider_value; - value<slider_value> speed, dz_end_pt, expt_slope, expt_norm, lin_norm; + value<slider_value> speed, dz_len, expt_slope, expt_len, expt_norm, log_slope, log_len, log_norm; spline acc_mode_spline; rel_settings(); -private slots: - void draw_spline(); + double gain(double value); + +public slots: + void make_spline(); }; struct settings final : public opts |