diff options
82 files changed, 2035 insertions, 898 deletions
diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 00000000..ca3ba840 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,10 @@ +The following people contributed parts of the codebase to the project, in +chronological order: + +- Stanislaw Halik <<sthalik@misaki.pl>> +- Chris Thompson <<mm0zct@gmail.com>> +- Donovan Baarda <<abo@minkirri.apana.org.au>> +- Xavier Hallade <<xavier.hallade@intel.com>> +- Michael Welter <<mw.pub@welter-4d.de>> + +See OPENTRACK-LICENSING.txt for licensing information. diff --git a/CMakeLists.txt b/CMakeLists.txt index a953c1e7..e3d12828 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ cmake_minimum_required(VERSION 2.8.11) set_property(GLOBAL PROPERTY opentrack-all-modules "") set_property(GLOBAL PROPERTY opentrack-all-source-dirs "") -set(opentrack-all-translations pl_PL ru_RU) +set(opentrack-all-translations nl_NL ru_RU stub) include(opentrack-policy) include(opentrack-word-size) @@ -38,6 +38,7 @@ include(opentrack-hier) include(opentrack-qt) include(opentrack-platform) include(opentrack-boilerplate) + include(opentrack-version) include(opentrack-install) @@ -45,7 +46,7 @@ if(WIN32) enable_language(RC) endif() -add_custom_target(mrproper COMMAND cmake -P "${CMAKE_SOURCE_DIR}/cmake/opentrack-clean-build-directory.cmake" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") +add_custom_target(mrproper COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/opentrack-clean-build-directory.cmake" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") set(C CMakeLists.txt) file(GLOB opentrack-subprojects diff --git a/OPENTRACK-LICENSING.txt b/OPENTRACK-LICENSING.txt new file mode 100644 index 00000000..068fb101 --- /dev/null +++ b/OPENTRACK-LICENSING.txt @@ -0,0 +1,78 @@ +# Code licensing + +The following modules are licensed as part of the opentrack project. See +3rdparty-notices/ for third-party code. + +# Lack of warranty + +Unless warranty terms are specified in particular licensing terms, the +following warranty disclaimer terms apply: + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +# core, various modules + +Copyright (c) 2012-2016 Stanislaw Halik + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +# Rift modules, Sixense Hydra module + +Copyright (c) 2013 mm0zct + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +# EWMA filter + +Copyright (c) 2014 Donovan Baarda <abo@minkirri.apana.org.au> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +# Kalman filter + +Copyright (c) 2016 Michael Welter <mw.pub@welter-4d.de> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +# Intel RealSense tracker + +Copyright (c) 2015, Intel Corporation +Author: Xavier Hallade <xavier.hallade@intel.com> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +# EOF + +# vim: noai:ts=4:sw=4:tw=79 diff --git a/api/plugin-api.hpp b/api/plugin-api.hpp index 996d32d3..e9de9dad 100644 --- a/api/plugin-api.hpp +++ b/api/plugin-api.hpp @@ -12,6 +12,8 @@ #include <QWidget> #include <QFrame> #include <QIcon> +#include <QWidget> +#include <QDialog> #include "export.hpp" @@ -37,7 +39,7 @@ enum Axis { namespace plugin_api { namespace detail { -class OPENTRACK_API_EXPORT BaseDialog : public QWidget +class OPENTRACK_API_EXPORT BaseDialog : public QDialog { Q_OBJECT protected: diff --git a/cmake/opentrack-boilerplate.cmake b/cmake/opentrack-boilerplate.cmake index 8d23ad19..9ca382e8 100644 --- a/cmake/opentrack-boilerplate.cmake +++ b/cmake/opentrack-boilerplate.cmake @@ -199,7 +199,7 @@ function(opentrack_boilerplate n) if(NOT arg_STATIC) string(REGEX REPLACE "^opentrack-" "" n_ "${n}") - string(REGEX REPLACE "^(tracker|filter-proto)-" "" n_ "${n_}") + string(REGEX REPLACE "^(tracker|filter|proto)-" "" n_ "${n_}") string(REPLACE "-" "_" n_ ${n_}) target_compile_definitions(${n} PRIVATE "BUILD_${n_}") @@ -215,23 +215,30 @@ function(opentrack_boilerplate n) endif() endif() + set(SDK_REGEN_TRANSLATIONS FALSE CACHE BOOL "Regenerate translation files on build") + set(langs "") foreach(i ${opentrack-all-translations}) set(t "${CMAKE_CURRENT_SOURCE_DIR}/lang/${i}.ts") - list(APPEND langs "${t}") - get_property(tt GLOBAL PROPERTY opentrack-${i}-ts) - set(tt ${tt} ${t}) - set_property(GLOBAL PROPERTY opentrack-${i}-ts ${tt}) + if(SDK_REGEN_TRANSLATIONS OR NOT EXISTS "${t}") + list(APPEND langs "${t}") + get_property(tt GLOBAL PROPERTY opentrack-${i}-ts) + set(tt ${tt} ${t}) + set_property(GLOBAL PROPERTY opentrack-${i}-ts ${tt}) + endif() endforeach() get_property(modules GLOBAL PROPERTY opentrack-all-modules) list(APPEND modules "${n}") set_property(GLOBAL PROPERTY opentrack-all-modules "${modules}") - add_custom_target(${n}-i18n - COMMAND cmake -E make_directory "${CMAKE_CURRENT_SOURCE_DIR}/lang" - COMMAND "${Qt5_DIR}/../../../bin/lupdate" -silent -recursive -no-obsolete -locations relative . -ts ${langs} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - ) + if(NOT langs STREQUAL "") + add_custom_target(i18n-module-${n} + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_SOURCE_DIR}/lang" + COMMAND "${Qt5_DIR}/../../../bin/lupdate" -silent -recursive -no-obsolete -locations relative . -ts ${langs} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + else() + add_custom_target(i18n-module-${n}) + endif() endfunction() diff --git a/cmake/opentrack-install.cmake b/cmake/opentrack-install.cmake index 3e77e396..3406007b 100644 --- a/cmake/opentrack-install.cmake +++ b/cmake/opentrack-install.cmake @@ -36,6 +36,8 @@ opentrack_inst2("${opentrack-doc-src-pfx}" FILES "${CMAKE_SOURCE_DIR}/CMakeLists opentrack_inst2("${opentrack-doc-src-pfx}" FILES "${CMAKE_SOURCE_DIR}/README.md") opentrack_inst2("${opentrack-doc-src-pfx}" FILES "${CMAKE_SOURCE_DIR}/CONTRIBUTING.md") opentrack_inst2("${opentrack-doc-src-pfx}" FILES "${CMAKE_SOURCE_DIR}/WARRANTY.txt") +opentrack_inst2("${opentrack-doc-src-pfx}" FILES "${CMAKE_SOURCE_DIR}/OPENTRACK-LICENSING.txt") +opentrack_inst2("${opentrack-doc-src-pfx}" FILES "${CMAKE_SOURCE_DIR}/AUTHORS.md") function(opentrack_install_sources n) opentrack_sources(${n} sources) @@ -60,13 +62,16 @@ function(merge_translations) set(deps "") foreach(k ${modules}) - list(APPEND deps "${k}-i18n") + list(APPEND deps "i18n-module-${k}") endforeach() - add_custom_target(i18n-lang-${i} - COMMAND "${Qt5_DIR}/../../../bin/lrelease" -nounfinished -silent ${ts} -qm "${qm-output}" - DEPENDS ${deps} - ) + if(NOT "${ts}" STREQUAL "") + add_custom_target(i18n-lang-${i} + COMMAND "${Qt5_DIR}/../../../bin/lrelease" -nounfinished -silent ${ts} -qm "${qm-output}" + DEPENDS ${deps}) + else() + add_custom_target(i18n-lang-${i} DEPENDS ${deps}) + endif() list(APPEND all-deps "i18n-lang-${i}") install(FILES "${qm-output}" DESTINATION "${opentrack-i18n-pfx}" RENAME "${i}.qm" ${opentrack-perms}) endforeach() diff --git a/cmake/opentrack-qt.cmake b/cmake/opentrack-qt.cmake index bb492eda..c6ab2673 100644 --- a/cmake/opentrack-qt.cmake +++ b/cmake/opentrack-qt.cmake @@ -1,5 +1,5 @@ find_package(Qt5 REQUIRED COMPONENTS Core Network Widgets Gui QUIET) -find_package(Qt5 COMPONENTS SerialPort QUIET) +find_package(Qt5 COMPONENTS SerialPort Gamepad QUIET) include_directories(SYSTEM ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS}) add_definitions(${Qt5Core_DEFINITIONS} ${Qt5Gui_DEFINITIONS} ${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS}) set(MY_QT_LIBS ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES}) diff --git a/compat/make-unique.hpp b/compat/make-unique.hpp index bb5315c5..64c647b1 100644 --- a/compat/make-unique.hpp +++ b/compat/make-unique.hpp @@ -8,7 +8,7 @@ #include <utility> #include <cstddef> -namespace detail { +namespace raii_detail { template<class T> struct Unique_if { typedef std::unique_ptr<T> Single_object; @@ -26,18 +26,19 @@ template<class T, size_t N> struct Unique_if<T[N]> } template<class T, class... Args> - typename detail::Unique_if<T>::Single_object + typename ::raii_detail::Unique_if<T>::Single_object make_unique(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } template<class T> - typename detail::Unique_if<T>::Unknown_bound + typename ::raii_detail::Unique_if<T>::Unknown_bound make_unique(std::size_t n) { typedef typename std::remove_extent<T>::type U; return std::unique_ptr<T>(new U[n]()); } template<class T, class... Args> - typename detail::Unique_if<T>::Known_bound + typename ::raii_detail::Unique_if<T>::Known_bound make_unique(Args&&...) = delete; + diff --git a/compat/ndebug-guard.hpp b/compat/ndebug-guard.hpp new file mode 100644 index 00000000..209177b3 --- /dev/null +++ b/compat/ndebug-guard.hpp @@ -0,0 +1,3 @@ +#ifdef NDEBUG +# error "NDEBUG defined, don't define it" +#endif diff --git a/compat/run-in-thread.hpp b/compat/run-in-thread.hpp index d377f625..90aa143b 100644 --- a/compat/run-in-thread.hpp +++ b/compat/run-in-thread.hpp @@ -1,12 +1,16 @@ #pragma once -#include <QObject> +#include "ndebug-guard.hpp" +#include <cassert> #include <thread> #include <condition_variable> #include <utility> -namespace detail { +#include <QObject> +#include <QThread> + +namespace qt_impl_detail { template<typename t> struct run_in_thread_traits @@ -25,14 +29,14 @@ struct run_in_thread_traits<void> using ret_type = void; static inline void assign(unsigned char&, unsigned char&&) {} static inline void pass(type&&) {} - template<typename F> static type&& call(F& fun) { fun(); return std::move(type(0)); } + template<typename F> static type call(F& fun) { fun(); return type(0); } }; } template<typename F> auto run_in_thread_sync(QObject* obj, F&& fun) - -> typename detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>::ret_type + -> typename qt_impl_detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>::ret_type { using lock_guard = std::unique_lock<std::mutex>; @@ -42,7 +46,7 @@ auto run_in_thread_sync(QObject* obj, F&& fun) std::thread::id waiting_thread = std::this_thread::get_id(); - using traits = detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>; + using traits = qt_impl_detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>; typename traits::type ret; @@ -50,7 +54,9 @@ auto run_in_thread_sync(QObject* obj, F&& fun) { QObject src; - src.moveToThread(obj->thread()); + QThread* t(obj->thread()); + assert(t); + src.moveToThread(t); QObject::connect(&src, &QObject::destroyed, obj, @@ -80,6 +86,8 @@ template<typename F> void run_in_thread_async(QObject* obj, F&& fun) { QObject src; - src.moveToThread(obj->thread()); + QThread* t(obj->thread()); + assert(t); + src.moveToThread(t); QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection); } diff --git a/compat/util.hpp b/compat/util.hpp index 7a6858a3..5b9e2c69 100644 --- a/compat/util.hpp +++ b/compat/util.hpp @@ -1,10 +1,15 @@ #pragma once +#include "ndebug-guard.hpp" + #include "make-unique.hpp" #include "run-in-thread.hpp" #include <memory> #include <cmath> +#include <utility> + +#include <QSharedPointer> #define progn(...) ([&]() { __VA_ARGS__ }()) template<typename t> using mem = std::shared_ptr<t>; @@ -22,7 +27,7 @@ int iround(const t& val) return int(std::round(val)); } -namespace detail { +namespace util_detail { template<typename n> inline auto clamp_(n val, n min, n max) -> n @@ -39,5 +44,13 @@ inline auto clamp_(n val, n min, n max) -> n template<typename t, typename u, typename w> inline auto clamp(const t& val, const u& min, const w& max) -> decltype(val * min * max) { - return ::detail::clamp_<decltype(val * min * max)>(val, min, max); + return ::util_detail::clamp_<decltype(val * min * max)>(val, min, max); +} + +template<typename t, typename... xs> +auto qptr(xs... args) +{ + return QSharedPointer<t>(new t(std::forward<xs>(args)...)); } + +template<typename t> using qshared = QSharedPointer<t>; diff --git a/contrib/translation-stub.sh b/contrib/translation-stub.sh new file mode 100644 index 00000000..bb5ed51d --- /dev/null +++ b/contrib/translation-stub.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +build_dir=build-msvc15 + +set -e + +dir="$(dirname -- "$0")" +cd "$dir/.." + +pushd "./$build_dir" >/dev/null +cmake --build . --target i18n >/dev/null +popd >/dev/null + +rel="$(git describe --tag --alw)" + +rm -f "$rel" +find . -wholename "?*/lang/stub.ts" | zip -q9 "$build_dir/$rel-i18n-stub.zip" -@ diff --git a/filter-accela/accela-settings.hpp b/filter-accela/accela-settings.hpp index b012fc07..2ecd8bbc 100644 --- a/filter-accela/accela-settings.hpp +++ b/filter-accela/accela-settings.hpp @@ -50,11 +50,11 @@ struct settings_accela : opts value<slider_value> rot_nonlinearity; settings_accela() : opts("accela-sliders"), - rot_sensitivity(b, "rotation-sensitivity", slider_value(1.8, .05, 5)), + rot_sensitivity(b, "rotation-sensitivity", slider_value(1.5, .05, 5)), trans_sensitivity(b, "translation-sensitivity", slider_value(1., .05, 1.5)), rot_deadzone(b, "rotation-deadzone", slider_value(.03, 0, 2)), trans_deadzone(b, "translation-deadzone", slider_value(.1, 0, 1)), ewma(b, "ewma", slider_value(0, 0, 30)), - rot_nonlinearity(b, "rotation-nonlinearity", slider_value(1.05, 1, 1.5)) + rot_nonlinearity(b, "rotation-nonlinearity", slider_value(1.2, 1, 1.5)) {} }; diff --git a/filter-accela/ftnoir_accela_filtercontrols.ui b/filter-accela/ftnoir_accela_filtercontrols.ui index 4ed91099..4649bcd6 100644 --- a/filter-accela/ftnoir_accela_filtercontrols.ui +++ b/filter-accela/ftnoir_accela_filtercontrols.ui @@ -10,7 +10,7 @@ <x>0</x> <y>0</y> <width>550</width> - <height>412</height> + <height>435</height> </rect> </property> <property name="sizePolicy"> @@ -308,55 +308,89 @@ <property name="title"> <string>Rotation nonlinearity</string> </property> - <layout class="QGridLayout" name="gridLayout_4"> - <item row="0" column="1"> - <widget class="QLabel" name="rot_nl"> - <property name="minimumSize"> - <size> - <width>50</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>12</pointsize> - </font> - </property> - <property name="text"> - <string><html><head/><body><p>x<span style=" vertical-align:super;">2</span></p></body></html></string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_3"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_5"> <property name="text"> - <string>Value</string> + <string>Setting it higher will filter out rotation noise but may cause sudden jumps on very high settings.</string> </property> </widget> </item> - <item row="0" column="2"> - <widget class="QSlider" name="rot_nl_slider"> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>20</number> - </property> - <property name="pageStep"> - <number>2</number> - </property> - <property name="value"> - <number>1</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> - </property> - <property name="tickInterval"> - <number>3</number> - </property> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Value</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="rot_nl"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="font"> + <font> + <pointsize>12</pointsize> + </font> + </property> + <property name="text"> + <string><html><head/><body><p>x<span style=" vertical-align:super;">2</span></p></body></html></string> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="rot_nl_slider"> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>20</number> + </property> + <property name="pageStep"> + <number>2</number> + </property> + <property name="value"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksAbove</enum> + </property> + <property name="tickInterval"> + <number>3</number> + </property> + </widget> + </item> + </layout> </widget> </item> </layout> diff --git a/filter-accela/ftnoir_filter_accela.cpp b/filter-accela/ftnoir_filter_accela.cpp index 4fec74b2..dea39eb7 100644 --- a/filter-accela/ftnoir_filter_accela.cpp +++ b/filter-accela/ftnoir_filter_accela.cpp @@ -35,45 +35,46 @@ void accela::filter(const double* input, double *output) return; } - const double rot_t = s.rot_sensitivity->cur(); - const double trans_t = s.trans_sensitivity->cur(); +#define cast(x) (static_cast<slider_value&&>((x))) + + const double rot_t = cast(s.rot_sensitivity).cur(); + const double trans_t = cast(s.trans_sensitivity).cur(); const double dt = t.elapsed_seconds(); t.start(); - const double RC = s.ewma->cur() / 1000.; // seconds + const double RC = cast(s.ewma).cur() / 1000.; // seconds const double alpha = dt/(dt+RC); - const double rot_dz = s.rot_deadzone->cur(); - const double trans_dz = s.trans_deadzone->cur(); - const slider_value rot_nl_ = s.rot_nonlinearity; - const double rot_nl = rot_nl_.cur(); + const double rot_dz = cast(s.rot_deadzone).cur(); + const double trans_dz = cast(s.trans_deadzone).cur(); + const slider_value nl = s.rot_nonlinearity; for (int i = 0; i < 6; i++) { spline& m = i >= 3 ? rot : trans; - smoothed_input[i] = smoothed_input[i] * (1.-alpha) + input[i] * alpha; + smoothed_input[i] = smoothed_input[i] * (1-alpha) + input[i] * alpha; const double in = smoothed_input[i]; - const double vec = in - last_output[i]; + const double vec_ = in - last_output[i]; const double dz = i >= 3 ? rot_dz : trans_dz; - const double vec_ = std::max(0., fabs(vec) - dz); + const double vec = std::max(0., fabs(vec_) - dz); const double thres = i >= 3 ? rot_t : trans_t; - const double out_ = vec_ / thres; + const double out_ = vec / thres; const double out = progn( const bool should_apply_rot_nonlinearity = i >= 3 && - std::fabs(rot_nl - 1) > 5e-3 && - vec_ < rot_nl_.max(); + std::fabs(nl.cur() - 1) > 5e-3 && + vec < nl.max(); if (should_apply_rot_nonlinearity) - return std::pow(out_/rot_nl_.max(), rot_nl); + return std::pow(out_/nl.max(), nl.cur()) * nl.max(); else return out_; ); const double val = double(m.getValue(out)); - last_output[i] = output[i] = last_output[i] + signum(vec) * dt * val; + last_output[i] = output[i] = last_output[i] + signum(vec_) * dt * val; } } diff --git a/filter-accela/ftnoir_filter_accela_dialog.cpp b/filter-accela/ftnoir_filter_accela_dialog.cpp index 79db1ffa..f0ee8391 100644 --- a/filter-accela/ftnoir_filter_accela_dialog.cpp +++ b/filter-accela/ftnoir_filter_accela_dialog.cpp @@ -41,33 +41,36 @@ dialog_accela::dialog_accela() update_trans_dz_display(s.trans_deadzone); update_rot_nl_slider(s.rot_nonlinearity); - { //#define SPLINE_ROT_DEBUG //#define SPLINE_TRANS_DEBUG -#if defined(SPLINE_ROT_DEBUG) || defined(SPLINE_TRANS_DEBUG) - spline rot, trans; - s.make_splines(rot, trans); - QDialog d; - - spline_widget r(&d); - r.set_preview_only(true); - r.setEnabled(false); - r.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - -#if defined(SPLINE_TRANS_DEBUG) -# if defined(SPLINE_ROT_DEBUG) -# error "rot xor trans" -# endif - r.setConfig(&trans); -#else - - r.setConfig(&rot); + +#if defined SPLINE_ROT_DEBUG || defined SPLINE_TRANS_DEBUG + { + spline rot, trans; + s.make_splines(rot, trans); + QDialog dr, dt; + spline_widget r(&dr); + spline_widget t(&dt); + dr.setWindowTitle("Accela rotation gain"); r.set_preview_only(true); r.setEnabled(false); + r.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); r.setConfig(&rot); + dt.setWindowTitle("Accela translation gain"); t.set_preview_only(true); t.setEnabled(false); + r.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); t.setConfig(&trans); + r.setFixedSize(1024, 600); t.setFixedSize(1024, 600); + +#ifdef SPLINE_ROT_DEBUG + dr.show(); #endif - r.setFixedSize(1024, 600); - d.show(); - d.exec(); + +#ifdef SPLINE_TRANS_DEBUG + dt.show(); #endif + + if (dr.isVisible()) + dr.exec(); + if (dt.isVisible()) + dt.exec(); } +#endif } void dialog_accela::doOK() diff --git a/filter-accela/lang/ru_RU.ts b/filter-accela/lang/ru_RU.ts new file mode 100644 index 00000000..c6dc44cf --- /dev/null +++ b/filter-accela/lang/ru_RU.ts @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> + <name>AccelaUICdialog_accela</name> + <message> + <location filename="../ftnoir_accela_filtercontrols.ui" line="+23"/> + <source>Filter settings</source> + <translation>Настройка фильтра</translation> + </message> + <message> + <location line="+13"/> + <source>General</source> + <translation>Основные настройки</translation> + </message> + <message> + <location line="+12"/> + <source>Smoothing</source> + <translation>Сглаживание</translation> + </message> + <message> + <location line="+13"/> + <source>0 ms</source> + <translation>0 мс</translation> + </message> + <message> + <location line="+32"/> + <source>Position filtering (X, Y, Z - translation)</source> + <translation>Фильтрация смещений (X, Y, Z)</translation> + </message> + <message> + <location line="+24"/> + <location line="+111"/> + <source>Sensitivity</source> + <translation>Чувствительность</translation> + </message> + <message> + <location line="-98"/> + <location line="+29"/> + <source>0mm</source> + <translation>0мм</translation> + </message> + <message> + <location line="+7"/> + <location line="+104"/> + <source>Deadzone</source> + <translation>Мертвая зона</translation> + </message> + <message> + <location line="-66"/> + <source>Rotation filtering (Yaw, pitch, and roll)</source> + <translation>Фильтрация поворотов (Рысканье, тангаж, крен)</translation> + </message> + <message> + <location line="+37"/> + <location line="+42"/> + <source>0°</source> + <translation></translation> + </message> + <message> + <location line="+26"/> + <source>Rotation nonlinearity</source> + <translation>Нелинейность поворотов</translation> + </message> + <message> + <location line="+17"/> + <source><html><head/><body><p>x<span style=" vertical-align:super;">2</span></p></body></html></source> + <translation></translation> + </message> + <message> + <location line="+7"/> + <source>Value</source> + <translation>Значение</translation> + </message> + <message> + <location line="+49"/> + <source><html><head/><body><p align="right"><br/><span style=" font-size:10pt;">Accela by </span><a href="https://github.com/sthalik"><span style=" font-size:10pt; text-decoration: underline; color:#0057ae;">Stanisław Halik</span></a><span style=" font-size:10pt;"><br/>Thanks to </span><a href="https://github.com/dbaarda"><span style=" font-size:10pt; text-decoration: underline; color:#0057ae;">Donovan Baarda</span></a></p><p align="right"><span style=" font-size:10pt;">2012-2015</span></p><p align="right"><br/><span style=" font-size:8pt;">Visit </span><a href="https://github.com/opentrack/opentrack/wiki/Accela-in-opentrack-2.3"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">our wiki</span></a><span style=" font-size:8pt;"> for description of the settings.</span></p></body></html></source> + <translation><html><head/><body><p align="right"><br/><span style=" font-size:10pt;">Accela-фильтр создан </span><a href="https://github.com/sthalik"><span style=" font-size:10pt; text-decoration: underline; color:#0057ae;">Stanisław Halik</span></a><span style=" font-size:10pt;">'ом<br/>Спасибо за помощь </span><a href="https://github.com/dbaarda"><span style=" font-size:10pt; text-decoration: underline; color:#0057ae;">Donovan Baarda</span></a></p><p align="right"><span style=" font-size:10pt;">2012-2016</span></p><p align="right"><br/><span style=" font-size:8pt;">Посетите </span><a href="https://github.com/opentrack/opentrack/wiki/Accela-in-opentrack-2.3"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">нашу вики</span></a><span style=" font-size:8pt;"> для понимания процесса настройки.</span></p></body></html></translation> + </message> +</context> +<context> + <name>accelaDll</name> + <message> + <location filename="../ftnoir_filter_accela.h" line="+63"/> + <source>Accela</source> + <translation></translation> + </message> +</context> +</TS> diff --git a/gui/images/english.png b/gui/images/english.png Binary files differnew file mode 100644 index 00000000..187aabf9 --- /dev/null +++ b/gui/images/english.png diff --git a/gui/lang/ru_RU.ts b/gui/lang/ru_RU.ts index fd7842af..9f5e0463 100644 --- a/gui/lang/ru_RU.ts +++ b/gui/lang/ru_RU.ts @@ -50,7 +50,7 @@ <context> <name>MainWindow</name> <message> - <location filename="../main-window.cpp" line="+90"/> + <location filename="../main-window.cpp" line="+97"/> <source>Create new empty config</source> <translation>Создать новый пустой профиль</translation> </message> @@ -77,18 +77,18 @@ </message> <message> <location line="+8"/> - <location line="+555"/> + <location line="+559"/> <source>Show the Octopus</source> <translation>Показать осьминожка</translation> </message> <message> - <location line="-555"/> - <location line="+555"/> + <location line="-559"/> + <location line="+559"/> <source>Hide the Octopus</source> <translation>Спрятать осьминожка</translation> </message> <message> - <location line="-548"/> + <location line="-552"/> <source>Tracker settings</source> <translation>Настройка источника данных</translation> </message> @@ -153,6 +153,19 @@ Exiting now.</source> </message> </context> <context> + <name>OptionsDialog</name> + <message> + <location filename="../options-dialog.cpp" line="+27"/> + <source>Joy button %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>None</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>UI_new_config</name> <message> <location filename="../new_config.ui" line="+17"/> @@ -188,38 +201,38 @@ Exiting now.</source> <message> <location line="+67"/> <location line="+479"/> - <source>TZ</source> - <translation>Смещение по Z</translation> + <source>Z</source> + <translation>Смещение по оси Z</translation> </message> <message> <location line="-463"/> <location line="+409"/> - <source>pitch</source> - <translation>тангаж</translation> + <source>Pitch</source> + <translation>Тангаж</translation> </message> <message> <location line="-356"/> <location line="+315"/> - <source>TY</source> - <translation>Смещение по Y</translation> + <source>Y</source> + <translation>Смещение по оси Y</translation> </message> <message> <location line="-299"/> <location line="+277"/> - <source>TX</source> - <translation>Смещение по X</translation> + <source>X</source> + <translation>Смещение по оси X</translation> </message> <message> <location line="-230"/> <location line="+309"/> - <source>roll</source> - <translation>крен</translation> + <source>Roll</source> + <translation>Крен</translation> </message> <message> <location line="-293"/> <location line="+309"/> - <source>yaw</source> - <translation>рысканье</translation> + <source>Yaw</source> + <translation>Рысканье</translation> </message> <message> <location line="-200"/> @@ -328,9 +341,22 @@ Exiting now.</source> </message> </context> <context> + <name>new_file_dialog</name> + <message> + <location filename="../new_file_dialog.h" line="+42"/> + <source>File exists</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>This file already exists. Pick another name.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>options_dialog</name> <message> - <location filename="../options-dialog.ui" line="+14"/> + <location filename="../options-dialog.ui" line="+20"/> <source>Options</source> <translation>Настройки</translation> </message> @@ -420,12 +446,17 @@ Exiting now.</source> <translation>Центрирование при запуске</translation> </message> <message> - <location line="+7"/> + <location line="+34"/> + <source>Never translate the application interface</source> + <translation></translation> + </message> + <message> + <location line="+20"/> <source>Minimize to tray</source> <translation>Настройка трея</translation> </message> <message> - <location line="+220"/> + <location line="+234"/> <location line="+69"/> <location line="+53"/> <source>°</source> @@ -437,17 +468,17 @@ Exiting now.</source> <translation>Включить</translation> </message> <message> - <location line="-486"/> + <location line="-488"/> <source>Minimize to tray on startup when enabled</source> <translation>Сворачивать в трей при запуске</translation> </message> <message> - <location line="-7"/> + <location line="-13"/> <source>Enable tray</source> <translation>Добавлять opentrack в трей</translation> </message> <message> - <location line="+34"/> + <location line="+40"/> <source>Camera</source> <translation>Камера</translation> </message> @@ -462,20 +493,12 @@ Exiting now.</source> <translation>Использование функции коррекции камеры</translation> </message> <message> - <location line="+7"/> - <source><html><head/><body><p>Specify an angle for off-center camera as a basis for which direction is which, avoiding axis interconnect.</p> - -<p>You can derive it from the center pose as per the checkbox or specify it manually.</p> - -<p>You can specify both, and it can be helpful to specify pitch when moving toward the screen results in translating upward.</p></body></html></source> - <translation><html><head/><body><p>Используйте данную настройку для того, чтобы скорректировать работу камеры в случае, когда она у Вас расположена не четко напротив клипсы.</p> - -<p>Если у вас при зуме осьминожек убегает - то это то, что Вам нужно! Например: при "убегании" вниз добавьте тангажа.</p> - -<p>You can specify both, and it can be helpful to specify pitch when moving toward the screen results in translating upward.</p></body></html></translation> + <location line="+13"/> + <source>Specify an angle to rotate position tracking. It lets the tracker avoid moving on the X and Y axes when zooming, since camera isn't mounted perfectly in front of the model, but above or below.</source> + <translation>Используйте данную настройку для того, чтобы скорректировать работу камеры в случае, когда она у Вас расположена не четко напротив клипсы. Если у вас при зуме осьминожек убегает - то это то, что Вам нужно! Например: при "убегании" вниз добавьте тангажа.</translation> </message> <message> - <location line="+38"/> + <location line="+34"/> <location line="+206"/> <location line="+381"/> <location line="+47"/> @@ -681,12 +704,12 @@ Exiting now.</source> <message> <location line="-158"/> <source>Destination</source> - <translation>Игровая ось:</translation> + <translation>Игровая ось</translation> </message> <message> <location line="+28"/> <source>Source</source> - <translation>Исходная ось:</translation> + <translation>Исходная ось</translation> </message> <message> <location line="+184"/> diff --git a/gui/main-window.cpp b/gui/main-window.cpp index d3e0baa8..d760783b 100644 --- a/gui/main-window.cpp +++ b/gui/main-window.cpp @@ -42,7 +42,14 @@ MainWindow::MainWindow() : menu_action_mappings(&tray_menu) { ui.setupUi(this); - setFixedSize(size()); + + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + adjustSize(); + setFixedSize(size()); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags()); + } + updateButtonState(false, false); if (group::ini_directory().size() == 0) @@ -566,19 +573,21 @@ bool mk_dialog(mem<dylib> lib, ptr<t>& orig) { if (orig && orig->isVisible()) { - orig->show(); - orig->raise(); + QDialog& d = *orig; + d.show(); + d.raise(); return false; } if (lib && lib->Dialog) { t* dialog = reinterpret_cast<t*>(lib->Dialog()); - dialog->setWindowFlags(Qt::Dialog); - // HACK: prevent stderr whining by adding a few pixels - dialog->setFixedSize(dialog->size() + QSize(4, 4)); - dialog->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - dialog->show(); + QDialog& d = *dialog; + d.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + d.adjustSize(); + d.setFixedSize(d.size()); + d.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | d.windowFlags()); + d.show(); orig.reset(dialog); @@ -613,18 +622,20 @@ static bool mk_window(ptr<t>* place, Args&&... params) { if (*place && (*place)->isVisible()) { - (*place)->show(); - (*place)->raise(); + QDialog& d = **place; + d.show(); + d.raise(); return false; } else { *place = make_unique<t>(std::forward<Args>(params)...); - (*place)->setWindowFlags(Qt::Dialog); - // HACK: prevent stderr whining by adding a few pixels - (*place)->setFixedSize((*place)->size() + QSize(4, 4)); - (*place)->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - (*place)->show(); + QDialog& d = **place; + d.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + d.adjustSize(); + d.setFixedSize(d.size()); + d.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | d.windowFlags()); + d.show(); return true; } } diff --git a/gui/main-window.ui b/gui/main-window.ui index 8bf78efd..33ee99d3 100644 --- a/gui/main-window.ui +++ b/gui/main-window.ui @@ -342,7 +342,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>TZ</string> + <string>Z</string> </property> </widget> </item> @@ -358,7 +358,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>pitch</string> + <string>Pitch</string> </property> </widget> </item> @@ -411,7 +411,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>TY</string> + <string>Y</string> </property> </widget> </item> @@ -427,7 +427,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>TX</string> + <string>X</string> </property> </widget> </item> @@ -474,7 +474,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>roll</string> + <string>Roll</string> </property> </widget> </item> @@ -490,7 +490,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>yaw</string> + <string>Yaw</string> </property> </widget> </item> @@ -704,7 +704,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>TX</string> + <string>X</string> </property> </widget> </item> @@ -726,7 +726,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>TY</string> + <string>Y</string> </property> </widget> </item> @@ -767,7 +767,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>pitch</string> + <string>Pitch</string> </property> </widget> </item> @@ -783,7 +783,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>roll</string> + <string>Roll</string> </property> </widget> </item> @@ -799,7 +799,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>yaw</string> + <string>Yaw</string> </property> </widget> </item> @@ -821,7 +821,7 @@ <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>TZ</string> + <string>Z</string> </property> </widget> </item> diff --git a/gui/main.cpp b/gui/main.cpp index de904f94..5cc041fb 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -141,10 +141,13 @@ main(int argc, char** argv) add_win32_path(); #endif - // QLocale::setDefault(QLocale("pl_PL")); // force i18n for testing + // QLocale::setDefault(QLocale("ru_RU")); // force i18n for testing - (void) t.load(QLocale(), "", "", QCoreApplication::applicationDirPath() + "/i18n", ".qm"); - (void) QCoreApplication::installTranslator(&t); + if (!QSettings(OPENTRACK_ORG).value("disable-translation", false).toBool()) + { + (void) t.load(QLocale(), "", "", QCoreApplication::applicationDirPath() + "/i18n", ".qm"); + (void) QCoreApplication::installTranslator(&t); + } do { diff --git a/gui/mapping-window.hpp b/gui/mapping-window.hpp index 7648c994..06d969cc 100644 --- a/gui/mapping-window.hpp +++ b/gui/mapping-window.hpp @@ -4,11 +4,12 @@ #include "ui_mapping-window.h" #include <QWidget> +#include <QDialog> #include <QShowEvent> #include <QCloseEvent> #include <QCheckBox> -class MapWidget final : public QWidget +class MapWidget final : public QDialog { Q_OBJECT public: diff --git a/gui/new_file_dialog.h b/gui/new_file_dialog.h index 6ba3ede3..cd5ca528 100644 --- a/gui/new_file_dialog.h +++ b/gui/new_file_dialog.h @@ -39,7 +39,8 @@ private slots: if (text == "" || text == ".ini" || QFile(options::group::ini_directory() + "/" + text).exists()) { QMessageBox::warning(this, - "File exists", "This file already exists. Pick another name.", + tr("File exists"), + tr("This file already exists. Pick another name."), QMessageBox::Ok, QMessageBox::NoButton); return; } diff --git a/gui/opentrack-res.qrc b/gui/opentrack-res.qrc index 7b7fee54..1a3728cd 100644 --- a/gui/opentrack-res.qrc +++ b/gui/opentrack-res.qrc @@ -7,5 +7,6 @@ <file>images/no-feed.png</file> <file>images/filter-16.png</file> <file>images/tracking-not-started.png</file> + <file>images/english.png</file> </qresource> </RCC> diff --git a/gui/options-dialog.cpp b/gui/options-dialog.cpp index 2f40e39f..bafddb57 100644 --- a/gui/options-dialog.cpp +++ b/gui/options-dialog.cpp @@ -14,7 +14,7 @@ #include <QDialog> #include <QFileDialog> -static QString kopts_to_string(const key_opts& kopts) +QString OptionsDialog::kopts_to_string(const key_opts& kopts) { if (static_cast<QString>(kopts.guid) != "") { @@ -24,13 +24,18 @@ static QString kopts_to_string(const key_opts& kopts) if (mods & Qt::ControlModifier) mm += "Control+"; if (mods & Qt::AltModifier) mm += "Alt+"; if (mods & Qt::ShiftModifier) mm += "Shift+"; - return mm + "Joy button " + QString::number(btn); + return mm + tr("Joy button %1").arg(QString::number(btn)); } if (static_cast<QString>(kopts.keycode) == "") - return "None"; + return tr("None"); return kopts.keycode; } +void OptionsDialog::set_disable_translation_state(bool value) +{ + QSettings(OPENTRACK_ORG).setValue("disable-translation", value); +} + OptionsDialog::OptionsDialog(std::function<void(bool)> pause_keybindings) : pause_keybindings(pause_keybindings) { @@ -84,6 +89,8 @@ OptionsDialog::OptionsDialog(std::function<void(bool)> pause_keybindings) : tie_setting(main.tracklogging_enabled, ui.tracklogging_enabled); + ui.disable_translation->setChecked(QSettings(OPENTRACK_ORG).value("disable-translation", false).toBool()); + struct tmp { key_opts& opt; @@ -130,6 +137,7 @@ void OptionsDialog::bind_key(key_opts& kopts, QLabel* label) d.setLayout(&l); d.setFixedSize(QSize(500, 300)); d.setWindowFlags(Qt::Dialog); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags()); d.setWindowModality(Qt::ApplicationModal); connect(&k, &KeyboardListener::key_pressed, @@ -165,6 +173,7 @@ void OptionsDialog::doOK() { main.b->save(); ui.game_detector->save(); + set_disable_translation_state(ui.disable_translation->isChecked()); close(); emit closing(); } diff --git a/gui/options-dialog.hpp b/gui/options-dialog.hpp index b0e285e8..8c62b278 100644 --- a/gui/options-dialog.hpp +++ b/gui/options-dialog.hpp @@ -3,10 +3,11 @@ #include "ui_options-dialog.h" #include "logic/shortcuts.h" #include <QObject> +#include <QDialog> #include <QWidget> #include <functional> -class OptionsDialog : public QWidget +class OptionsDialog : public QDialog { Q_OBJECT signals: @@ -18,8 +19,10 @@ private: std::function<void(bool)> pause_keybindings; Ui::options_dialog ui; void closeEvent(QCloseEvent *) override { doCancel(); } + static QString kopts_to_string(const key_opts& opts); private slots: void doOK(); void doCancel(); void bind_key(key_opts &kopts, QLabel* label); + void set_disable_translation_state(bool value); }; diff --git a/gui/options-dialog.ui b/gui/options-dialog.ui index f6b8e3d6..2168fa8d 100644 --- a/gui/options-dialog.ui +++ b/gui/options-dialog.ui @@ -6,10 +6,16 @@ <rect> <x>0</x> <y>0</y> - <width>459</width> - <height>615</height> + <width>450</width> + <height>599</height> </rect> </property> + <property name="minimumSize"> + <size> + <width>450</width> + <height>0</height> + </size> + </property> <property name="windowTitle"> <string>Options</string> </property> @@ -27,7 +33,7 @@ <item> <widget class="QTabWidget" name="tabWidget"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -337,6 +343,53 @@ </widget> </item> <item> + <widget class="QFrame" name="frame_3"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QCheckBox" name="disable_translation"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Never translate the application interface</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_29"> + <property name="text"> + <string/> + </property> + <property name="pixmap"> + <pixmap resource="opentrack-res.qrc">:/images/english.png</pixmap> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QGroupBox" name="groupBox_11"> <property name="title"> <string>Minimize to tray</string> @@ -353,6 +406,12 @@ </property> <item> <widget class="QCheckBox" name="trayp"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> <string>Enable tray</string> </property> @@ -360,6 +419,12 @@ </item> <item> <widget class="QCheckBox" name="tray_start"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> <string>Minimize to tray on startup when enabled</string> </property> @@ -378,7 +443,7 @@ </property> <property name="sizeHint" stdset="0"> <size> - <width>0</width> + <width>10</width> <height>0</height> </size> </property> @@ -406,12 +471,14 @@ </item> <item row="5" column="0"> <widget class="QLabel" name="label_17"> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> <property name="text"> - <string><html><head/><body><p>Specify an angle for off-center camera as a basis for which direction is which, avoiding axis interconnect.</p> - -<p>You can derive it from the center pose as per the checkbox or specify it manually.</p> - -<p>You can specify both, and it can be helpful to specify pitch when moving toward the screen results in translating upward.</p></body></html></string> + <string>Specify an angle to rotate position tracking. It lets the tracker avoid moving on the X and Y axes when zooming, since camera isn't mounted perfectly in front of the model, but above or below.</string> </property> <property name="alignment"> <set>Qt::AlignJustify|Qt::AlignVCenter</set> @@ -810,7 +877,7 @@ <item> <widget class="QGroupBox" name="groupBox_"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -1514,7 +1581,6 @@ <tabstop>bind_toggle_tracking</tabstop> <tabstop>bind_restart_tracking</tabstop> <tabstop>bind_tcomp_off_held</tabstop> - <tabstop>center_at_startup</tabstop> <tabstop>trayp</tabstop> <tabstop>tray_start</tabstop> <tabstop>use_center_as_translation_camera_offset</tabstop> @@ -1549,7 +1615,9 @@ <tabstop>tcomp_src_pitch_disable</tabstop> <tabstop>tcomp_src_roll_disable</tabstop> </tabstops> - <resources/> + <resources> + <include location="opentrack-res.qrc"/> + </resources> <connections/> <slots> <slot>startEngineClicked()</slot> diff --git a/logic/tracker.cpp b/logic/tracker.cpp index 8757e98e..a5892dd0 100644 --- a/logic/tracker.cpp +++ b/logic/tracker.cpp @@ -26,6 +26,7 @@ #endif using namespace euler; +using namespace gui_tracker_impl; constexpr double Tracker::r2d; constexpr double Tracker::d2r; @@ -34,7 +35,8 @@ Tracker::Tracker(Mappings& m, SelectedLibraries& libs, TrackLogger& logger) : m(m), libs(libs), logger(logger), - backlog_time(0) + backlog_time(0), + tracking_started(false) { set(f_center, s.center_at_startup); } @@ -169,28 +171,23 @@ void Tracker::logic() scaled_rotation.camera = get_camera_offset_matrix(c_div); real_rotation.camera = get_camera_offset_matrix(1); - scaled_rotation.rotation = scaled_rotation.camera * scaled_rotation.rotation; - real_rotation.rotation = real_rotation.camera * real_rotation.rotation; - bool nanp = is_nan(value) || is_nan(scaled_rotation.rotation) || is_nan(real_rotation.rotation); if (!nanp) { - bool can_center = false; - - if (get(f_center)) + if (!tracking_started) { using std::fabs; for (int i = 0; i < 6; i++) if (fabs(newpose(i)) != 0) { - can_center = true; + tracking_started = true; break; } } - if (can_center) + if (get(f_center) && tracking_started) { set(f_center, false); @@ -260,10 +257,7 @@ void Tracker::logic() const euler_t rot = r2d * c_mult * rmat_to_euler(rotation); euler_t pos = euler_t(&value[TX]) - t_center; - if (s.use_camera_offset_from_centering) - t_compensate((real_rotation.camera * real_rotation.rot_center).t(), pos, pos, false, false, false); - else - t_compensate(real_rotation.camera.t(), pos, pos, false, false, false); + t_compensate(real_rotation.camera.t(), pos, pos, false, false, false); for (int i = 0; i < 3; i++) { @@ -452,25 +446,34 @@ void Tracker::get_raw_and_mapped_poses(double* mapped, double* raw) const void bits::set(bits::flags flag_, bool val_) { - unsigned b_(b); const unsigned flag = unsigned(flag_); - const unsigned val = unsigned(!!val_); - while (!b.compare_exchange_weak(b_, + const unsigned val = unsigned(val_); + + for (;;) + { + unsigned b_(b); + if (b.compare_exchange_weak(b_, unsigned((b_ & ~flag) | (flag * val)), std::memory_order_seq_cst, std::memory_order_seq_cst)) - { /* empty */ } + break; + } } void bits::negate(bits::flags flag_) { - unsigned b_(b); const unsigned flag = unsigned(flag_); - while (!b.compare_exchange_weak(b_, - (b_ & ~flag) | (flag & ~b_), + + for (;;) + { + unsigned b_(b); + + if (b.compare_exchange_weak(b_, + b_ ^ flag, std::memory_order_seq_cst, std::memory_order_seq_cst)) - { /* empty */ } + break; + } } bool bits::get(bits::flags flag) diff --git a/logic/tracker.h b/logic/tracker.h index b9012910..4a5bdf50 100644 --- a/logic/tracker.h +++ b/logic/tracker.h @@ -29,6 +29,8 @@ #include "export.hpp" +namespace gui_tracker_impl { + using Pose = Mat<double, 6, 1>; struct bits @@ -86,6 +88,8 @@ private: long backlog_time; + bool tracking_started; + double map(double pos, Map& axis); void logic(); void t_compensate(const rmat& rmat, const euler_t& ypr, euler_t& output, @@ -115,3 +119,7 @@ public: void zero() { negate(f_zero); } void toggle_enabled() { negate(f_enabled); } }; + +} // ns impl + +using gui_tracker_impl::Tracker; diff --git a/migration/20160917_00-accela.cpp b/migration/20160917_00-accela.cpp index 60104c24..aefe6dda 100644 --- a/migration/20160917_00-accela.cpp +++ b/migration/20160917_00-accela.cpp @@ -94,7 +94,7 @@ struct move_accela_to_sliders : migration tmp = val; } - value<slider_value> tmp(new_b, slider_name, slider_value(-1e6, s.rot_nonlinearity->min(), s.rot_nonlinearity->max())); + value<slider_value> tmp(new_b, slider_name, slider_value(-1e6, s.rot_nonlinearity().min(), s.rot_nonlinearity().max())); tmp = old_b->contains(slider_name) ? old_b->get<slider_value>(slider_name) : slider_value(1.1, 1, 1.75); diff --git a/options/bundle.cpp b/options/bundle.cpp index 96291476..74e08eb9 100644 --- a/options/bundle.cpp +++ b/options/bundle.cpp @@ -199,12 +199,12 @@ OPENTRACK_OPTIONS_EXPORT bundler& singleton() } // end options::detail -OPENTRACK_OPTIONS_EXPORT std::shared_ptr<bundle_type> make_bundle(const QString& name) +OPENTRACK_OPTIONS_EXPORT std::shared_ptr<bundle_> make_bundle(const QString& name) { if (name.size()) return detail::singleton().make_bundle(name); else - return std::make_shared<bundle_type>(QStringLiteral("")); + return std::make_shared<bundle_>(QStringLiteral("")); } QMutex* options::detail::bundle::get_mtx() const { return mtx; } diff --git a/options/bundle.hpp b/options/bundle.hpp index 2d7fa7f4..5bbe6f60 100644 --- a/options/bundle.hpp +++ b/options/bundle.hpp @@ -103,9 +103,9 @@ public: OPENTRACK_OPTIONS_EXPORT bundler& singleton(); } -using bundle_type = detail::bundle; -using bundle = std::shared_ptr<bundle_type>; +using bundle_ = detail::bundle; +using bundle = std::shared_ptr<bundle_>; -OPENTRACK_OPTIONS_EXPORT std::shared_ptr<bundle_type> make_bundle(const QString& name); +OPENTRACK_OPTIONS_EXPORT std::shared_ptr<bundle_> make_bundle(const QString& name); } diff --git a/options/options.hpp b/options/options.hpp index ebc480f3..c1eed97c 100644 --- a/options/options.hpp +++ b/options/options.hpp @@ -6,6 +6,10 @@ */ #pragma once +#include "compat/ndebug-guard.hpp" + +#include "compat/util.hpp" + #include "defs.hpp" #include "bundle.hpp" #include "group.hpp" diff --git a/options/tie.hpp b/options/tie.hpp index accd958e..942c6098 100644 --- a/options/tie.hpp +++ b/options/tie.hpp @@ -31,40 +31,23 @@ inline typename std::enable_if<std::is_enum<t>::value>::type tie_setting(value<t>& v, QComboBox* cb) { - cb->setCurrentIndex(cb->findData((unsigned)static_cast<t>(v))); + cb->setCurrentIndex(cb->findData(int(static_cast<t>(v)))); v = static_cast<t>(cb->currentData().toInt()); - std::vector<int> enum_cases(unsigned(cb->count())); - - for (int i = 0; i < cb->count(); i++) - enum_cases[i] = cb->itemData(i).toInt(); - base_value::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), &v, - [&, enum_cases](int idx) { - if (idx < 0 || idx >= (int)enum_cases.size()) - v = static_cast<t>(-1); - else - v = static_cast<t>(enum_cases[idx]); + [&v, cb](int idx) + { + run_in_thread_sync(cb, + [&]() { + v = static_cast<t>(cb->itemData(idx).toInt()); + }); }, v.DIRECT_CONNTYPE); - base_value::connect(&v, - static_cast<void (base_value::*)(int) const>(&base_value::valueChanged), - cb, - [&, enum_cases](int val) { - for (unsigned i = 0; i < enum_cases.size(); i++) - { - if (val == enum_cases[i]) - { - cb->setCurrentIndex(i); - return; - } - } - cb->setCurrentIndex(-1); - }, - // don't change or else hatire crashes -sh 20160917 - Qt::QueuedConnection); + base_value::connect(&v, static_cast<void (base_value::*)(int) const>(&base_value::valueChanged), + cb, [cb](int x) { cb->setCurrentIndex(cb->findData(x)); }, + v.SAFE_CONNTYPE); } template<> @@ -144,27 +127,36 @@ inline void tie_setting(value<int>& v, QTabWidget* t) template<> inline void tie_setting(value<slider_value>& v, QSlider* w) { - // we can't get these at runtime since signals cross threads - const int q_min = w->minimum(); - const int q_max = w->maximum(); + { + const int q_min = w->minimum(); + const int q_max = w->maximum(); - w->setValue(v->to_slider_pos(q_min, q_max)); - v = v->update_from_slider(w->value(), q_min, q_max); + w->setValue(v().to_slider_pos(q_min, q_max)); + v = v().update_from_slider(w->value(), q_min, q_max); + } base_value::connect(w, &QSlider::valueChanged, &v, - [=, &v](int pos) { - v = v->update_from_slider(pos, q_min, q_max); - w->setValue(v->to_slider_pos(q_min, q_max)); + [=, &v](int pos) + { + run_in_thread_sync(w, [&]() + { + const int q_min = w->minimum(); + const int q_max = w->maximum(); + v = v().update_from_slider(pos, q_min, q_max); + w->setValue(v().to_slider_pos(q_min, q_max)); + }); }, v.DIRECT_CONNTYPE); base_value::connect(&v, static_cast<void(base_value::*)(double) const>(&base_value::valueChanged), w, [=, &v](double) { - w->setValue(v->to_slider_pos(q_min, q_max)); - v = v->update_from_slider(w->value(), q_min, q_max); + const int q_min = w->minimum(); + const int q_max = w->maximum(); + w->setValue(v().to_slider_pos(q_min, q_max)); + v = v().update_from_slider(w->value(), q_min, q_max); }, v.SAFE_CONNTYPE); } diff --git a/options/value.hpp b/options/value.hpp index 3c45bf15..01746d67 100644 --- a/options/value.hpp +++ b/options/value.hpp @@ -166,14 +166,6 @@ public: { } - t get() const - { - t val = b->contains(self_name) - ? static_cast<t>(b->get<element_type>(self_name)) - : def; - return detail::value_get_traits<t>::get(val, def); - } - t default_value() const { return def; @@ -191,13 +183,20 @@ public: emit valueChanged(static_cast<detail::value_type_t<t>>(get())); } - element_type const* operator->() const + element_type operator()() const { - static thread_local element_type last; - last = get(); - return &last; + return get(); } + private: + t get() const + { + t val = b->contains(self_name) + ? static_cast<t>(b->get<element_type>(self_name)) + : def; + return detail::value_get_traits<t>::get(val, def); + } + const t def; }; diff --git a/pose-widget/glwidget.cpp b/pose-widget/glwidget.cpp index 01a6b74b..023e1926 100644 --- a/pose-widget/glwidget.cpp +++ b/pose-widget/glwidget.cpp @@ -15,7 +15,7 @@ #include <QDebug> using namespace euler; -using namespace impl; +using namespace pose_widget_impl; GLWidget::GLWidget(QWidget *parent) : QWidget(parent) { diff --git a/pose-widget/glwidget.h b/pose-widget/glwidget.h index f98b4628..ae06e5c5 100644 --- a/pose-widget/glwidget.h +++ b/pose-widget/glwidget.h @@ -19,7 +19,7 @@ # define POSE_WIDGET_EXPORT Q_DECL_IMPORT #endif -namespace impl { +namespace pose_widget_impl { using num = float; using vec3 = Mat<num, 3, 1>; @@ -52,4 +52,4 @@ private: } -using impl::GLWidget; +using pose_widget_impl::GLWidget; diff --git a/proto-ft/lang/ru_RU.ts b/proto-ft/lang/ru_RU.ts new file mode 100644 index 00000000..e5984c77 --- /dev/null +++ b/proto-ft/lang/ru_RU.ts @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> + <name>FTControls</name> + <message> + <location filename="../ftnoir_protocol_ft_dialog.cpp" line="+53"/> + <source>Select the desired NPClient DLL</source> + <translation>Укажите путь до файла NPClient DLL </translation> + </message> + <message> + <location line="+0"/> + <source>Dll file (*.dll);;All Files (*)</source> + <translation></translation> + </message> +</context> +<context> + <name>UICFTControls</name> + <message> + <location filename="../ftnoir_ftcontrols.ui" line="+32"/> + <source>freetrack protocol settings</source> + <translation>Настройки протокола Freetrack</translation> + </message> + <message> + <location line="+22"/> + <source>Select interface</source> + <translation>Выберите интерфейс</translation> + </message> + <message> + <location line="+18"/> + <source>Disable one of the protocols if game is confused by presence of both at the same time.</source> + <translation>Отключите один из протоколов в случае, если при включении обоих интерфейсов игра не корректно определяет их.</translation> + </message> + <message> + <location line="+19"/> + <source>TIRViews</source> + <translation></translation> + </message> + <message> + <location line="+21"/> + <source>Memory hacks</source> + <translation>Взлом памяти</translation> + </message> + <message> + <location line="+7"/> + <source>Only for very old and buggy old games such as CFS3.</source> + <translation>Только для очень старых игр, например таких как CFS3 </translation> + </message> + <message> + <location line="+25"/> + <source>Repair NPClient location</source> + <translation>Решение проблем с расположением NPClient'а </translation> + </message> + <message> + <location line="+15"/> + <source>Locate DLL</source> + <translation>Укажите DLL</translation> + </message> + <message> + <location line="+13"/> + <source>Replace the registry entry if you want to use other software with the NPClient protocol and it doesn't work automatically. + +Starting tracking will again overwrite the DLL locations.</source> + <translation>Заменить запись в реестре расположения библиотеки NPClient.dll если протокол не работает в автоматическом режиме. + +Запуск отслеживания в opentrack приведет к перезаписи расположения DLL-файла.</translation> + </message> + <message> + <location line="+28"/> + <source>Disable tracking for games on exit</source> + <translation>Отключить работы интерфейсов после закрытия Opentrack </translation> + </message> + <message> + <location line="+15"/> + <source>Disable on program exit</source> + <translation>Отключить по закрытию</translation> + </message> + <message> + <location line="+7"/> + <source>Use this for games that disable mouselook when opentrack isn't running, and you're not running opentrack that much. To use opentrack with this option you need to start it before the games to have it work at all.</source> + <translation>По умолчанию протокол opentrack'а работает постоянно, благодаря чему возможно запускать opentrack на любой стадии загрузки игр. При активации данного пункта необходимо запускать opentrack перед запуском игры.</translation> + </message> +</context> +<context> + <name>freetrackDll</name> + <message> + <location filename="../ftnoir_protocol_ft.h" line="+96"/> + <source>freetrack 2.0 Enhanced</source> + <translation></translation> + </message> +</context> +</TS> diff --git a/settings/facetracknoir supported games.csv b/settings/facetracknoir supported games.csv index a4b68554..1be3b350 100644 --- a/settings/facetracknoir supported games.csv +++ b/settings/facetracknoir supported games.csv @@ -619,3 +619,8 @@ No;Game Name;Game protocol;Supported since;Verified;By;INTERNATIONAL_ID;FTN_ID 753;Synergy Integration;FreeTrack20;V160;;;20140;02F185EAE564E30DA1B600 754;Bus Simulator 16;FreeTrack20;V170;;;4775;02F2E24441D3178B43DB00 755;Subnautica;FreeTrack20;V170;;;4850;02F3F92B360B97FA550A00 +756;DGA;FreeTrack20;V160;;;20145;02F4106F2B3FE0E58AE000 +757;ED Science Lab;FreeTrack20;V160;;;20160;02F518B03B53A0A9A32600 +758;F1 2016;FreeTrack20;V160;;;8111;02F62FF3D6F90370C8A300 +759;SW Plugin;FreeTrack20;V160;;;20155;02F7E45153116EF485A500 +760;Vector 36;FreeTrack20;V160;;;5325;02F8C318595317B9FF3B00 diff --git a/spline-widget/spline-widget.cpp b/spline-widget/spline-widget.cpp index 45a90c6a..f10995d4 100644 --- a/spline-widget/spline-widget.cpp +++ b/spline-widget/spline-widget.cpp @@ -101,7 +101,8 @@ void spline_widget::drawBackground() _background = QPixmap(width(), height()); QPainter painter(&_background); - painter.fillRect(rect(), QColor::fromRgb(204, 204, 204)); + + painter.fillRect(rect(), QWidget::palette().color(QWidget::backgroundRole())); QColor bg_color(112, 154, 209); if (!isEnabled() && !_preview_only) @@ -308,11 +309,11 @@ void spline_widget::drawLine(QPainter& painter, const QPoint& start, const QPoin void spline_widget::mousePressEvent(QMouseEvent *e) { - if (!_config || !isEnabled()) - return; - - if (!is_in_bounds(e->pos())) + if (!_config || !isEnabled() || !is_in_bounds(e->pos())) + { + clearFocus(); return; + } const int point_pixel_closeness_limit = get_closeness_limit(); @@ -384,6 +385,13 @@ void spline_widget::mousePressEvent(QMouseEvent *e) void spline_widget::mouseMoveEvent(QMouseEvent *e) { + if (_preview_only && _config) + { + show_tooltip(e->pos()); + clearFocus(); + return; + } + if (!_config || !isEnabled() || !isActiveWindow() || (moving_control_point_idx != -1 && !hasFocus())) { clearFocus(); @@ -488,11 +496,15 @@ int spline_widget::get_closeness_limit() return std::max(iround(snap_x * c.x()), 1); } -void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_, const QString& prefix) +void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_) { const QPointF value = QPoint(0, 0) == value_ ? pixel_coord_to_point(pos) : value_; double x = value.x(), y = value.y(); + + if (_preview_only) + y = _config->get_value_no_save(x); + const int x_ = iround(x), y_ = iround(y); using std::fabs; @@ -508,7 +520,7 @@ void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_, const const QPoint pix(int(pos.x()) + add_x, int(pos.y()) + add_y); QToolTip::showText(mapToGlobal(pix), - QStringLiteral("value: %1%2x%3").arg(prefix).arg(x).arg(y), + QStringLiteral("value: %1x%2").arg(x).arg(y), this, rect(), 0); diff --git a/spline-widget/spline-widget.hpp b/spline-widget/spline-widget.hpp index d52e249c..921f44a2 100644 --- a/spline-widget/spline-widget.hpp +++ b/spline-widget/spline-widget.hpp @@ -32,6 +32,8 @@ class OPENTRACK_SPLINE_EXPORT spline_widget final : public QWidget Q_OBJECT Q_PROPERTY(QColor colorBezier READ colorBezier WRITE setColorBezier) Q_PROPERTY(bool is_preview_only READ is_preview_only WRITE set_preview_only) + Q_PROPERTY(int x_step READ x_step WRITE set_x_step) + Q_PROPERTY(int y_step READ y_step WRITE set_y_step) using points_t = spline::points_t; public: @@ -47,6 +49,12 @@ public: void force_redraw(); void set_preview_only(bool val); bool is_preview_only() const; + + double x_step() { return _x_step; } + double y_step() { return _y_step; } + void set_x_step(double val) { _x_step = val; } + void set_y_step(double val) { _y_step = val; } + void set_snap(double x, double y) { snap_x = x; snap_y = y; } void get_snap(double& x, double& y) const { x = snap_x; y = snap_y; } protected slots: @@ -57,7 +65,7 @@ protected slots: void reload_spline(); private: int get_closeness_limit(); - void show_tooltip(const QPoint& pos, const QPointF& value = QPointF(0, 0), const QString& prefix = QStringLiteral("")); + void show_tooltip(const QPoint& pos, const QPointF& value = QPointF(0, 0)); bool is_in_bounds(const QPoint& pos) const; void drawBackground(); @@ -89,6 +97,7 @@ private: QMetaObject::Connection connection; double snap_x, snap_y; + double _x_step, _y_step; int moving_control_point_idx; bool _draw_function, _preview_only; diff --git a/spline-widget/spline.cpp b/spline-widget/spline.cpp index 4742bdc8..57817d40 100644 --- a/spline-widget/spline.cpp +++ b/spline-widget/spline.cpp @@ -29,7 +29,8 @@ spline::spline(qreal maxx, qreal maxy, const QString& name) : _mutex(QMutex::Recursive), max_x(maxx), max_y(maxy), - activep(false) + activep(false), + validp(false) { set_bundle(options::make_bundle(name)); } @@ -62,7 +63,7 @@ void spline::removeAllPoints() { QMutexLocker l(&_mutex); s->points = points_t(); - update_interp_data(); + validp = false; } void spline::setMaxInput(qreal max_input) @@ -123,6 +124,12 @@ bool spline::getLastPoint(QPointF& point) float spline::getValueInternal(int x) { + if (!validp) + { + update_interp_data(); + validp = true; + } + float sign = x < 0 ? -1 : 1; x = std::abs(x); float ret; @@ -247,7 +254,7 @@ void spline::removePoint(int i) { points.erase(points.begin() + i); s->points = points; - update_interp_data(); + validp = false; } } @@ -259,7 +266,12 @@ void spline::addPoint(QPointF pt) points.push_back(pt); std::stable_sort(points.begin(), points.end(), sort_fn); s->points = points; - update_interp_data(); + validp = false; +} + +void spline::addPoint(double x, double y) +{ + addPoint(QPointF(x, y)); } void spline::movePoint(int idx, QPointF pt) @@ -274,7 +286,7 @@ void spline::movePoint(int idx, QPointF pt) // we don't allow points to be reordered, but sort due to possible caller logic error std::stable_sort(points.begin(), points.end(), sort_fn); s->points = points; - update_interp_data(); + validp = false; } } @@ -287,7 +299,7 @@ QList<QPointF> spline::getPoints() const int spline::get_point_count() const { QMutexLocker foo(&_mutex); - return s->points.get().size(); + return s->points().size(); } void spline::reload() @@ -322,7 +334,7 @@ void spline::set_bundle(bundle b) if (b) { - connection = QObject::connect(b.get(), &bundle_type::changed, + connection = QObject::connect(b.get(), &bundle_::changed, s.get(), [&]() { // we're holding the mutex to allow signal disconnection in spline dtor // before this slot gets called for the next time @@ -389,8 +401,7 @@ void spline::recompute() last_input_value = QPointF(0, 0); activep = false; - - update_interp_data(); + validp = false; } // the return value is only safe to use with no spline::set_bundle calls diff --git a/spline-widget/spline.hpp b/spline-widget/spline.hpp index b79263ec..adabd33c 100644 --- a/spline-widget/spline.hpp +++ b/spline-widget/spline.hpp @@ -56,12 +56,13 @@ class OPENTRACK_SPLINE_EXPORT spline final std::vector<float> data; using interp_data_t = decltype(data); - static constexpr int value_count = 2048; + static constexpr int value_count = 4096; MyMutex _mutex; QPointF last_input_value; qreal max_x, max_y; volatile bool activep; + bool validp; public: using settings = spline_detail::settings; @@ -87,6 +88,7 @@ public: void removeAllPoints(); void addPoint(QPointF pt); + void addPoint(double x, double y); void movePoint(int idx, QPointF pt); QList<QPointF> getPoints() const; void setMaxInput(qreal MaxInput); @@ -99,6 +101,6 @@ public: mem<settings> get_settings(); mem<const settings> get_settings() const; - using points_t = decltype(s->points.get()); + using points_t = decltype(s->points()); int get_point_count() const; }; diff --git a/tracker-aruco/CMakeLists.txt b/tracker-aruco/CMakeLists.txt index c0b50bee..d68b8f59 100644 --- a/tracker-aruco/CMakeLists.txt +++ b/tracker-aruco/CMakeLists.txt @@ -1,7 +1,9 @@ -find_package(OpenCV 3.0 QUIET COMPONENTS ${opencv-modules}) -set(SDK_ARUCO_LIBPATH "" CACHE FILEPATH "Aruco paper marker tracker static library path") -if(SDK_ARUCO_LIBPATH AND OpenCV_FOUND) - opentrack_boilerplate(opentrack-tracker-aruco) - target_link_libraries(opentrack-tracker-aruco opentrack-cv ${SDK_ARUCO_LIBPATH} ${OpenCV_LIBS}) - target_include_directories(opentrack-tracker-aruco SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) +find_package(OpenCV 3.0 QUIET) +if(OpenCV_FOUND) + set(SDK_ARUCO_LIBPATH "" CACHE FILEPATH "Aruco paper marker tracker static library path") + if(SDK_ARUCO_LIBPATH) + opentrack_boilerplate(opentrack-tracker-aruco) + target_link_libraries(opentrack-tracker-aruco opentrack-cv ${SDK_ARUCO_LIBPATH} ${OpenCV_LIBS}) + target_include_directories(opentrack-tracker-aruco SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) + endif() endif() diff --git a/tracker-aruco/aruco-trackercontrols.ui b/tracker-aruco/aruco-trackercontrols.ui index 1479b2e7..7d7be812 100644 --- a/tracker-aruco/aruco-trackercontrols.ui +++ b/tracker-aruco/aruco-trackercontrols.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>dialog_aruco</class> - <widget class="QWidget" name="dialog_aruco"> + <class>Form</class> + <widget class="QWidget" name="Form"> <property name="windowModality"> <enum>Qt::NonModal</enum> </property> @@ -103,7 +103,7 @@ </property> <item> <property name="text"> - <string notr="true">Default</string> + <string>Default</string> </property> </item> <item> @@ -145,10 +145,10 @@ <locale language="English" country="UnitedStates"/> </property> <property name="minimum"> - <number>10</number> + <number>35</number> </property> <property name="maximum"> - <number>180</number> + <number>90</number> </property> </widget> </item> @@ -172,11 +172,6 @@ </item> <item> <property name="text"> - <string>320x200</string> - </property> - </item> - <item> - <property name="text"> <string>Default (not recommended!)</string> </property> </item> diff --git a/tracker-aruco/ftnoir_tracker_aruco.cpp b/tracker-aruco/ftnoir_tracker_aruco.cpp index 8edb8cf5..02b1f830 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.cpp +++ b/tracker-aruco/ftnoir_tracker_aruco.cpp @@ -5,13 +5,13 @@ * copyright notice and this permission notice appear in all copies. */ +#include "cv/video-widget.hpp" #include "ftnoir_tracker_aruco.h" -#include "api/plugin-api.hpp" -#include "compat/camera-names.hpp" #include "cv/video-property-page.hpp" +#include "compat/camera-names.hpp" +#include "compat/sleep.hpp" #include <opencv2/core.hpp> -#include <opencv2/videoio.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/calib3d.hpp> @@ -30,64 +30,56 @@ struct resolution_tuple int height; }; -static resolution_tuple resolution_choices[] = +static constexpr const resolution_tuple resolution_choices[] = { { 640, 480 }, { 320, 240 }, - { 320, 200 }, { 0, 0 } }; -tracker_aruco::tracker_aruco() : - stop(false), - layout(nullptr), - videoWidget(nullptr), +constexpr const double aruco_tracker::RC; +constexpr const float aruco_tracker::size_min; +constexpr const float aruco_tracker::size_max; + +aruco_tracker::aruco_tracker() : + fps(0), obj_points(4), - intrinsics(decltype(intrinsics)::eye()), - dist_coeffs(decltype(dist_coeffs)::zeros()), - rmat(decltype(rmat)::eye()), + intrinsics(cv::Matx33d::eye()), + rmat(cv::Matx33d::eye()), roi_points(4), last_roi(65535, 65535, 0, 0), - freq(cv::getTickFrequency()), // XXX change to timer.hpp - cur_fps(0) + stop(false) { // param 2 ignored for Otsu thresholding. it's required to use our fork of Aruco. - detector.setThresholdParams(5, -1); + detector.setThresholdParams(7, -1); detector.setDesiredSpeed(3); detector._thresMethod = aruco::MarkerDetector::FIXED_THRES; } -tracker_aruco::~tracker_aruco() +aruco_tracker::~aruco_tracker() { stop = true; wait(); - if (videoWidget) - delete videoWidget; - if(layout) - delete layout; + // fast start/stop causes breakage + portable::sleep(1000); camera.release(); } -void tracker_aruco::start_tracker(QFrame* videoframe) +void aruco_tracker::start_tracker(QFrame* videoframe) { videoframe->show(); - videoWidget = new cv_video_widget(videoframe); - QHBoxLayout* layout_ = new QHBoxLayout(); - layout_->setContentsMargins(0, 0, 0, 0); - layout_->addWidget(videoWidget); - if (videoframe->layout()) - delete videoframe->layout(); - videoframe->setLayout(layout_); + videoWidget = qptr<cv_video_widget>(videoframe); + layout = qptr<QHBoxLayout>(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(videoWidget.data()); + videoframe->setLayout(layout.data()); videoWidget->show(); start(); for (int i = 0; i < 6; i++) pose[i] = 0; - layout = layout_; } -#define HT_PI M_PI - -void tracker_aruco::getRT(cv::Matx33d& r_, cv::Vec3d& t_) +void aruco_tracker::getRT(cv::Matx33d& r_, cv::Vec3d& t_) { QMutexLocker l(&mtx); @@ -95,17 +87,14 @@ void tracker_aruco::getRT(cv::Matx33d& r_, cv::Vec3d& t_) t_ = t; } -bool tracker_aruco::detect_with_roi() +bool aruco_tracker::detect_with_roi() { if (last_roi.width > 1 && last_roi.height > 1) { - float min = std::min(1.f, std::max(.01f, size_min * grayscale.cols / last_roi.width)); - float max = std::max(.01f, std::min(1.f, size_max * grayscale.cols / last_roi.width)); - detector.setMinMaxSize(min, max); + detector.setMinMaxSize(clamp(size_min * grayscale.cols / last_roi.width, .01f, 1.f), + clamp(size_max * grayscale.cols / last_roi.width, .01f, 1.f)); - cv::Mat grayscale_ = grayscale(last_roi); - - detector.detect(grayscale_, markers, cv::Mat(), cv::Mat(), -1, false); + detector.detect(grayscale(last_roi), markers, cv::Mat(), cv::Mat(), -1, false); if (markers.size() == 1 && markers[0].size() == 4) { @@ -124,14 +113,14 @@ bool tracker_aruco::detect_with_roi() return false; } -bool tracker_aruco::detect_without_roi() +bool aruco_tracker::detect_without_roi() { detector.setMinMaxSize(size_min, size_max); detector.detect(grayscale, markers, cv::Mat(), cv::Mat(), -1, false); return markers.size() == 1 && markers[0].size() == 4; } -bool tracker_aruco::open_camera() +bool aruco_tracker::open_camera() { int rint = s.resolution; if (rint < 0 || rint >= (int)(sizeof(resolution_choices) / sizeof(resolution_tuple))) @@ -180,7 +169,7 @@ bool tracker_aruco::open_camera() return true; } -void tracker_aruco::set_intrinsics() +void aruco_tracker::set_intrinsics() { const int w = grayscale.cols, h = grayscale.rows; const double diag_fov = static_cast<int>(s.fov) * M_PI / 180.; @@ -195,16 +184,20 @@ void tracker_aruco::set_intrinsics() intrinsics(1, 2) = grayscale.rows/2; } -void tracker_aruco::update_fps(double alpha) +void aruco_tracker::update_fps() { - std::uint64_t time = std::uint64_t(cv::getTickCount()); - const double dt = std::max(0., (time - last_time) / freq); - last_time = time; + const double dt = fps_timer.elapsed_seconds(); + fps_timer.start(); + const double alpha = dt/(dt + RC); - cur_fps = cur_fps * alpha + (1-alpha) * (fabs(dt) < 1e-6 ? 0 : 1./dt); + if (dt > 1e-3) + { + fps *= 1 - alpha; + fps += alpha * (1./dt + .8); + } } -void tracker_aruco::draw_ar(bool ok) +void aruco_tracker::draw_ar(bool ok) { if (ok) { @@ -213,13 +206,13 @@ void tracker_aruco::draw_ar(bool ok) cv::line(frame, m[i], m[(i+1)%4], cv::Scalar(0, 0, 255), 2, 8); } - char buf[32]; - ::snprintf(buf, sizeof(buf)-1, "Hz: %d", (int)(unsigned short)cur_fps); + char buf[9]; + ::snprintf(buf, sizeof(buf)-1, "Hz: %d", clamp(int(fps), 0, 9999)); buf[sizeof(buf)-1] = '\0'; cv::putText(frame, buf, cv::Point(10, 32), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar(0, 255, 0), 1); } -void tracker_aruco::clamp_last_roi() +void aruco_tracker::clamp_last_roi() { if (last_roi.x < 0) last_roi.x = 0; @@ -242,7 +235,7 @@ void tracker_aruco::clamp_last_roi() last_roi.height -= last_roi.y; } -void tracker_aruco::set_points() +void aruco_tracker::set_points() { using f = float; const f hx = f(s.headpos_x), hy = f(s.headpos_y), hz = f(s.headpos_z); @@ -257,19 +250,18 @@ void tracker_aruco::set_points() obj_points[x4] = cv::Point3f(-size + hx, size + hy, 0 + hz); } -void tracker_aruco::draw_centroid() +void aruco_tracker::draw_centroid() { repr2.clear(); static const std::vector<cv::Point3f> centroid { cv::Point3f(0, 0, 0) }; - cv::projectPoints(centroid, rvec, tvec, intrinsics, dist_coeffs, repr2); + cv::projectPoints(centroid, rvec, tvec, intrinsics, cv::noArray(), repr2); - auto s = cv::Scalar(255, 0, 255); - cv::circle(frame, repr2[0], 4, s, -1); + cv::circle(frame, repr2[0], 4, cv::Scalar(255, 0, 255), -1); } -void tracker_aruco::set_last_roi() +void aruco_tracker::set_last_roi() { roi_projection.clear(); @@ -281,12 +273,12 @@ void tracker_aruco::set_last_roi() roi_points[i] = pt * c_search_window; } - cv::projectPoints(roi_points, rvec, tvec, intrinsics, dist_coeffs, roi_projection); + cv::projectPoints(roi_points, rvec, tvec, intrinsics, cv::noArray(), roi_projection); set_roi_from_projection(); } -void tracker_aruco::set_rmat() +void aruco_tracker::set_rmat() { cv::Rodrigues(rvec, rmat); @@ -305,7 +297,7 @@ void tracker_aruco::set_rmat() t = cv::Vec3d(tvec[0], -tvec[1], tvec[2]); } -void tracker_aruco::set_roi_from_projection() +void aruco_tracker::set_roi_from_projection() { last_roi = cv::Rect(color.cols-1, color.rows-1, 0, 0); @@ -328,7 +320,7 @@ void tracker_aruco::set_roi_from_projection() clamp_last_roi(); } -void tracker_aruco::run() +void aruco_tracker::run() { cv::setNumThreads(0); @@ -340,7 +332,7 @@ void tracker_aruco::run() if (!open_camera()) return; - last_time = std::uint64_t(cv::getTickCount()); + fps_timer.start(); while (!stop) { @@ -357,7 +349,7 @@ void tracker_aruco::run() set_intrinsics(); - update_fps(alpha_); + update_fps(); markers.clear(); @@ -367,10 +359,7 @@ void tracker_aruco::run() { set_points(); - if (!cv::solvePnP(obj_points, markers[0], intrinsics, dist_coeffs, rvec, tvec, false, cv::SOLVEPNP_DLS)) - goto fail; - - if (!cv::solvePnP(obj_points, markers[0], intrinsics, dist_coeffs, rvec, tvec, true, cv::SOLVEPNP_ITERATIVE)) + if (!cv::solvePnP(obj_points, markers[0], intrinsics, cv::noArray(), rvec, tvec, false, cv::SOLVEPNP_ITERATIVE)) goto fail; set_last_roi(); @@ -389,19 +378,19 @@ fail: } } -void tracker_aruco::data(double *data) +void aruco_tracker::data(double *data) { QMutexLocker lck(&mtx); data[Yaw] = pose[Yaw]; data[Pitch] = pose[Pitch]; data[Roll] = pose[Roll]; - data[TX] = pose[TX] * .5; - data[TY] = pose[TY] * .5; + data[TX] = pose[TX]; + data[TY] = pose[TY]; data[TZ] = pose[TZ]; } -dialog_aruco::dialog_aruco() +aruco_dialog::aruco_dialog() { tracker = nullptr; calib_timer.setInterval(250); @@ -419,34 +408,15 @@ dialog_aruco::dialog_aruco() connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); connect(ui.btn_calibrate, SIGNAL(clicked()), this, SLOT(toggleCalibrate())); connect(this, SIGNAL(destroyed()), this, SLOT(cleanupCalib())); - connect(&calib_timer, SIGNAL(timeout()), this, SLOT(update_tracker_calibration())); - connect(ui.cameraName, &QComboBox::currentTextChanged, this, &dialog_aruco::set_camera_settings_available); - set_camera_settings_available(ui.cameraName->currentText()); - connect(ui.camera_settings, &QPushButton::clicked, this, &dialog_aruco::show_camera_settings); -} + connect(ui.camera_settings, SIGNAL(clicked()), this, SLOT(camera_settings())); + connect(&s.camera_name, SIGNAL(valueChanged(const QString&)), this, SLOT(update_camera_settings_state(const QString&))); -void dialog_aruco::set_camera_settings_available(const QString& camera_name) -{ - const bool avail = video_property_page::should_show_dialog(camera_name); - ui.camera_settings->setEnabled(avail); + update_camera_settings_state(s.camera_name); } -void dialog_aruco::show_camera_settings() -{ - const int idx = ui.cameraName->currentIndex(); - if (tracker) - { - cv::VideoCapture& cap = tracker->camera; - if (cap.isOpened()) - video_property_page::show_from_capture(cap, idx); - } - else - video_property_page::show(idx); -} - -void dialog_aruco::toggleCalibrate() +void aruco_dialog::toggleCalibrate() { if (!calib_timer.isActive()) { @@ -467,13 +437,13 @@ void dialog_aruco::toggleCalibrate() } } -void dialog_aruco::cleanupCalib() +void aruco_dialog::cleanupCalib() { if (calib_timer.isActive()) calib_timer.stop(); } -void dialog_aruco::update_tracker_calibration() +void aruco_dialog::update_tracker_calibration() { if (calib_timer.isActive() && tracker) { @@ -484,15 +454,31 @@ void dialog_aruco::update_tracker_calibration() } } -void dialog_aruco::doOK() +void aruco_dialog::doOK() { s.b->save(); close(); } -void dialog_aruco::doCancel() +void aruco_dialog::doCancel() { close(); } -OPENTRACK_DECLARE_TRACKER(tracker_aruco, dialog_aruco, aruco_metadata) +void aruco_dialog::camera_settings() +{ + if (tracker) + { + QMutexLocker l(&tracker->camera_mtx); + video_property_page::show_from_capture(tracker->camera, camera_name_to_index(s.camera_name)); + } + else + video_property_page::show(camera_name_to_index(s.camera_name)); +} + +void aruco_dialog::update_camera_settings_state(const QString& name) +{ + ui.camera_settings->setEnabled(video_property_page::should_show_dialog(name)); +} + +OPENTRACK_DECLARE_TRACKER(aruco_tracker, aruco_dialog, aruco_metadata) diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h index ccb1ad21..cdea1e42 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.h +++ b/tracker-aruco/ftnoir_tracker_aruco.h @@ -8,14 +8,13 @@ #pragma once #include "ui_aruco-trackercontrols.h" +#include "options/options.hpp" +#include "trans_calib.h" #include "api/plugin-api.hpp" -#include "include/markerdetector.h" - #include "cv/video-widget.hpp" -#include "cv/translation-calibrator.hpp" +#include "compat/timer.hpp" -#include <opencv2/core.hpp> -#include <opencv2/videoio.hpp> +#include "include/markerdetector.h" #include <QObject> #include <QThread> @@ -26,7 +25,9 @@ #include <cinttypes> -#include "options/options.hpp" +#include <opencv2/core.hpp> +#include <opencv2/videoio.hpp> + using namespace options; struct settings : opts { @@ -46,16 +47,16 @@ struct settings : opts { {} }; -class dialog_aruco; +class aruco_dialog; -class tracker_aruco : protected QThread, public ITracker +class aruco_tracker : protected QThread, public ITracker { Q_OBJECT - friend class dialog_aruco; + friend class aruco_dialog; static constexpr float c_search_window = 1.3f; public: - tracker_aruco(); - ~tracker_aruco() override; + aruco_tracker(); + ~aruco_tracker() override; void start_tracker(QFrame* frame) override; void data(double *data) override; void run() override; @@ -65,7 +66,7 @@ private: bool detect_without_roi(); bool open_camera(); void set_intrinsics(); - void update_fps(double dt); + void update_fps(); void draw_ar(bool ok); void clamp_last_roi(); void set_points(); @@ -77,16 +78,14 @@ private: cv::VideoCapture camera; QMutex camera_mtx; QMutex mtx; - volatile bool stop; - QHBoxLayout* layout; - cv_video_widget* videoWidget; + qshared<cv_video_widget> videoWidget; + qshared<QHBoxLayout> layout; settings s; - double pose[6]; + double pose[6], fps; cv::Mat frame, grayscale, color; cv::Matx33d r; std::vector<cv::Point3f> obj_points; cv::Matx33d intrinsics; - cv::Matx14f dist_coeffs; aruco::MarkerDetector detector; std::vector<aruco::Marker> markers; cv::Vec3d t; @@ -97,25 +96,26 @@ private: cv::Vec3d euler; std::vector<cv::Point3f> roi_points; cv::Rect last_roi; - double freq, cur_fps; - std::uint64_t last_time; + Timer fps_timer; + + volatile bool stop; - static constexpr float size_min = 0.05f; - static constexpr float size_max = 0.3f; + static constexpr const float size_min = 0.05; + static constexpr const float size_max = 0.3; - static constexpr double alpha_ = .95; + static constexpr const double RC = .25; }; -class dialog_aruco : public ITrackerDialog +class aruco_dialog : public ITrackerDialog { Q_OBJECT public: - dialog_aruco(); - void register_tracker(ITracker * x) override { tracker = static_cast<tracker_aruco*>(x); } + aruco_dialog(); + void register_tracker(ITracker * x) override { tracker = static_cast<aruco_tracker*>(x); } void unregister_tracker() override { tracker = nullptr; } private: - Ui::dialog_aruco ui; - tracker_aruco* tracker; + Ui::Form ui; + aruco_tracker* tracker; settings s; TranslationCalibrator calibrator; QTimer calib_timer; @@ -125,12 +125,12 @@ private slots: void toggleCalibrate(); void cleanupCalib(); void update_tracker_calibration(); - void set_camera_settings_available(const QString& camera_name); - void show_camera_settings(); + void camera_settings(); + void update_camera_settings_state(const QString& name); }; class aruco_metadata : public Metadata { - QString name() { return QString(QCoreApplication::translate("aruco_metadata", "aruco -- paper marker tracker")); } + QString name() { return QString("aruco -- paper marker tracker"); } QIcon icon() { return QIcon(":/images/aruco.png"); } }; diff --git a/tracker-aruco/trans_calib.cpp b/tracker-aruco/trans_calib.cpp new file mode 100644 index 00000000..b5148efd --- /dev/null +++ b/tracker-aruco/trans_calib.cpp @@ -0,0 +1,41 @@ +/* Copyright (c) 2012 Patrick Ruoff + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#include "trans_calib.h" + +TranslationCalibrator::TranslationCalibrator() +{ + reset(); +} + +void TranslationCalibrator::reset() +{ + P = cv::Matx66f::zeros(); + y = cv::Vec6f(0,0,0, 0,0,0); +} + +void TranslationCalibrator::update(const cv::Matx33d& R_CM_k, const cv::Vec3d& t_CM_k) +{ + cv::Matx<double, 6,3> H_k_T = cv::Matx<double, 6,3>::zeros(); + for (int i=0; i<3; ++i) { + for (int j=0; j<3; ++j) { + H_k_T(i,j) = R_CM_k(j,i); + } + } + for (int i=0; i<3; ++i) + { + H_k_T(3+i,i) = 1.0; + } + P += H_k_T * H_k_T.t(); + y += H_k_T * t_CM_k; +} + +cv::Vec3f TranslationCalibrator::get_estimate() +{ + cv::Vec6f x = P.inv() * y; + return cv::Vec3f(x[0], x[1], x[2]); +} diff --git a/tracker-aruco/trans_calib.h b/tracker-aruco/trans_calib.h new file mode 100644 index 00000000..3d1e56fa --- /dev/null +++ b/tracker-aruco/trans_calib.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2012 Patrick Ruoff + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#ifndef TRANSCALIB_H +#define TRANSCALIB_H + +#include <opencv2/core/core.hpp> + +//----------------------------------------------------------------------------- +// Calibrates the translation from head to model = t_MH +// by recursive least squares / +// kalman filter in information form with identity noise covariance +// measurement equation when head position = t_CH is fixed: +// (R_CM_k , Id)*(-t_MH, t_CH) = t_CM_k + +class TranslationCalibrator +{ +public: + TranslationCalibrator(); + + // reset the calibration process + void reset(); + + // update the current estimate + void update(const cv::Matx33d& R_CM_k, const cv::Vec3d& t_CM_k); + + // get the current estimate for t_MH + cv::Vec3f get_estimate(); + +private: + cv::Matx66f P; // normalized precision matrix = inverse covariance + cv::Vec6f y; // P*(-t_MH, t_CH) +}; + +#endif //TRANSCALIB_H diff --git a/tracker-hatire/ftnoir_tracker_hat.cpp b/tracker-hatire/ftnoir_tracker_hat.cpp index 421a0e26..e8da968f 100644 --- a/tracker-hatire/ftnoir_tracker_hat.cpp +++ b/tracker-hatire/ftnoir_tracker_hat.cpp @@ -56,10 +56,10 @@ void hatire::start_tracker(QFrame*) case result_ok: break; case result_error: - QMessageBox::warning(0,"Error", ret.error, QMessageBox::Ok,QMessageBox::NoButton); + QMessageBox::warning(0, tr("Error"), ret.error, QMessageBox::Ok,QMessageBox::NoButton); break; case result_open_error: - QMessageBox::warning(0,"Error", "Unable to open ComPort: " + ret.error, QMessageBox::Ok,QMessageBox::NoButton); + QMessageBox::warning(0, tr("Error"), tr("Unable to open ComPort: %1").arg(ret.error), QMessageBox::Ok,QMessageBox::NoButton); break; } diff --git a/tracker-hydra/ftnoir_tracker_hydra.cpp b/tracker-hydra/ftnoir_tracker_hydra.cpp index 781339fa..77e62243 100644 --- a/tracker-hydra/ftnoir_tracker_hydra.cpp +++ b/tracker-hydra/ftnoir_tracker_hydra.cpp @@ -1,4 +1,18 @@ -/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */ +/* Copyright (c) 2013 mm0zct + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "ftnoir_tracker_hydra.h" #include "api/plugin-api.hpp" #include <cstdio> diff --git a/tracker-pt/camera.cpp b/tracker-pt/camera.cpp index 82f4a6fe..60eb4bb8 100644 --- a/tracker-pt/camera.cpp +++ b/tracker-pt/camera.cpp @@ -6,60 +6,23 @@ */ #include "camera.h" -#include "compat/camera-names.hpp" #include <string> #include <QDebug> -Camera::~Camera() {} - -void Camera::set_device(const QString& name) -{ - const int index = camera_name_to_index(name); - - desired_name = name; - - if (desired_index != index) - { - desired_index = index; - _set_device_index(); - - // reset fps - dt_valid = 0; - dt_mean = 0; - active_index = index; - } -} - QString Camera::get_desired_name() const { return desired_name; } -void Camera::set_fps(int fps) -{ - if (cam_desired.fps != fps) - { - cam_desired.fps = fps; - _set_fps(); - } -} - -void Camera::set_res(int x_res, int y_res) +QString Camera::get_active_name() const { - if (cam_desired.res_x != x_res || cam_desired.res_y != y_res) - { - cam_desired.res_x = x_res; - cam_desired.res_y = y_res; - _set_res(); - } + return active_name; } DEFUN_WARN_UNUSED bool Camera::get_info(CamInfo& ret) { if (cam_info.res_x == 0 || cam_info.res_y == 0) - { return false; - } ret = cam_info; return true; } @@ -68,7 +31,7 @@ bool Camera::get_frame(double dt, cv::Mat* frame) { bool new_frame = _get_frame(frame); // measure fps of valid frames - static constexpr double RC = 1; // second + static constexpr double RC = .1; // seconds const double alpha = dt/(dt + RC); dt_valid += dt; if (new_frame) @@ -76,8 +39,8 @@ bool Camera::get_frame(double dt, cv::Mat* frame) if (dt_mean < 2e-3) dt_mean = dt; else - dt_mean = alpha * dt_mean + (1 - alpha) * dt_valid; - cam_info.fps = int(std::round(dt_mean > 2e-3 ? 1 / dt_mean : 0)); + dt_mean = (1-alpha) * dt_mean + alpha * dt_valid; + cam_info.fps = dt_mean > 2e-3 ? int(1 / dt_mean + .65) : 0; dt_valid = 0; } else @@ -85,40 +48,52 @@ bool Camera::get_frame(double dt, cv::Mat* frame) return new_frame; } -void CVCamera::start() +DEFUN_WARN_UNUSED bool Camera::start(int idx, int fps, int res_x, int res_y) { - stop(); - cap = new cv::VideoCapture(desired_index); - _set_res(); - _set_fps(); - // extract camera info - if (cap->isOpened()) + if (idx >= 0 && fps >= 0 && res_x > 0 && res_y > 0) { - active_index = desired_index; - cam_info.res_x = 0; - cam_info.res_y = 0; - } else { - stop(); + if (!cap || cap->isOpened() || + cam_desired.idx != idx || + cam_desired.fps != fps || + cam_desired.res_x != res_x || + cam_desired.res_y != res_y) + { + cam_desired.idx = idx; + cam_desired.fps = fps; + cam_desired.res_x = res_x; + cam_desired.res_y = res_y; + + cap = camera_ptr(new cv::VideoCapture(cam_desired.idx)); + + cap->set(cv::CAP_PROP_FRAME_WIDTH, cam_desired.res_x); + cap->set(cv::CAP_PROP_FRAME_HEIGHT, cam_desired.res_y); + cap->set(cv::CAP_PROP_FPS, cam_desired.fps); + + if (cap->isOpened()) + { + cam_info.idx = cam_desired.idx; + cam_info.res_x = 0; + cam_info.res_y = 0; + active_name = desired_name; + + return true; + } + } } + + return stop(), false; } -void CVCamera::stop() +void Camera::stop() { - if (cap) - { - const bool opened = cap->isOpened(); - if (opened) - { - qDebug() << "pt: freeing camera"; - cap->release(); - } - delete cap; - cap = nullptr; - qDebug() << "pt camera: stopped"; - } + cap = nullptr; + desired_name = QString(); + active_name = QString(); + cam_info = CamInfo(); + cam_desired = CamInfo(); } -bool CVCamera::_get_frame(cv::Mat* frame) +bool Camera::_get_frame(cv::Mat* frame) { if (cap && cap->isOpened()) { @@ -135,21 +110,12 @@ bool CVCamera::_get_frame(cv::Mat* frame) return false; } -void CVCamera::_set_fps() -{ - if (cap) cap->set(cv::CAP_PROP_FPS, cam_desired.fps); -} - -void CVCamera::_set_res() +void Camera::camera_deleter::operator()(cv::VideoCapture* cap) { if (cap) { - cap->set(cv::CAP_PROP_FRAME_WIDTH, cam_desired.res_x); - cap->set(cv::CAP_PROP_FRAME_HEIGHT, cam_desired.res_y); + if (cap->isOpened()) + cap->release(); + std::default_delete<cv::VideoCapture>()(cap); } } -void CVCamera::_set_device_index() -{ - if (desired_index != active_index) - stop(); -} diff --git a/tracker-pt/camera.h b/tracker-pt/camera.h index be7d35c4..3f5a8f43 100644 --- a/tracker-pt/camera.h +++ b/tracker-pt/camera.h @@ -7,6 +7,9 @@ #pragma once +#undef NDEBUG +#include <cassert> + #include "compat/util.hpp" #include <opencv2/core/core.hpp> @@ -17,81 +20,51 @@ struct CamInfo { - CamInfo() : res_x(0), res_y(0), fps(0) {} + CamInfo() : res_x(0), res_y(0), fps(-1), idx(-1) {} int res_x; int res_y; int fps; + int idx; }; -// ---------------------------------------------------------------------------- -// Base class for cameras, calculates the frame rate -class Camera +class Camera final { public: - Camera() : dt_valid(0), dt_mean(0), desired_index(0), active_index(-1) {} - virtual ~Camera() = 0; - - // start/stop capturing - virtual void start() = 0; - virtual void stop() = 0; - void restart() { stop(); start(); } + Camera() : dt_valid(0), dt_mean(0) {} - // calls corresponding template methods and reinitializes frame rate calculation - void set_device(const QString& name); - void set_fps(int fps); - void set_res(int x_res, int y_res); + DEFUN_WARN_UNUSED bool start(int idx, int fps, int res_x, int res_y); + void stop(); - // gets a frame from the camera, dt: time since last call in seconds DEFUN_WARN_UNUSED bool get_frame(double dt, cv::Mat* frame); - - // WARNING: returned references are valid as long as object DEFUN_WARN_UNUSED bool get_info(CamInfo &ret); + CamInfo get_desired() const { return cam_desired; } QString get_desired_name() const; -protected: - // get a frame from the camera - DEFUN_WARN_UNUSED virtual bool _get_frame(cv::Mat* frame) = 0; - - // update the camera using cam_desired, write res and f to cam_info if successful - virtual void _set_device_index() = 0; - virtual void _set_fps() = 0; - virtual void _set_res() = 0; + QString get_active_name() const; + + cv::VideoCapture& operator*() { assert(cap); return *cap; } + const cv::VideoCapture& operator*() const { assert(cap); return *cap; } + cv::VideoCapture* operator->() { assert(cap); return cap.get(); } + const cv::VideoCapture* operator->() const { return cap.get(); } + operator bool() const { return cap && cap->isOpened(); } + private: + DEFUN_WARN_UNUSED bool _get_frame(cv::Mat* frame); + double dt_valid; double dt_mean; -protected: + CamInfo cam_info; CamInfo cam_desired; - QString desired_name; - int desired_index; - int active_index; -}; + QString desired_name, active_name; -// ---------------------------------------------------------------------------- -// camera based on OpenCV's videoCapture -class CVCamera final : public Camera -{ -public: - CVCamera() : cap(NULL) {} - ~CVCamera() { stop(); } - - void start() override; - void stop() override; - - operator cv::VideoCapture*() { return cap; } -protected: - bool _get_frame(cv::Mat* frame) override; - void _set_fps() override; - void _set_res() override; - void _set_device_index() override; -private: - cv::VideoCapture* cap; -}; + struct camera_deleter final + { + void operator()(cv::VideoCapture* cap); + }; -enum RotationType -{ - CLOCKWISE = 0, - ZERO = 1, - COUNTER_CLOCKWISE = 2 + using camera_ptr = std::unique_ptr<cv::VideoCapture, camera_deleter>; + + camera_ptr cap; }; diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp index d13c5545..40293f56 100644 --- a/tracker-pt/ftnoir_tracker_pt.cpp +++ b/tracker-pt/ftnoir_tracker_pt.cpp @@ -19,8 +19,6 @@ //----------------------------------------------------------------------------- Tracker_PT::Tracker_PT() : - video_widget(nullptr), - video_frame(nullptr), point_count(0), commands(0), ever_success(false) @@ -32,13 +30,7 @@ Tracker_PT::~Tracker_PT() { set_command(ABORT); wait(); - if (video_widget) - delete video_widget; - video_widget = NULL; - if (video_frame) - { - if (video_frame->layout()) delete video_frame->layout(); - } + // fast start/stop causes breakage camera.stop(); } @@ -65,12 +57,21 @@ bool Tracker_PT::get_focal_length(f& ret) using std::tan; using std::atan; using std::sqrt; - +#if 1 const double w = info.res_x, h = info.res_y; const double diag = sqrt(w/h*w/h + h/w*h/w); const double diag_fov = static_cast<int>(s.fov) * M_PI / 180.; const double fov = 2.*atan(tan(diag_fov/2.)/diag); ret = .5 / tan(.5 * fov); +#else + // the formula looks correct but doesn't work right regardless + + const double diag_fov = s.fov * M_PI/180; + const double aspect = w / sqrt(w*w + h*h); + const double fov = 2*atan(tan(diag_fov*.5) * aspect); + ret = .5 / tan(fov * .5); + static bool once = false; if (!once) { once = true; qDebug() << "f" << ret << "fov" << (fov * 180/M_PI); } +#endif return true; } return false; @@ -171,7 +172,7 @@ void Tracker_PT::run() video_widget->update_image(frame_); } } - qDebug() << "pt: Thread stopping"; + qDebug() << "pt: thread stopped"; } void Tracker_PT::apply_settings() @@ -180,37 +181,26 @@ void Tracker_PT::apply_settings() QMutexLocker l(&camera_mtx); - CamInfo info = camera.get_desired(); - const QString name = camera.get_desired_name(); + CamInfo info; - if (s.cam_fps != info.fps || - s.cam_res_x != info.res_x || - s.cam_res_y != info.res_y || - s.camera_name != name) - { - qDebug() << "pt: starting camera"; - camera.stop(); - camera.set_device(s.camera_name); - camera.set_res(s.cam_res_x, s.cam_res_y); - camera.set_fps(s.cam_fps); + if (!camera.get_info(info) || frame.rows != info.res_y || frame.cols != info.res_x) frame = cv::Mat(); - camera.start(); - } + + camera.start(camera_name_to_index(s.camera_name), s.cam_fps, s.cam_res_x, s.cam_res_y); qDebug() << "pt: done applying settings"; } -void Tracker_PT::start_tracker(QFrame *parent_window) +void Tracker_PT::start_tracker(QFrame* video_frame) { - video_frame = parent_window; video_frame->setAttribute(Qt::WA_NativeWindow); - video_frame->show(); - video_widget = new cv_video_widget(video_frame); - QHBoxLayout* video_layout = new QHBoxLayout(parent_window); - video_layout->setContentsMargins(0, 0, 0, 0); - video_layout->addWidget(video_widget); - video_frame->setLayout(video_layout); + video_widget = qptr<cv_video_widget>(video_frame); + layout = qptr<QHBoxLayout>(video_frame); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(video_widget.data()); + video_frame->setLayout(layout.data()); video_widget->resize(video_frame->width(), video_frame->height()); + video_frame->show(); start(); } diff --git a/tracker-pt/ftnoir_tracker_pt.h b/tracker-pt/ftnoir_tracker_pt.h index 020694ae..66928655 100644 --- a/tracker-pt/ftnoir_tracker_pt.h +++ b/tracker-pt/ftnoir_tracker_pt.h @@ -18,12 +18,14 @@ #include "point_tracker.h" #include "compat/timer.hpp" #include "cv/video-widget.hpp" +#include "compat/util.hpp" #include <QCoreApplication> #include <QThread> #include <QMutex> #include <QMutexLocker> #include <QTime> +#include <QLayout> #include <atomic> #include <memory> #include <vector> @@ -65,12 +67,12 @@ private: QMutex camera_mtx; QMutex data_mtx; - CVCamera camera; + Camera camera; PointExtractor point_extractor; PointTracker point_tracker; - cv_video_widget* video_widget; - QFrame* video_frame; + qshared<cv_video_widget> video_widget; + qshared<QLayout> layout; settings_pt s; Timer time; diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.cpp b/tracker-pt/ftnoir_tracker_pt_dialog.cpp index 749ea1ff..d3951bd9 100644 --- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp +++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp @@ -142,12 +142,16 @@ void TrackerDialog_PT::set_camera_settings_available(const QString& camera_name) void TrackerDialog_PT::show_camera_settings() { const int idx = ui.camdevice_combo->currentIndex(); + if (tracker) { - cv::VideoCapture* cap = tracker->camera; - if (cap && cap->isOpened()) + if (tracker->camera) { - video_property_page::show_from_capture(*cap, idx); + cv::VideoCapture& cap = *tracker->camera; + + CamInfo info; + if (tracker->camera.get_info(info)) + video_property_page::show_from_capture(cap, info.idx); } } else diff --git a/tracker-pt/lang/ru_RU.ts b/tracker-pt/lang/ru_RU.ts new file mode 100644 index 00000000..f6094d27 --- /dev/null +++ b/tracker-pt/lang/ru_RU.ts @@ -0,0 +1,294 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> + <name>PT_metadata</name> + <message> + <location filename="../ftnoir_tracker_pt.h" line="+92"/> + <source>PointTracker 1.1</source> + <translation></translation> + </message> +</context> +<context> + <name>UICPTClientControls</name> + <message> + <location filename="../FTNoIR_PT_Controls.ui" line="+23"/> + <source>PointTracker Settings</source> + <translation>Настройки PointTracker</translation> + </message> + <message> + <location line="+38"/> + <source>Camera</source> + <translation>Камера</translation> + </message> + <message> + <location line="+6"/> + <source>Camera settings</source> + <translation>Настройка камеры</translation> + </message> + <message> + <location line="+19"/> + <source>°</source> + <translation></translation> + </message> + <message> + <location line="+22"/> + <source>Diagonal field of view</source> + <translation>Угол обзора камеры</translation> + </message> + <message> + <location line="+13"/> + <source>Width</source> + <translation>Ширина</translation> + </message> + <message> + <location line="+13"/> + <source>FPS</source> + <translation>FPS (Кадров в секунду)</translation> + </message> + <message> + <location line="+16"/> + <source>Desired capture height</source> + <translation></translation> + </message> + <message> + <location line="+3"/> + <location line="+55"/> + <location line="+175"/> + <location line="+16"/> + <source> px</source> + <translation></translation> + </message> + <message> + <location line="-233"/> + <source>Dynamic pose timeout</source> + <translation>Динамическая поза (время ожидания)</translation> + </message> + <message> + <location line="+13"/> + <source>Desired capture framerate</source> + <translation>Желаемая частота кадров</translation> + </message> + <message> + <location line="+3"/> + <source> Hz</source> + <translation> Гц</translation> + </message> + <message> + <location line="+23"/> + <source>Desired capture width</source> + <translation>Желаемая ширина захвата</translation> + </message> + <message> + <location line="+22"/> + <source>Height</source> + <translation>Высота</translation> + </message> + <message> + <location line="+7"/> + <source> ms</source> + <translation> мс</translation> + </message> + <message> + <location line="+19"/> + <source>Dynamic pose resolution</source> + <translation>Динамическая поза (активация) </translation> + </message> + <message> + <location line="+13"/> + <source>Device</source> + <translation>Устройство</translation> + </message> + <message> + <location line="+16"/> + <source>Open</source> + <translation>Открыть</translation> + </message> + <message> + <location line="+7"/> + <source>Camera settings (when available)</source> + <translation>Параметры камеры (если доступно)</translation> + </message> + <message> + <location line="+10"/> + <source>Point extraction</source> + <translation>Извлечение точек</translation> + </message> + <message> + <location line="+6"/> + <source>Max size</source> + <translation>Макс.размер</translation> + </message> + <message> + <location line="+10"/> + <source>Threshold</source> + <translation>Порог</translation> + </message> + <message> + <location line="+10"/> + <source>Min size</source> + <translation>Мин.размер</translation> + </message> + <message> + <location line="+16"/> + <source>Intensity threshold for point extraction</source> + <translation>Порог интенсивности для извлечения точки</translation> + </message> + <message> + <location line="+25"/> + <source>Automatic threshold</source> + <translation>Автоматич. порог</translation> + </message> + <message> + <location line="+7"/> + <source>Enable, slider sets point size</source> + <translation>Полузнок устанавливает размер точек</translation> + </message> + <message> + <location line="+7"/> + <source>Maximum point diameter</source> + <translation></translation> + </message> + <message> + <location line="+16"/> + <source>Minimum point diameter</source> + <translation></translation> + </message> + <message> + <location line="+20"/> + <source>Model</source> + <translation>Модель</translation> + </message> + <message> + <location line="+28"/> + <source>Clip</source> + <translation>Клипса</translation> + </message> + <message> + <location line="+18"/> + <location line="+154"/> + <location line="+129"/> + <source>Model Dimensions</source> + <translation>Размеры модели</translation> + </message> + <message> + <location line="-271"/> + <location line="+19"/> + <location line="+48"/> + <location line="+19"/> + <location line="+97"/> + <location line="+35"/> + <location line="+32"/> + <location line="+40"/> + <location line="+26"/> + <location line="+13"/> + <location line="+13"/> + <location line="+13"/> + <location line="+26"/> + <location line="+132"/> + <location line="+26"/> + <location line="+26"/> + <source> mm</source> + <translation> мм</translation> + </message> + <message> + <location line="-511"/> + <location line="+116"/> + <source>Side</source> + <translation>Сбоку</translation> + </message> + <message> + <location line="-65"/> + <location line="+132"/> + <source>Front</source> + <translation>Спереди</translation> + </message> + <message> + <location line="-107"/> + <source>Cap</source> + <translation>Кепка</translation> + </message> + <message> + <location line="+135"/> + <source>Custom</source> + <translation>Свой</translation> + </message> + <message> + <location line="+18"/> + <location line="+169"/> + <location line="+106"/> + <source>z:</source> + <translation></translation> + </message> + <message> + <location line="-249"/> + <location line="+104"/> + <location line="+93"/> + <source>x:</source> + <translation></translation> + </message> + <message> + <location line="-132"/> + <source><html><head/><body><p>Location of the two remaining model points<br/>with respect to the reference point in default pose</p><p>Use any units you want, not necessarily centimeters.</p></body></html></source> + <translatorcomment>Расположение двух оставшихся точек модели относительно опорной точки в стандартной позе. Возможно исп-ть любые единицы измерения, не обязательно сантиметры.</translatorcomment> + <translation><html><head/><body><p> Расположение двух оставшихся точек модели<br/>относительно опорной точки в стандартной позе. </p><p>Возможно использовать любые единицы измерения.</p></body></html</translation> + </message> + <message> + <location line="+26"/> + <location line="+65"/> + <location line="+67"/> + <source>y:</source> + <translation></translation> + </message> + <message> + <location line="-106"/> + <source><html><head/><body><p><span style=" font-size:16pt;">P</span><span style=" font-size:16pt; vertical-align:sub;">3</span></p></body></html></source> + <translation></translation> + </message> + <message> + <location line="+13"/> + <source><html><head/><body><p><span style=" font-size:16pt;">P</span><span style=" font-size:16pt; vertical-align:sub;">2</span></p></body></html></source> + <translation></translation> + </message> + <message> + <location line="+46"/> + <source>Model position</source> + <translation>Положение модели</translation> + </message> + <message> + <location line="+105"/> + <source><html><head/><body><p><a href="https://github.com/opentrack/opentrack/wiki/model-calibration-for-PT-and-Aruco-trackers"><span style=" text-decoration: underline; color:#0000ff;">Instructions on the opentrack wiki</span></a></p></body></html></source> + <translation><html><head/><body><p><a href="https://github.com/opentrack/opentrack/wiki/model-calibration-for-PT-and-Aruco-trackers"><span style=" text-decoration: underline; color:#0000ff;">Инструкция на opentrack-вики</span></a></p></body></html></translation> + </message> + <message> + <location line="+13"/> + <source>Start calibration</source> + <translation>Начать калибровку</translation> + </message> + <message> + <location line="+17"/> + <source>About</source> + <translation>О программе</translation> + </message> + <message> + <location line="+6"/> + <source><html><head/><body><p><span style=" font-weight:600;">FTNoIR PointTracker Plugin<br/>Version 1.1</span></p><p><span style=" font-weight:600;">by Patrick Ruoff</span></p><p><a href="http://ftnoirpt.sourceforge.net/"><span style=" font-weight:600; text-decoration: underline; color:#0000ff;">Manual (external)</span></a></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">FTNoIR PointTracker Plugin<br/>Version 1.1</span></p><p><span style=" font-weight:600;">by Patrick Ruoff</span></p><p><a href="http://ftnoirpt.sourceforge.net/"><span style=" font-weight:600; text-decoration: underline; color:#0000ff;">Руководство (PointTracker)</span></a></p></body></html></translation> + </message> + <message> + <location line="+36"/> + <source>Status</source> + <translation>Статус</translation> + </message> + <message> + <location line="+6"/> + <source>Extracted Points:</source> + <translation>Извлечено точек:</translation> + </message> + <message> + <location line="+7"/> + <source>Camera Info:</source> + <translation>Параметры камеры:</translation> + </message> +</context> +</TS> diff --git a/tracker-qt-gamepad/CMakeLists.txt b/tracker-qt-gamepad/CMakeLists.txt new file mode 100644 index 00000000..c892c5c6 --- /dev/null +++ b/tracker-qt-gamepad/CMakeLists.txt @@ -0,0 +1,3 @@ +if(FALSE AND Qt5Gamepad_FOUND) + opentrack_boilerplate(opentrack-tracker-qt-gamepad) +endif() diff --git a/tracker-qt-gamepad/test.cpp b/tracker-qt-gamepad/test.cpp new file mode 100644 index 00000000..4f2c8daa --- /dev/null +++ b/tracker-qt-gamepad/test.cpp @@ -0,0 +1,99 @@ +/* Copyright (c) 2014, Stanislaw Halik <sthalik@misaki.pl> + + * Permission to use, copy, modify, and/or distribute this + * software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission + * notice appear in all copies. + */ + +#include "test.h" +#include "api/plugin-api.hpp" +#include <cmath> + +#include <QDebug> + +const double gamepad_tracker::incr[6] = +{ + 50, 40, 80, + 70, 5, 3 +}; + +gamepad_tracker::gamepad_tracker() : + last_x { 0, 0, 0, 0, 0, 0 } +{ +} + +gamepad_tracker::~gamepad_tracker() +{ +} + +void gamepad_tracker::start_tracker(QFrame*) +{ + t.start(); +} + +#ifdef EMIT_NAN +# include <cstdlib> +#endif + +void gamepad_tracker::data(double *data) +{ + using std::fmod; + using std::sin; + using std::fabs; + using std::copysign; + + const double dt = t.elapsed_seconds(); + t.start(); + +#ifdef EMIT_NAN + if ((rand()%4) == 0) + { + for (int i = 0; i < 6; i++) + data[i] = 0./0.; + } + else +#endif + for (int i = 0; i < 6; i++) + { + double x = fmod(last_x[i] + incr[i] * d2r * dt, 2 * M_PI); + last_x[i] = x; + + if (i >= 3) + { +#ifdef DISCONTINUITY + if (x > pi + pi/2) + x -= M_PI; + else if (x > pi/2 && x < pi) + x += M_PI; +#endif + + data[i] = sin(x) * 180; + } + else + { + data[i] = sin(x) * 100; + } + } +} + +gamepad_dialog::gamepad_dialog() +{ + ui.setupUi(this); + + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); +} + +void gamepad_dialog::doOK() +{ + //s.b->save(); + close(); +} + +void gamepad_dialog::doCancel() +{ + close(); +} + +OPENTRACK_DECLARE_TRACKER(gamepad_tracker, gamepad_dialog, gamepad_metadata) diff --git a/tracker-qt-gamepad/test.h b/tracker-qt-gamepad/test.h new file mode 100644 index 00000000..ffa9ac44 --- /dev/null +++ b/tracker-qt-gamepad/test.h @@ -0,0 +1,45 @@ +#pragma once +#include "ui_test.h" +#include "api/plugin-api.hpp" +#include "compat/timer.hpp" + +#include <cmath> + +class gamepad_tracker : public ITracker +{ +public: + gamepad_tracker(); + ~gamepad_tracker() override; + void start_tracker(QFrame *) override; + void data(double *data) override; + +private: + static constexpr double r2d = 180 / M_PI; + static constexpr double d2r = M_PI / 180; + + static const double incr[6]; + double last_x[6]; + Timer t; +}; + +class gamepad_dialog : public ITrackerDialog +{ + Q_OBJECT + + Ui::test_ui ui; +public: + gamepad_dialog(); + void register_tracker(ITracker *) override {} + void unregister_tracker() override {} +private slots: + void doOK(); + void doCancel(); +}; + +class gamepad_metadata : public Metadata +{ +public: + QString name() { return QString(QCoreApplication::translate("gamepad_metadata", "Gamepad input")); } + QIcon icon() { return QIcon(":/images/facetracknoir.png"); } +}; + diff --git a/tracker-qt-gamepad/test.ui b/tracker-qt-gamepad/test.ui new file mode 100644 index 00000000..bdf2cd4e --- /dev/null +++ b/tracker-qt-gamepad/test.ui @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>test_ui</class> + <widget class="QWidget" name="test_ui"> + <property name="windowModality"> + <enum>Qt::NonModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>352</width> + <height>224</height> + </rect> + </property> + <property name="windowTitle"> + <string>Gamepad input</string> + </property> + <property name="windowIcon"> + <iconset> + <normaloff>../gui/images/facetracknoir.png</normaloff>../gui/images/facetracknoir.png</iconset> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> + <slots> + <slot>startEngineClicked()</slot> + <slot>stopEngineClicked()</slot> + <slot>cameraSettingsClicked()</slot> + </slots> +</ui> diff --git a/tracker-qt-gamepad/test_dialog.cpp b/tracker-qt-gamepad/test_dialog.cpp new file mode 100644 index 00000000..5d33555b --- /dev/null +++ b/tracker-qt-gamepad/test_dialog.cpp @@ -0,0 +1,3 @@ +#include "test.h" +#include "api/plugin-api.hpp" + diff --git a/tracker-rift-025/ftnoir_tracker_rift_025.cpp b/tracker-rift-025/ftnoir_tracker_rift_025.cpp index 7c0b2a55..257b4502 100644 --- a/tracker-rift-025/ftnoir_tracker_rift_025.cpp +++ b/tracker-rift-025/ftnoir_tracker_rift_025.cpp @@ -1,4 +1,18 @@ -/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */ +/* Copyright (c) 2013 mm0zct + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "ftnoir_tracker_rift_025.h" #include "api/plugin-api.hpp" #include <OVR.h> @@ -8,7 +22,7 @@ using namespace OVR; -Rift_Tracker::Rift_Tracker() +rift_tracker_025::rift_tracker_025() { pManager = NULL; pSensor = NULL; @@ -16,7 +30,7 @@ Rift_Tracker::Rift_Tracker() old_yaw = 0; } -Rift_Tracker::~Rift_Tracker() +rift_tracker_025::~rift_tracker_025() { if (pSensor) pSensor->Release(); @@ -27,7 +41,7 @@ Rift_Tracker::~Rift_Tracker() System::Destroy(); } -void Rift_Tracker::start_tracker(QFrame*) +void rift_tracker_025::start_tracker(QFrame*) { System::Init(Log::ConfigureDefaultLog(LogMask_All)); pManager = DeviceManager::Create(); @@ -46,23 +60,32 @@ void Rift_Tracker::start_tracker(QFrame*) } else { - QMessageBox::warning(0,"Error", "Unable to create Rift sensor",QMessageBox::Ok,QMessageBox::NoButton); + QMessageBox::warning(nullptr, + QCoreApplication::translate("rift_tracker_025", "Error"), + QCoreApplication::translate("rift_tracker_025", "Unable to create Rift sensor"), + QMessageBox::Ok,QMessageBox::NoButton); } } else { - QMessageBox::warning(0,"Error", "Unable to enumerate Rift tracker",QMessageBox::Ok,QMessageBox::NoButton); + QMessageBox::warning(nullptr, + QCoreApplication::translate("rift_tracker_025", "Error"), + QCoreApplication::translate("rift_tracker_025", "Unable to enumerate Rift tracker"), + QMessageBox::Ok,QMessageBox::NoButton); } } else { - QMessageBox::warning(0,"Error", "Unable to start Rift tracker",QMessageBox::Ok,QMessageBox::NoButton); + QMessageBox::warning(nullptr, + QCoreApplication::translate("rift_tracker_025", "Error"), + QCoreApplication::translate("rift_tracker_025", "Unable to start Rift tracker"), + QMessageBox::Ok,QMessageBox::NoButton); } } -void Rift_Tracker::data(double *data) +void rift_tracker_025::data(double *data) { if (pSFusion != NULL && pSensor != NULL) { @@ -100,4 +123,4 @@ void Rift_Tracker::data(double *data) } } -OPENTRACK_DECLARE_TRACKER(Rift_Tracker, dialog_rift_025, rift_025Dll) +OPENTRACK_DECLARE_TRACKER(rift_tracker_025, dialog_rift_025, rift_025Dll) diff --git a/tracker-rift-025/ftnoir_tracker_rift_025.h b/tracker-rift-025/ftnoir_tracker_rift_025.h index 96331fd2..b65d3e56 100644 --- a/tracker-rift-025/ftnoir_tracker_rift_025.h +++ b/tracker-rift-025/ftnoir_tracker_rift_025.h @@ -21,11 +21,11 @@ struct settings : opts { {} }; -class Rift_Tracker : public ITracker +class rift_tracker_025 : public ITracker { public: - Rift_Tracker(); - virtual ~Rift_Tracker() override; + rift_tracker_025(); + virtual ~rift_tracker_025() override; void start_tracker(QFrame *) override; void data(double *data) override; private: diff --git a/tracker-rift-042/ftnoir_tracker_rift_042.cpp b/tracker-rift-042/ftnoir_tracker_rift_042.cpp index af8a386f..a25288fa 100644 --- a/tracker-rift-042/ftnoir_tracker_rift_042.cpp +++ b/tracker-rift-042/ftnoir_tracker_rift_042.cpp @@ -1,4 +1,18 @@ -/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */ +/* Copyright (c) 2013 mm0zct + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "ftnoir_tracker_rift_042.h" #include "api/plugin-api.hpp" @@ -12,17 +26,17 @@ using namespace OVR; -Rift_Tracker::Rift_Tracker() : old_yaw(0), hmd(nullptr) +rift_tracker_042::rift_tracker_042() : old_yaw(0), hmd(nullptr) { } -Rift_Tracker::~Rift_Tracker() +rift_tracker_042::~rift_tracker_042() { ovrHmd_Destroy(hmd); ovr_Shutdown(); } -void Rift_Tracker::start_tracker(QFrame*) +void rift_tracker_042::start_tracker(QFrame*) { ovr_Initialize(); hmd = ovrHmd_Create(0); @@ -34,14 +48,14 @@ void Rift_Tracker::start_tracker(QFrame*) { QMessageBox::warning(nullptr, "Error", - QStringLiteral("Unable to start Rift tracker: %1").arg(ovrHmd_GetLastError(nullptr)), + QCoreApplication::translate("rift_tracker_042", "Unable to start Rift tracker: %1").arg(ovrHmd_GetLastError(nullptr)), QMessageBox::Ok, QMessageBox::NoButton); } } -void Rift_Tracker::data(double *data) +void rift_tracker_042::data(double *data) { if (hmd) { @@ -87,4 +101,4 @@ void Rift_Tracker::data(double *data) } } -OPENTRACK_DECLARE_TRACKER(Rift_Tracker, dialog_rift_042, rift_042Dll) +OPENTRACK_DECLARE_TRACKER(rift_tracker_042, dialog_rift_042, rift_042Dll) diff --git a/tracker-rift-042/ftnoir_tracker_rift_042.h b/tracker-rift-042/ftnoir_tracker_rift_042.h index ed802da0..82081085 100644 --- a/tracker-rift-042/ftnoir_tracker_rift_042.h +++ b/tracker-rift-042/ftnoir_tracker_rift_042.h @@ -21,11 +21,11 @@ struct settings : opts { {} }; -class Rift_Tracker : public ITracker +class rift_tracker_042 : public ITracker { public: - Rift_Tracker(); - virtual ~Rift_Tracker() override; + rift_tracker_042(); + virtual ~rift_tracker_042() override; void start_tracker(QFrame *) override; void data(double *data) override; private: diff --git a/tracker-rift-080/ftnoir_tracker_rift_080.cpp b/tracker-rift-080/ftnoir_tracker_rift_080.cpp index 35a268f2..98edcb4f 100644 --- a/tracker-rift-080/ftnoir_tracker_rift_080.cpp +++ b/tracker-rift-080/ftnoir_tracker_rift_080.cpp @@ -1,4 +1,18 @@ -/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */ +/* Copyright (c) 2013 mm0zct + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "ftnoir_tracker_rift_080.h" #include "api/plugin-api.hpp" #include "compat/util.hpp" @@ -10,18 +24,18 @@ using namespace OVR; -Rift_Tracker::Rift_Tracker() : old_yaw(0), hmd(nullptr) +rift_tracker_080::rift_tracker_080() : old_yaw(0), hmd(nullptr) { } -Rift_Tracker::~Rift_Tracker() +rift_tracker_080::~rift_tracker_080() { if (hmd) ovr_Destroy(hmd); ovr_Shutdown(); } -void Rift_Tracker::start_tracker(QFrame*) +void rift_tracker_080::start_tracker(QFrame*) { ovrResult code; ovrGraphicsLuid luid = {{0}}; @@ -55,7 +69,7 @@ error: QMessageBox::NoButton); } -void Rift_Tracker::data(double *data) +void rift_tracker_080::data(double *data) { if (hmd) { @@ -100,4 +114,4 @@ void Rift_Tracker::data(double *data) } } -OPENTRACK_DECLARE_TRACKER(Rift_Tracker, dialog_rift_080, rift_080Dll) +OPENTRACK_DECLARE_TRACKER(rift_tracker_080, dialog_rift_080, rift_080Dll) diff --git a/tracker-rift-080/ftnoir_tracker_rift_080.h b/tracker-rift-080/ftnoir_tracker_rift_080.h index 7868b2a2..e5ad9c3a 100644 --- a/tracker-rift-080/ftnoir_tracker_rift_080.h +++ b/tracker-rift-080/ftnoir_tracker_rift_080.h @@ -20,11 +20,11 @@ struct settings : opts { {} }; -class Rift_Tracker : public ITracker +class rift_tracker_080 : public ITracker { public: - Rift_Tracker(); - ~Rift_Tracker() override; + rift_tracker_080(); + ~rift_tracker_080() override; void start_tracker(QFrame *) override; void data(double *data) override; private: diff --git a/tracker-rift-140/impl.cpp b/tracker-rift-140/impl.cpp index 67d5bb47..45bfcc97 100644 --- a/tracker-rift-140/impl.cpp +++ b/tracker-rift-140/impl.cpp @@ -1,4 +1,18 @@ -/* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */ +/* Copyright (c) 2013 mm0zct + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "rift-140.hpp" #include "api/plugin-api.hpp" #include "compat/util.hpp" @@ -8,11 +22,11 @@ using namespace OVR; -Rift_Tracker::Rift_Tracker() : old_yaw(0), hmd(nullptr) +rift_tracker_140::rift_tracker_140() : old_yaw(0), hmd(nullptr) { } -Rift_Tracker::~Rift_Tracker() +rift_tracker_140::~rift_tracker_140() { if (hmd) { @@ -21,7 +35,7 @@ Rift_Tracker::~Rift_Tracker() } } -void Rift_Tracker::start_tracker(QFrame*) +void rift_tracker_140::start_tracker(QFrame*) { if (OVR_FAILURE(ovr_Initialize(nullptr))) goto error; @@ -44,12 +58,12 @@ error: QMessageBox::warning(nullptr, "Error", - QStringLiteral("Unable to start Rift tracker: %1").arg(strerror), + QCoreApplication::translate("rift_tracker_140", "Unable to start Rift tracker: %1").arg(strerror), QMessageBox::Ok, QMessageBox::NoButton); } -void Rift_Tracker::data(double *data) +void rift_tracker_140::data(double *data) { if (hmd) { @@ -94,4 +108,4 @@ void Rift_Tracker::data(double *data) } } -OPENTRACK_DECLARE_TRACKER(Rift_Tracker, dialog_rift_140, rift_140Dll) +OPENTRACK_DECLARE_TRACKER(rift_tracker_140, dialog_rift_140, rift_140Dll) diff --git a/tracker-rift-140/rift-140.hpp b/tracker-rift-140/rift-140.hpp index 23ec6fb3..47eb7cd2 100644 --- a/tracker-rift-140/rift-140.hpp +++ b/tracker-rift-140/rift-140.hpp @@ -20,11 +20,11 @@ struct settings : opts { {} }; -class Rift_Tracker : public ITracker +class rift_tracker_140 : public ITracker { public: - Rift_Tracker(); - ~Rift_Tracker() override; + rift_tracker_140(); + ~rift_tracker_140() override; void start_tracker(QFrame *) override; void data(double *data) override; private: diff --git a/tracker-rs/ftnoir_tracker_rs.cpp b/tracker-rs/ftnoir_tracker_rs.cpp index 6eae0693..d2c77f12 100644 --- a/tracker-rs/ftnoir_tracker_rs.cpp +++ b/tracker-rs/ftnoir_tracker_rs.cpp @@ -87,7 +87,10 @@ bool RSTracker::startSdkInstallationProcess() bool pStarted = QProcess::startDetached(contrib_path + "intel_rs_sdk_runtime_websetup_10.0.26.0396.exe --finstall=core,face3d --fnone=all"); if(!pStarted){ - QMessageBox::warning(0, "Intel® RealSense™ Runtime Installation", "Installation process failed to start.", QMessageBox::Ok); + QMessageBox::warning(nullptr, + tr("Intel® RealSense™ Runtime Installation"), + tr("Installation process failed to start."), + QMessageBox::Ok); } return pStarted; } @@ -100,19 +103,19 @@ void RSTracker::showRealSenseErrorMessageBox(int exitCode) switch(exitCode){ case -101: //The implementation got an invalid handle from the RealSense SDK session/modules - msgBox.setInformativeText("Couldn't initialize RealSense tracking. Please make sure SDK Runtime 2016 R2 is installed."); + msgBox.setInformativeText(tr("Couldn't initialize RealSense tracking. Please make sure SDK Runtime 2016 R2 is installed.")); break; case -301: //RealSense SDK runtime execution aborted. - msgBox.setInformativeText("Tracking stopped after the RealSense SDK Runtime execution has aborted."); + msgBox.setInformativeText(tr("Tracking stopped after the RealSense SDK Runtime execution has aborted.")); break; case -601: //RealSense Camera stream configuration has changed. - msgBox.setInformativeText("Tracking stopped after another program changed camera streams configuration."); + msgBox.setInformativeText(tr("Tracking stopped after another program changed camera streams configuration.")); break; default: msgBox.setInformativeText("Status code: " + QString::number(exitCode) + ".\n\nNote that you need the latest camera drivers and the SDK runtime 2016 R2 to be installed."); } - QPushButton* triggerSdkInstallation = msgBox.addButton("Install Runtime", QMessageBox::ActionRole); + QPushButton* triggerSdkInstallation = msgBox.addButton(tr("Install Runtime"), QMessageBox::ActionRole); msgBox.addButton(QMessageBox::Ok); msgBox.exec(); diff --git a/tracker-steamvr/dialog.cpp b/tracker-steamvr/dialog.cpp index 2e672ca4..bbb8866d 100644 --- a/tracker-steamvr/dialog.cpp +++ b/tracker-steamvr/dialog.cpp @@ -1,7 +1,7 @@ #include "steamvr.hpp" #include "api/plugin-api.hpp" -dialog::dialog() +steamvr_dialog::steamvr_dialog() { ui.setupUi(this); @@ -9,13 +9,13 @@ dialog::dialog() connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); } -void dialog::doOK() +void steamvr_dialog::doOK() { s.b->save(); close(); } -void dialog::doCancel() +void steamvr_dialog::doCancel() { close(); } diff --git a/tracker-steamvr/steamvr.cpp b/tracker-steamvr/steamvr.cpp index 9c357a7a..bab98f0d 100644 --- a/tracker-steamvr/steamvr.cpp +++ b/tracker-steamvr/steamvr.cpp @@ -54,7 +54,9 @@ void steamvr::start_tracker(QFrame*) if (!vr) { - QMessageBox::warning(nullptr, "Valve SteamVR init error", strerror(e), QMessageBox::Close, QMessageBox::NoButton); + QMessageBox::warning(nullptr, + QCoreApplication::translate("steamvr", "Valve SteamVR init error"), strerror(e), + QMessageBox::Close, QMessageBox::NoButton); return; } @@ -69,7 +71,10 @@ void steamvr::start_tracker(QFrame*) if (!ok) { - QMessageBox::warning(nullptr, "Valve SteamVR init warning", "No HMD connected", QMessageBox::Close, QMessageBox::NoButton); + QMessageBox::warning(nullptr, + QCoreApplication::translate("steamvr", "Valve SteamVR init warning"), + QCoreApplication::translate("steamvr", "No HMD connected"), + QMessageBox::Close, QMessageBox::NoButton); return; } } @@ -125,7 +130,7 @@ done: } } -void dialog::register_tracker(ITracker*) {} -void dialog::unregister_tracker() {} +void steamvr_dialog::register_tracker(ITracker*) {} +void steamvr_dialog::unregister_tracker() {} -OPENTRACK_DECLARE_TRACKER(steamvr, dialog, metadata) +OPENTRACK_DECLARE_TRACKER(steamvr, steamvr_dialog, steamvr_metadata) diff --git a/tracker-steamvr/steamvr.hpp b/tracker-steamvr/steamvr.hpp index 2947f3f1..9b53979e 100644 --- a/tracker-steamvr/steamvr.hpp +++ b/tracker-steamvr/steamvr.hpp @@ -40,11 +40,11 @@ private: static QString strerror(error_t error); }; -class dialog : public ITrackerDialog +class steamvr_dialog : public ITrackerDialog { Q_OBJECT public: - dialog(); + steamvr_dialog(); void register_tracker(ITracker *) override; void unregister_tracker() override; @@ -57,10 +57,10 @@ private slots: void doCancel(); }; -class metadata : public Metadata +class steamvr_metadata : public Metadata { public: - QString name() { return QString(QCoreApplication::translate("metadata", "Valve SteamVR")); } + QString name() { return QString(QCoreApplication::translate("steamvr_metadata", "Valve SteamVR")); } QIcon icon() { return QIcon(":/images/rift_tiny.png"); } }; diff --git a/tracker-test/test.cpp b/tracker-test/test.cpp index eba1f603..78c72178 100644 --- a/tracker-test/test.cpp +++ b/tracker-test/test.cpp @@ -77,7 +77,7 @@ void test_tracker::data(double *data) } } -dialog::dialog() +test_dialog::test_dialog() { ui.setupUi(this); @@ -85,15 +85,15 @@ dialog::dialog() connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); } -void dialog::doOK() +void test_dialog::doOK() { //s.b->save(); close(); } -void dialog::doCancel() +void test_dialog::doCancel() { close(); } -OPENTRACK_DECLARE_TRACKER(test_tracker, dialog, metadata) +OPENTRACK_DECLARE_TRACKER(test_tracker, test_dialog, test_metadata) diff --git a/tracker-test/test.h b/tracker-test/test.h index fd446f98..8338e8f3 100644 --- a/tracker-test/test.h +++ b/tracker-test/test.h @@ -22,13 +22,13 @@ private: Timer t; }; -class dialog: public ITrackerDialog +class test_dialog : public ITrackerDialog { Q_OBJECT Ui::test_ui ui; public: - dialog(); + test_dialog(); void register_tracker(ITracker *) override {} void unregister_tracker() override {} private slots: @@ -36,10 +36,10 @@ private slots: void doCancel(); }; -class metadata : public Metadata +class test_metadata : public Metadata { public: - QString name() { return QString(QCoreApplication::translate("metadata", "Testing - sine wave")); } + QString name() { return QString(QCoreApplication::translate("test_metadata", "Testing - sine wave")); } QIcon icon() { return QIcon(":/images/facetracknoir.png"); } }; 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 |