diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2015-09-30 16:21:41 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2015-09-30 16:22:00 +0200 |
commit | ac4dc94acc5d1e1d8fd4867730b835e2aa1fcc89 (patch) | |
tree | 8ca3dc84958e572e3f890ac3f56dfda670c63f60 | |
parent | 03a164ed74d616e8d5203142ec7e9738c8aae547 (diff) | |
parent | eb7703b4ecea5540a4c4a65faa1ff4c53fe97631 (diff) |
Merge branch 'unstable' into trackhat-ui
Sponsored-by: TrackHat
* unstable: (112 commits)
Revert "accela: try more complex deadzone algorithm"
pt: add wiki link for calibration instructions
qfc: more gray colors when graph is disabled
qfc, mapping: disallow editing and change color when checkbox disabled
qfc: don't allow editing when disabled
plugin-support: workaround multiple copies of modules
accela: change translation gain
accela: change rotation gain
pt: change extraction code
freepie-udp: add license
freepie-udp: update
accela: try more complex deadzone algorithm
accela: add helpful comment
accela: revert to more quadratic spline at start
also save if save pending when start tracking
ui: force saving if saving timer is pending
accela: slightly more linear function
pose-widget: simplify
freepie-udp: nix harmless warning
pose-widget: add comment
simple-mat: add __restrict for GNU
ui: save settings on a timer
ui: show a warning if configuration directory can't be used
aruco: tune min/max marker size
win32-joystick: fix duplicate device name selection logic
ui: no need to reset settings on tracking start
ui: save profile when combobox text changes, not merely index
ui: save current tracker/filter/proto, not last
win32-joystick: fix COM failure path
win32-joystick: nix unused variable
win32-joystick: allow for unique selection of joysticks
qfc: also draw line from function start while moving
no need to save on tracking start anymore
save axis opts, not just spline control points
tracker: change centering order again
fix typo
cmake: don't say "opentrack" twice for osx build
ui: really don't switch profiles while refreshing combobox
pt: fix type mismatch
ui: save prior to switching configs
tracker_rs: adjusted tracker's name
ui: don't reload settings if config wasn't changed
ui: save everything before switching profiles
options: return .ini basename easier
tracker_rs: more readable icon
tracker_rs: prevent potential thread starvation
ui: switch to newly-created config
ui: decruft
ui: also bail on new config if its name is ".ini"
ui: auto-refresh config list
ui: display up to 20 profiles w/o scrolling
ui: create an empty config properly in another code branch
udp-tracker: silence harmless warning
plugin-support: fix typo
plugin-support: set more sane RTLD flags
plugin-support: free modules from address space when needed
ui: rename symbol to reflect current usage
ui: move some slots to private
ui, shortcuts: these are slots, actually
ui: no need to write anything to new empty config
ui: avoid potential memory leak
ft: new game support
game-data: chomp newlines in extractor
ui: append extension in the right place
simple-mat: fix arglist SFINAE
simple-mat: can use static_cast here
nix default empty config. it gets created on its own.
ui: these aren't qt slots
ui: nix wrong headers
ui: remove GNU GPL
ui: simplify condition
props changed only
main, ui: store config files in a predefined directory
pt: show extracted points' areas and success count
close dialogs on switch to new module
...
68 files changed, 2356 insertions, 1747 deletions
@@ -5,3 +5,6 @@ # merely git cola saves there /patches/ .DS_Store +*.obj +*.exp +*.pdb
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 303a3bb0..a902aaf5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,11 +37,7 @@ if(MSVC) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) endif() -set(my-qt-deps) -if(WIN32) # hack to avoid breakage on buildbot - set(my-qt-deps ws2_32) -endif() -set(MY_QT_LIBS ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Xml_LIBRARIES} ${my-qt-deps}) +set(MY_QT_LIBS ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Xml_LIBRARIES}) # note, hatire supports both ftnoir and opentrack # don't remove without being sure as hell -sh 20140922 @@ -132,6 +128,7 @@ macro(opentrack_library n dir) if(NOT foolib_STATIC) install(TARGETS ${n} RUNTIME DESTINATION . LIBRARY DESTINATION .) endif() + opentrack_compat(${n}) endmacro() function(link_with_dinput8 n) @@ -151,7 +148,11 @@ IF(WIN32) SET(SDK_CONSOLE_DEBUG FALSE CACHE BOOL "Console window visible at runtime") ENDIF() -IF("${CMAKE_SYSTEM}" MATCHES "Linux" OR APPLE) +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINUX TRUE) +endif() + +if(LINUX OR APPLE) set(SDK_XPLANE "" CACHE PATH "Path to X-Plane SDK") set(SDK_ENABLE_LIBEVDEV FALSE CACHE BOOL "libevdev virtual joystick protocol support") endif() @@ -410,8 +411,14 @@ target_link_libraries(opentrack ${OpenCV_LIBS}) link_with_dinput8(opentrack) target_link_libraries(opentrack ${MY_QT_LIBS}) -if(CMAKE_SYSTEM STREQUAL LINUX) - link_libraries(rt) +if(APPLE) + # for process detector + target_link_libraries(opentrack proc) +endif() + +if(LINUX) + # for process detector + target_link_libraries(opentrack procps) endif() # ---- @@ -448,6 +455,11 @@ if(WIN32) install(FILES "${CMAKE_SOURCE_DIR}/bin/cleye.config" DESTINATION .) endif() +if(WIN32) + install(FILES "${CMAKE_SOURCE_DIR}/ftnoir_tracker_rs/rs_impl/bin/opentrack-tracker-rs-impl.exe" DESTINATION . ${opentrack-perms}) + install(FILES "${CMAKE_SOURCE_DIR}/ftnoir_tracker_rs/redist/intel_rs_sdk_runtime_websetup_6.0.21.6598.exe" DESTINATION ./clientfiles/ ${opentrack-perms}) +endif() + if(APPLE) install(CODE " execute_process(COMMAND /bin/sh \"${CMAKE_SOURCE_DIR}/macosx/make-app-bundle.sh\" @@ -28,6 +28,7 @@ Not to be confused with railway planning software <<http://opentrack.ch>> - Joystick analog axes (Windows, Linux) - Windows Phone [tracker](http://www.windowsphone.com/en-us/store/app/opentrack-head-tracking/1c604f32-6d68-40ef-aa44-3163e30f547f) over opentrack UDP protocol - Arduino with custom firmware +- Intel RealSense 3D cameras (Windows) # Output @@ -60,6 +61,7 @@ Don't be afraid to submit an issue/feature request if need arises. - Ryan Spicer (OSX tester, contributor) - Patrick Ruoff (PT tracker) - Ulf Schreiber (PT tracker) +- Xavier Hallade (RealSense tracker) - furax49 (hatire tracker) - Andrzej Czarnowski (quality assurance) - uglyDwarf (high CON) diff --git a/bin/NPClient.dll b/bin/NPClient.dll Binary files differindex bf971c03..bf971c03 100755..100644 --- a/bin/NPClient.dll +++ b/bin/NPClient.dll diff --git a/bin/NPClient64.dll b/bin/NPClient64.dll Binary files differindex fd3164e5..fd3164e5 100755..100644 --- a/bin/NPClient64.dll +++ b/bin/NPClient64.dll diff --git a/bin/freetrackclient.dll b/bin/freetrackclient.dll Binary files differindex 3206558f..3206558f 100755..100644 --- a/bin/freetrackclient.dll +++ b/bin/freetrackclient.dll diff --git a/bin/settings/default.ini b/bin/settings/default.ini deleted file mode 100644 index e69de29b..00000000 --- a/bin/settings/default.ini +++ /dev/null diff --git a/bin/settings/facetracknoir supported games.csv b/bin/settings/facetracknoir supported games.csv index 98dba166..ad6f1393 100644 --- a/bin/settings/facetracknoir supported games.csv +++ b/bin/settings/facetracknoir supported games.csv @@ -66,6 +66,7 @@ No;Game Name;Game protocol;Supported since;Verified;By;INTERNATIONAL_ID;FTN_ID 57;Caterpillar Simulators;FreeTrack20;V160;;;23080;0039F52E4221742FA63100 58;CCG Metamedia;FreeTrack20;V160;;;20009;003A6FA8A8DD86FE8BDB00 547;CDF Ghostship;FreeTrack20;V160;;;4150;02230A04C45292CC5DA400 +568;CDF Starfighter;FreeTrack20;V160;;;4153;0238E7D9A14B2A1AE98900 59;CEWIT Immersive Cabin;FreeTrack20;V160;;;20370;003B90B5780C90078DEE00 60;Chopper;FreeTrack20;V160;;;3150;003CC166C8632A32EA2300 61;Clearbox;FreeTrack20;V160;;;20565;003D5C1FDE74279D1DB800 @@ -75,6 +76,7 @@ No;Game Name;Game protocol;Supported since;Verified;By;INTERNATIONAL_ID;FTN_ID 63;Colin McRae DiRT 2;FreeTrack20;V170;V;V4Friend;8107;003F7815C5379491C73300 64;Colin McRae Rally 04;FreeTrack20;V160;;;8103;0040D13C2A843DA526A610 65;Comanche;FreeTrack20;V160;;;2275;00414097C36A12BA32BA00 +569;Combat Air Patrol 2;FreeTrack20;V160;;;4425;02399AAA88F5062AB14500 66;Combat Flight Simulator 3;FreeTrack20;V160;V;V4Friend;2304;00420EC5F763AB21CA7310 67;Combat Helo;FreeTrack20;V160;;;2001;004329DFA863FAE1EA7300 515;Combined Arms;FreeTrack20;V160;;;1309;0203387EDB1A5AFEADB600 diff --git a/clientfiles/freepie-udp/com.freepie.android.imu.apk b/clientfiles/freepie-udp/com.freepie.android.imu.apk Binary files differindex 11934d67..b1f052aa 100644 --- a/clientfiles/freepie-udp/com.freepie.android.imu.apk +++ b/clientfiles/freepie-udp/com.freepie.android.imu.apk diff --git a/clientfiles/freepie-udp/license.txt b/clientfiles/freepie-udp/license.txt new file mode 100644 index 00000000..c40094f2 --- /dev/null +++ b/clientfiles/freepie-udp/license.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2012-2015 Anders Malmgren +Copyright (c) 2014-2015 Stanislaw Halik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. diff --git a/clientfiles/make-csv.pl b/clientfiles/make-csv.pl index ee60364e..ee60364e 100755..100644 --- a/clientfiles/make-csv.pl +++ b/clientfiles/make-csv.pl diff --git a/clientfiles/very-important-source-code/important-stuff/game_data.c b/clientfiles/very-important-source-code/important-stuff/game_data.c index f80a7d44..48774187 100644 --- a/clientfiles/very-important-source-code/important-stuff/game_data.c +++ b/clientfiles/very-important-source-code/important-stuff/game_data.c @@ -114,6 +114,20 @@ static void game_data_close() #define ltr_int_log_message(...) fprintf(stderr, __VA_ARGS__) +static void remove_newlines(const char* str, char* out, int out_len) +{ + int i, j; + int len = strlen(str); + for (i = 0, j = 0; str[i] && j + 1 < out_len; i++) + { + if (str[i] == '\r' || str[i] == '\n') + continue; + out[j++] = str[i]; + } + if (j < out_len) + out[j] = '\0'; +} + bool get_game_data(const char *input_fname, const char *output_fname, bool from_update) { FILE *outfile = NULL; @@ -129,19 +143,21 @@ bool get_game_data(const char *input_fname, const char *output_fname, bool from_ mxml_node_t *game; const char *name; const char *id; - for(game = mxmlFindElement(tree, tree, "Game", NULL, NULL, MXML_DESCEND); + for(game = mxmlFindElement(tree, tree, "Game", NULL, NULL, MXML_DESCEND); game != NULL; - game = mxmlFindElement(game, tree, "Game", NULL, NULL, MXML_DESCEND)){ + game = mxmlFindElement(game, tree, "Game", NULL, NULL, MXML_DESCEND)) + { name = mxmlElementGetAttr(game, "Name"); id = mxmlElementGetAttr(game, "Id"); - + mxml_node_t *appid = mxmlFindElement(game, game, "ApplicationID", NULL, NULL, MXML_DESCEND); - if(appid == NULL){ - fprintf(outfile, "%s \"%s\"\n", id, name); - }else{ - fprintf(outfile, "%s \"%s\" (%s)\n", id, name, appid->child->value.text.string); - } - } + char name_[256]; + remove_newlines(name, name_, sizeof(name_)); + if(appid == NULL) + fprintf(outfile, "%s \"%s\"\n", id, name_); + else + fprintf(outfile, "%s \"%s\" (%s)\n", id, name_, appid->child->value.text.string); + } fclose(outfile); game_data_close(); return true; diff --git a/cmake/mingw-w64.cmake b/cmake/mingw-w64.cmake index 80ecf0f5..f8ee1d41 100644 --- a/cmake/mingw-w64.cmake +++ b/cmake/mingw-w64.cmake @@ -25,11 +25,12 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(cpu "-O3 -march=i686 -mtune=corei7-avx -ffast-math -mfpmath=both -msse -msse2 -mno-sse3 -mno-avx") +# oldest CPU supported here is Northwood-based Pentium 4. -sh 20150811 +set(cpu "-O3 -march=pentium4 -mtune=corei7-avx -ffast-math -mfpmath=both -msse -msse2 -mno-sse3") set(CFLAGS-OVERRIDE "" CACHE STRING "") -set(CMAKE_C_FLAGS_RELEASE "${rice} ${cpu} ${CFLAGS-OVERRIDE}" CACHE STRING "" FORCE) +set(CMAKE_C_FLAGS_RELEASE "${cpu} ${CFLAGS-OVERRIDE}" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE} CACHE STRING "" FORCE) set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${cpu} ${CFLAGS-OVERRIDE}" CACHE STRING "" FORCE) set(CMAKE_EXE_LINKER_FLAGS_RELEASE ${CMAKE_SHARED_LINKER_FLAGS_RELEASE} CACHE STRING "" FORCE) diff --git a/csv/csv.cpp b/csv/csv.cpp index e55b429c..4c754542 100644 --- a/csv/csv.cpp +++ b/csv/csv.cpp @@ -122,14 +122,14 @@ void CSV::getGameData( const int id, unsigned char* table, QString& gamename) if (gameLine.count() > 6) { if (gameLine.at(6).compare( gameID, Qt::CaseInsensitive ) == 0) { QByteArray id = gameLine.at(7).toLatin1(); - unsigned int tmp[8]; - unsigned int fuzz[3]; + unsigned char tmp[8]; + unsigned char fuzz[3]; if (gameLine.at(3) == QString("V160")) { qDebug() << "no table"; } else if (sscanf(id.constData(), - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", fuzz + 2, fuzz + 0, tmp + 3, diff --git a/facetracknoir/curve-config.cpp b/facetracknoir/curve-config.cpp index e199d005..2e9065b4 100644 --- a/facetracknoir/curve-config.cpp +++ b/facetracknoir/curve-config.cpp @@ -20,23 +20,24 @@ MapWidget::MapWidget(Mappings& m, main_settings& s) : struct { QFunctionConfigurator* qfc; Axis axis; + QCheckBox* checkbox; bool altp; } qfcs[] = { - { ui.rxconfig, Yaw, false }, - { ui.ryconfig, Pitch, false}, - { ui.rzconfig, Roll, false }, - { ui.txconfig, TX, false }, - { ui.tyconfig, TY, false }, - { ui.tzconfig, TZ, false }, + { ui.rxconfig, Yaw, nullptr, false }, + { ui.ryconfig, Pitch, nullptr, false }, + { ui.rzconfig, Roll, nullptr, false }, + { ui.txconfig, TX, nullptr, false }, + { ui.tyconfig, TY, nullptr, false }, + { ui.tzconfig, TZ, nullptr, false }, - { ui.rxconfig_alt, Yaw, true }, - { ui.ryconfig_alt, Pitch, true}, - { ui.rzconfig_alt, Roll, true }, - { ui.txconfig_alt, TX, true }, - { ui.tyconfig_alt, TY, true }, - { ui.tzconfig_alt, TZ, true }, - { nullptr, Yaw, false } + { ui.rxconfig_alt, Yaw, ui.rx_altp, true }, + { ui.ryconfig_alt, Pitch, ui.ry_altp, true }, + { ui.rzconfig_alt, Roll, ui.rz_altp, true }, + { ui.txconfig_alt, TX, ui.tx_altp, true }, + { ui.tyconfig_alt, TY, ui.ty_altp, true }, + { ui.tzconfig_alt, TZ, ui.tz_altp, true }, + { nullptr, Yaw, nullptr, false } }; for (int i = 0; qfcs[i].qfc; i++) @@ -45,7 +46,14 @@ MapWidget::MapWidget(Mappings& m, main_settings& s) : Mapping& axis = m(qfcs[i].axis); Map* conf = altp ? &axis.curveAlt : &axis.curve; const auto& name = qfcs[i].altp ? axis.name2 : axis.name1; - + if (altp) + { + QFunctionConfigurator& qfc = *qfcs[i].qfc; + connect(qfcs[i].checkbox, &QCheckBox::toggled, + [&](bool f) -> void {qfc.setEnabled(f); qfc.force_redraw();}); + qfc.setEnabled(qfcs[i].checkbox->isChecked()); + qfc.force_redraw(); + } qfcs[i].qfc->setConfig(conf, name); } } diff --git a/facetracknoir/keyboard.h b/facetracknoir/keyboard.h index 3df295e1..3df295e1 100755..100644 --- a/facetracknoir/keyboard.h +++ b/facetracknoir/keyboard.h diff --git a/facetracknoir/main.ui b/facetracknoir/main.ui index 4d8b6caf..f852abff 100755..100644 --- a/facetracknoir/main.ui +++ b/facetracknoir/main.ui @@ -7,8 +7,8 @@ <rect> <x>0</x> <y>0</y> - <width>744</width> - <height>473</height> + <width>693</width> + <height>530</height> </rect> </property> <property name="windowIcon"> @@ -31,25 +31,19 @@ <number>0</number> </property> <property name="leftMargin"> - <number>6</number> + <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> - <number>6</number> + <number>0</number> </property> <property name="bottomMargin"> - <number>10</number> + <number>0</number> </property> <item> <widget class="QFrame" name="frame"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> @@ -59,7 +53,7 @@ <property name="lineWidth"> <number>0</number> </property> - <layout class="QGridLayout" name="gridLayout"> + <layout class="QGridLayout" name="gridLayout_5"> <property name="leftMargin"> <number>0</number> </property> @@ -75,15 +69,15 @@ <property name="spacing"> <number>0</number> </property> - <item row="0" column="0" colspan="2"> - <widget class="QFrame" name="frame_2"> + <item row="0" column="0"> + <widget class="QFrame" name="top"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <layout class="QGridLayout" name="gridLayout_2"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> <property name="leftMargin"> <number>0</number> </property> @@ -96,127 +90,436 @@ <property name="bottomMargin"> <number>0</number> </property> - <property name="spacing"> - <number>0</number> - </property> - <item row="1" column="0"> - <widget class="QFrame" name="frame_3"> + <item> + <widget class="QFrame" name="video_feed"> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>480</width> + <height>360</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>480</width> + <height>360</height> + </size> + </property> + <widget class="QFrame" name="video_frame"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>480</width> + <height>360</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <widget class="QLabel" name="video_frame_label"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>480</width> + <height>360</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>480</width> + <height>360</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>480</width> + <height>360</height> + </size> + </property> + <property name="text"> + <string/> + </property> + </widget> + </widget> + </widget> + </item> + <item> + <widget class="QFrame" name="top_display"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> + <property name="lineWidth"> + <number>0</number> </property> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <layout class="QVBoxLayout"> <property name="spacing"> - <number>9</number> + <number>12</number> </property> <property name="leftMargin"> - <number>0</number> + <number>6</number> </property> <property name="topMargin"> - <number>6</number> + <number>12</number> </property> <property name="rightMargin"> - <number>0</number> + <number>8</number> </property> <property name="bottomMargin"> - <number>0</number> + <number>4</number> </property> <item> - <widget class="QGroupBox" name="groupProfile"> + <widget class="GLWidget" name="pose_display" native="true"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>10</horstretch> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="title"> - <string>Profile</string> + <property name="maximumSize"> + <size> + <width>80</width> + <height>90</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="box_raw_headpose"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <property name="flat"> - <bool>true</bool> + <property name="title"> + <string notr="true">Raw tracker data</string> </property> - <layout class="QGridLayout" name="gridLayout_7"> + <layout class="QGridLayout" name="gridLayout_12"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> - <number>13</number> + <number>0</number> </property> <property name="rightMargin"> - <number>4</number> + <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <property name="horizontalSpacing"> - <number>8</number> + <number>3</number> </property> <property name="verticalSpacing"> - <number>7</number> + <number>2</number> </property> - <item row="0" column="0"> - <widget class="QComboBox" name="iconcomboProfile"> + <item row="0" column="1"> + <widget class="QLCDNumber" name="raw_x"> + <property name="enabled"> + <bool>true</bool> + </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>7</horstretch> - <verstretch>5</verstretch> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> </sizepolicy> </property> - <property name="maxVisibleItems"> - <number>10</number> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Outline</enum> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QToolButton" name="btnSave"> + <item row="0" column="2"> + <widget class="QLabel" name="lblRotX_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <property name="text"> + <string>yaw</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QLabel" name="lblRotZ_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <property name="text"> + <string>roll</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="lblZ_4"> <property name="enabled"> <bool>true</bool> </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>3</horstretch> - <verstretch>5</verstretch> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> </sizepolicy> </property> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> <property name="text"> - <string>Save</string> + <string>TZ</string> </property> </widget> </item> - <item row="1" column="0"> - <widget class="QToolButton" name="btnLoad"> + <item row="1" column="2"> + <widget class="QLabel" name="lblRotY_4"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>7</horstretch> - <verstretch>4</verstretch> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> </sizepolicy> </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> <property name="text"> - <string>Load</string> + <string>pitch</string> </property> </widget> </item> <item row="1" column="1"> - <widget class="QToolButton" name="btnSaveAs"> + <widget class="QLCDNumber" name="raw_y"> <property name="enabled"> <bool>true</bool> </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>3</horstretch> - <verstretch>4</verstretch> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Outline</enum> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QLCDNumber" name="raw_yaw"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Outline</enum> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QLCDNumber" name="raw_roll"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> </sizepolicy> </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Outline</enum> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLCDNumber" name="raw_z"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Outline</enum> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="lblX_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <property name="text"> + <string>TX</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QLCDNumber" name="raw_pitch"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Outline</enum> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="lblY_4"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> <property name="text"> - <string>Save...</string> + <string>TY</string> </property> </widget> </item> @@ -224,230 +527,291 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupStartStop"> + <widget class="QGroupBox" name="box_mapped_headpose"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> - <horstretch>7</horstretch> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> + <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="title"> - <string notr="true">Controls</string> + <string notr="true">Game data</string> </property> - <property name="flat"> - <bool>true</bool> - </property> - <layout class="QGridLayout" name="gridLayout_5"> + <layout class="QGridLayout" name="gridLayout_14"> <property name="leftMargin"> - <number>4</number> + <number>0</number> </property> <property name="topMargin"> - <number>14</number> + <number>0</number> </property> <property name="rightMargin"> - <number>4</number> + <number>0</number> </property> <property name="bottomMargin"> - <number>10</number> + <number>0</number> </property> <property name="horizontalSpacing"> - <number>6</number> + <number>3</number> </property> <property name="verticalSpacing"> - <number>0</number> + <number>2</number> </property> - <item row="0" column="1"> - <widget class="QToolButton" name="btnStopTracker"> + <item row="1" column="0"> + <widget class="QLabel" name="lblY_2"> <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="autoFillBackground"> <bool>false</bool> </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <property name="text"> + <string>TY</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QLabel" name="lblRotY_2"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>45</height> - </size> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>Stop</string> + <string>pitch</string> </property> </widget> </item> - <item row="0" column="0"> - <widget class="QToolButton" name="btnStartTracker"> + <item row="0" column="1"> + <widget class="QLCDNumber" name="pose_x"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Flat</enum> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="lblRotX_2"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>45</height> - </size> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> </property> <property name="text"> - <string>Start</string> + <string>yaw</string> </property> </widget> </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupWindows"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="styleSheet"> - <string notr="true">QGroupBox { border: 0; }</string> - </property> - <layout class="QGridLayout" name="gridLayout_10"> - <property name="bottomMargin"> - <number>4</number> - </property> - <item row="0" column="0"> - <widget class="QGroupBox" name="groupGameProtocol"> + <item row="2" column="2"> + <widget class="QLabel" name="lblRotZ_2"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="title"> - <string>Protocol</string> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <property name="text"> + <string>roll</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="lblZ_2"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <property name="flat"> + <property name="autoFillBackground"> <bool>false</bool> </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="spacing"> - <number>5</number> - </property> - <property name="leftMargin"> - <number>2</number> - </property> - <property name="topMargin"> - <number>18</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QComboBox" name="iconcomboProtocol"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnShowServerControls"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="flat" stdset="0"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <property name="text"> + <string>TZ</string> + </property> </widget> </item> - <item row="1" column="0"> - <widget class="QFrame" name="frame_7"> + <item row="1" column="1"> + <widget class="QLCDNumber" name="pose_y"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Flat</enum> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QLCDNumber" name="pose_roll"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Flat</enum> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QLCDNumber" name="pose_yaw"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Flat</enum> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLCDNumber" name="pose_z"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> + <enum>QFrame::NoFrame</enum> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Flat</enum> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="lblx"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <property name="spacing"> - <number>8</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>2</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QPushButton" name="btnEditCurves"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Mapping</string> - </property> - <property name="icon"> - <iconset resource="ui-res.qrc"> - <normaloff>:/images/curves.png</normaloff>:/images/curves.png</iconset> - </property> - <property name="iconSize"> - <size> - <width>80</width> - <height>24</height> - </size> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="btnShortcuts"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Options</string> - </property> - <property name="icon"> - <iconset resource="ui-res.qrc"> - <normaloff>:/images/tools.png</normaloff>:/images/tools.png</iconset> - </property> - <property name="iconSize"> - <size> - <width>80</width> - <height>24</height> - </size> - </property> - </widget> - </item> - </layout> + <property name="text"> + <string>TX</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QLCDNumber" name="pose_pitch"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="smallDecimalPoint"> + <bool>true</bool> + </property> + <property name="digitCount"> + <number>4</number> + </property> + <property name="segmentStyle"> + <enum>QLCDNumber::Flat</enum> + </property> </widget> </item> </layout> @@ -456,940 +820,282 @@ </layout> </widget> </item> - <item row="0" column="0"> - <widget class="QFrame" name="frame_6"> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QFrame" name="bottom_controls"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="leftMargin"> + <number>4</number> + </property> + <property name="topMargin"> + <number>8</number> + </property> + <property name="rightMargin"> + <number>10</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="horizontalSpacing"> + <number>4</number> + </property> + <property name="verticalSpacing"> + <number>0</number> + </property> + <item row="1" column="1"> + <widget class="QGroupBox" name="groupWindows"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>4</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Settings</string> </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> + <property name="flat"> + <bool>true</bool> </property> - <layout class="QGridLayout" name="gridLayout_9"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="spacing"> + <number>3</number> + </property> <property name="leftMargin"> - <number>0</number> + <number>4</number> </property> <property name="topMargin"> - <number>0</number> + <number>2</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> - <number>0</number> - </property> - <property name="horizontalSpacing"> <number>6</number> </property> - <item row="0" column="0"> - <widget class="QFrame" name="video_feed"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>480</width> - <height>360</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>480</width> - <height>360</height> - </size> + <item> + <widget class="QGroupBox" name="groupGameProtocol"> + <property name="title"> + <string>Protocol</string> </property> - <widget class="QFrame" name="video_frame"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>480</width> - <height>360</height> - </rect> + <layout class="QGridLayout" name="gridLayout_4"> + <property name="leftMargin"> + <number>4</number> </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>4</number> </property> - <widget class="QLabel" name="video_frame_label"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>480</width> - <height>360</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>480</width> - <height>360</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>480</width> - <height>360</height> - </size> - </property> - <property name="text"> - <string/> - </property> - </widget> - </widget> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="horizontalSpacing"> + <number>3</number> + </property> + <property name="verticalSpacing"> + <number>0</number> + </property> + <item row="0" column="0"> + <widget class="QComboBox" name="iconcomboProtocol"/> + </item> + <item row="0" column="1"> + <widget class="QToolButton" name="btnShowServerControls"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="flat" stdset="0"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> </widget> </item> - <item row="0" column="1"> - <widget class="QFrame" name="top_display_3"> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="lineWidth"> - <number>0</number> + <item> + <widget class="QFrame" name="groupProfile"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>2</verstretch> + </sizepolicy> </property> - <layout class="QVBoxLayout" name="_3"> + <layout class="QHBoxLayout" name="horizontalLayout"> <property name="spacing"> - <number>12</number> - </property> - <property name="leftMargin"> <number>0</number> </property> - <property name="topMargin"> + <property name="leftMargin"> <number>0</number> </property> <property name="rightMargin"> - <number>0</number> + <number>2</number> </property> <property name="bottomMargin"> - <number>0</number> + <number>2</number> </property> <item> - <widget class="GLWidget" name="pose_display" native="true"> + <widget class="QToolButton" name="profile_button"> + <property name="enabled"> + <bool>true</bool> + </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="minimumSize"> - <size> - <width>100</width> - <height>110</height> - </size> + <property name="text"> + <string>Profile</string> </property> - </widget> - </item> - <item> - <widget class="QGroupBox" name="box_raw_headpose_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <property name="popupMode"> + <enum>QToolButton::InstantPopup</enum> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="arrowType"> + <enum>Qt::DownArrow</enum> </property> - <property name="title"> - <string notr="true">Raw tracker data</string> - </property> - <layout class="QGridLayout" name="gridLayout_16"> - <property name="leftMargin"> - <number>4</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <property name="horizontalSpacing"> - <number>3</number> - </property> - <property name="verticalSpacing"> - <number>2</number> - </property> - <item row="0" column="1"> - <widget class="QLCDNumber" name="raw_x"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="lineWidth"> - <number>1</number> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Outline</enum> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QLabel" name="lblRotX_6"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>yaw</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="2" column="2"> - <widget class="QLabel" name="lblRotZ_6"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>roll</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="lblZ_6"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>Z</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QLabel" name="lblRotY_6"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>pitch</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLCDNumber" name="raw_y"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="lineWidth"> - <number>1</number> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Outline</enum> - </property> - </widget> - </item> - <item row="0" column="3"> - <widget class="QLCDNumber" name="raw_yaw"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="lineWidth"> - <number>1</number> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Outline</enum> - </property> - </widget> - </item> - <item row="2" column="3"> - <widget class="QLCDNumber" name="raw_roll"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="lineWidth"> - <number>1</number> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Outline</enum> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLCDNumber" name="raw_z"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="lineWidth"> - <number>1</number> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Outline</enum> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="lblX_6"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>X</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="1" column="3"> - <widget class="QLCDNumber" name="raw_pitch"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="lineWidth"> - <number>1</number> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Outline</enum> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="lblY_6"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>Y</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - </layout> </widget> </item> <item> - <widget class="QGroupBox" name="box_mapped_headpose_3"> + <widget class="QComboBox" name="iconcomboProfile"> <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="title"> - <string notr="true">Game data</string> - </property> - <layout class="QGridLayout" name="gridLayout_17"> - <property name="leftMargin"> - <number>4</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <property name="horizontalSpacing"> - <number>3</number> - </property> - <property name="verticalSpacing"> - <number>2</number> - </property> - <item row="1" column="0"> - <widget class="QLabel" name="lblY_7"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>Y</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QLabel" name="lblRotY_7"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>pitch</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLCDNumber" name="pose_x"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Flat</enum> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QLabel" name="lblRotX_7"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>yaw</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="2" column="2"> - <widget class="QLabel" name="lblRotZ_7"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>roll</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="lblZ_7"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>Z</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLCDNumber" name="pose_y"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Flat</enum> - </property> - </widget> - </item> - <item row="2" column="3"> - <widget class="QLCDNumber" name="pose_roll"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Flat</enum> - </property> - </widget> - </item> - <item row="0" column="3"> - <widget class="QLCDNumber" name="pose_yaw"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Flat</enum> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLCDNumber" name="pose_z"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>35</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Flat</enum> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="lblx_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <property name="text"> - <string>X</string> - </property> - <property name="margin"> - <number>6</number> - </property> - </widget> - </item> - <item row="1" column="3"> - <widget class="QLCDNumber" name="pose_pitch"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>5</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>88</width> - <height>34</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="smallDecimalPoint"> - <bool>true</bool> - </property> - <property name="digitCount"> - <number>4</number> - </property> - <property name="segmentStyle"> - <enum>QLCDNumber::Flat</enum> - </property> - </widget> - </item> - </layout> + <property name="maxVisibleItems"> + <number>20</number> + </property> </widget> </item> </layout> </widget> </item> + <item> + <widget class="QPushButton" name="btnEditCurves"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>3</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Mapping</string> + </property> + <property name="icon"> + <iconset resource="ui-res.qrc"> + <normaloff>:/images/curves.png</normaloff>:/images/curves.png</iconset> + </property> + <property name="iconSize"> + <size> + <width>80</width> + <height>24</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnShortcuts"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>3</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Options</string> + </property> + <property name="icon"> + <iconset resource="ui-res.qrc"> + <normaloff>:/images/tools.png</normaloff>:/images/tools.png</iconset> + </property> + <property name="iconSize"> + <size> + <width>80</width> + <height>24</height> + </size> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QGroupBox" name="groupStartStop"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>3</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string notr="true">Controls</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="spacing"> + <number>8</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>8</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <widget class="QToolButton" name="btnStartTracker"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Start</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnStopTracker"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Stop</string> + </property> + </widget> + </item> </layout> </widget> </item> @@ -1410,8 +1116,12 @@ </customwidget> </customwidgets> <tabstops> + <tabstop>btnStartTracker</tabstop> + <tabstop>btnStopTracker</tabstop> <tabstop>iconcomboProtocol</tabstop> <tabstop>btnShowServerControls</tabstop> + <tabstop>btnEditCurves</tabstop> + <tabstop>btnShortcuts</tabstop> </tabstops> <resources> <include location="ui-res.qrc"/> diff --git a/facetracknoir/new_config.ui b/facetracknoir/new_config.ui new file mode 100644 index 00000000..27dce0f8 --- /dev/null +++ b/facetracknoir/new_config.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UI_new_config</class> + <widget class="QDialog" name="UI_new_config"> + <property name="windowModality"> + <enum>Qt::ApplicationModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>269</width> + <height>67</height> + </rect> + </property> + <property name="windowTitle"> + <string>Config filename</string> + </property> + <property name="windowIcon"> + <iconset> + <normaloff>images/facetracknoir.png</normaloff>images/facetracknoir.png</iconset> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>New file name:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="1"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/facetracknoir/new_file_dialog.h b/facetracknoir/new_file_dialog.h new file mode 100644 index 00000000..3a35cf71 --- /dev/null +++ b/facetracknoir/new_file_dialog.h @@ -0,0 +1,50 @@ +#pragma once + +#include "ui_new_config.h" +#include "opentrack/options.hpp" +#include <QFile> +#include <QRegExp> +#include <QString> +#include <QMessageBox> + +class new_file_dialog : public QDialog +{ + Q_OBJECT +public: + new_file_dialog(QWidget* parent = 0) : QDialog(parent), ok(false) + { + ui.setupUi(this); + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(ok_clicked())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(cancel_clicked())); + setFixedSize(size()); + } + bool is_ok(QString& name_) + { + name_ = name; + return ok; + } +private: + Ui::UI_new_config ui; + bool ok; + QString name; +private slots: + void cancel_clicked() { close(); } + void ok_clicked() + { + QString text = ui.lineEdit->text(); + text = text.replace('/', ""); + text = text.replace('\\', ""); + if (text != "" && !text.endsWith(".ini")) + text += ".ini"; + if (text == "" || text == ".ini" || QFile(options::group::ini_directory() + "/" + text).exists()) + { + QMessageBox::warning(this, + "File exists", "This file already exists. Pick another name.", + QMessageBox::Ok, QMessageBox::NoButton); + return; + } + ok = true; + close(); + name = text; + } +}; diff --git a/facetracknoir/process_detector.cpp b/facetracknoir/process_detector.cpp index df38eb29..19611241 100644 --- a/facetracknoir/process_detector.cpp +++ b/facetracknoir/process_detector.cpp @@ -8,6 +8,7 @@ #include "process_detector.h" #include "facetracknoir/ui.h" +#include "opentrack-compat/process-list.hpp" #include <QList> #include <QFileDialog> #include <QComboBox> @@ -153,36 +154,6 @@ void process_detector::remove() ui.tableWidget->removeRow(r); } -#ifdef _WIN32 - -#include <windows.h> -#include <TlHelp32.h> - -static QStringList get_all_executable_names() -{ - QStringList ret; - HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (h == INVALID_HANDLE_VALUE) - return ret; - - PROCESSENTRY32 e; - e.dwSize = sizeof(e); - - if (Process32First(h, &e) != TRUE) - { - CloseHandle(h); - return ret; - } - - do { - ret.append(e.szExeFile); - } while (Process32Next(h, &e) == TRUE); - - CloseHandle(h); - - return ret; -} - bool process_detector_worker::should_stop() { if (last_exe_name == "") @@ -237,6 +208,3 @@ bool process_detector_worker::config_to_start(QString& str) return false; } - - -#endif diff --git a/facetracknoir/process_detector.h b/facetracknoir/process_detector.h index 792a941f..f6497c90 100644 --- a/facetracknoir/process_detector.h +++ b/facetracknoir/process_detector.h @@ -74,8 +74,6 @@ public slots: void browse(); }; -#ifdef _WIN32 - class process_detector_worker : QObject { Q_OBJECT @@ -86,18 +84,3 @@ public: bool should_stop(); }; -#else - -class process_detector_worker : QObject -{ - Q_OBJECT -public: - bool config_to_start(QString&) - { - return false; - } - bool should_stop() { return false; } - -}; - -#endif diff --git a/facetracknoir/ui.cpp b/facetracknoir/ui.cpp index 4f1d8c18..15fdd308 100644 --- a/facetracknoir/ui.cpp +++ b/facetracknoir/ui.cpp @@ -1,27 +1,3 @@ -/******************************************************************************* -* FaceTrackNoIR This program is a private project of the some enthusiastic -* gamers from Holland, who don't like to pay much for -* head-tracking. -* -* Copyright (C) 2011 Wim Vriend (Developing) -* Ron Hendriks (Researching and Testing) -* -* Homepage -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 3 of the License, or (at your -* option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -* more details. -* -* You should have received a copy of the GNU General Public License along -* with this program; if not, see <http://www.gnu.org/licenses/>. -*********************************************************************************/ - /* Copyright (c) 2013-2015, Stanislaw Halik <sthalik@misaki.pl> * Permission to use, copy, modify, and/or distribute this @@ -35,8 +11,9 @@ #include "opentrack/options.hpp" #include "ftnoir_tracker_pt/ftnoir_tracker_pt.h" #include "ftnoir_filter_accela/ftnoir_filter_accela.h" +#include "facetracknoir/new_file_dialog.h" #include <QFileDialog> -#include <QFileInfo> +#include <QDesktopServices> #ifndef _WIN32 # include <unistd.h> @@ -47,7 +24,8 @@ MainWindow::MainWindow() : pose_update_timer(this), kbd_quit(QKeySequence("Ctrl+Q"), this), - no_feed_pixmap(":/images/no-feed.png") + no_feed_pixmap(":/images/no-feed.png"), + is_refreshing_profiles(false) { ui.setupUi(this); @@ -56,43 +34,80 @@ MainWindow::MainWindow() : updateButtonState(false, false); ui.video_frame_label->setPixmap(no_feed_pixmap); - connect(ui.btnLoad, SIGNAL(clicked()), this, SLOT(open())); - connect(ui.btnSave, SIGNAL(clicked()), this, SLOT(save())); - connect(ui.btnSaveAs, SIGNAL(clicked()), this, SLOT(saveAs())); - connect(ui.btnEditCurves, SIGNAL(clicked()), this, SLOT(showCurveConfiguration())); connect(ui.btnShortcuts, SIGNAL(clicked()), this, SLOT(showKeyboardShortcuts())); + connect(ui.btnShortcuts, SIGNAL(clicked()), this, SLOT(show_options_dialog())); connect(ui.btnShowServerControls, SIGNAL(clicked()), this, SLOT(showProtocolSettings())); for (auto x : modules.protocols()) ui.iconcomboProtocol->addItem(x->icon, x->name); - fill_profile_combobox(); + refresh_config_list(); + connect(&config_list_timer, SIGNAL(timeout()), this, SLOT(refresh_config_list())); + config_list_timer.start(1000 * 3); tie_setting(s.protocol_dll, ui.iconcomboProtocol); + connect(ui.iconcomboProtocol, + &QComboBox::currentTextChanged, + [&](QString) -> void { if (pProtocolDialog) pProtocolDialog = nullptr; save(); }); + connect(ui.btnStartTracker, SIGNAL(clicked()), this, SLOT(startTracker())); connect(ui.btnStopTracker, SIGNAL(clicked()), this, SLOT(stopTracker())); - connect(ui.iconcomboProfile, SIGNAL(currentIndexChanged(int)), this, SLOT(profileSelected(int))); + connect(ui.iconcomboProfile, SIGNAL(currentTextChanged(QString)), this, SLOT(profileSelected(QString))); connect(&pose_update_timer, SIGNAL(timeout()), this, SLOT(showHeadPose())); connect(&kbd_quit, SIGNAL(activated()), this, SLOT(exit())); + + save_timer.setSingleShot(true); + connect(&save_timer, SIGNAL(timeout()), this, SLOT(_save())); + + profile_menu.addAction("Create new empty config", this, SLOT(make_empty_config())); + profile_menu.addAction("Create new copied config", this, SLOT(make_copied_config())); + profile_menu.addAction("Open configuration directory", this, SLOT(open_config_directory())); + ui.profile_button->setMenu(&profile_menu); + kbd_quit.setEnabled(true); connect(&det_timer, SIGNAL(timeout()), this, SLOT(maybe_start_profile_from_executable())); det_timer.start(1000); ensure_tray(); - set_working_directory(); + + if (!QFile(group::ini_pathname()).exists()) + { + set_profile(OPENTRACK_DEFAULT_CONFIG); + const auto pathname = group::ini_pathname(); + if (!QFile(pathname).exists()) + { + QFile file(pathname); + (void) file.open(QFile::ReadWrite); + } + } + + if (group::ini_directory() == "") + QMessageBox::warning(this, + "Configuration not saved.", + "Can't create configuration directory! Expect major malfunction.", + QMessageBox::Ok, QMessageBox::NoButton); +} + +bool MainWindow::get_new_config_name_from_dialog(QString& ret) +{ + new_file_dialog dlg; + dlg.exec(); + return dlg.is_ok(ret); } MainWindow::~MainWindow() { + save_timer.stop(); + if (tray) tray->hide(); stopTracker(); - save(); + maybe_save(); } void MainWindow::set_working_directory() @@ -100,32 +115,26 @@ void MainWindow::set_working_directory() QDir::setCurrent(QCoreApplication::applicationDirPath()); } -void MainWindow::open() { - QFileDialog dialog(this); - dialog.setFileMode(QFileDialog::ExistingFile); - QString dir_path = QFileInfo(group::ini_pathname()).absolutePath(); - QString fileName = dialog.getOpenFileName( - this, - tr("Open the settings file"), - dir_path, - tr("Settings file (*.ini);;All Files (*)")); - set_working_directory(); +void MainWindow::save_mappings() { + pose.save_mappings(); +} - if (!fileName.isEmpty()) { - { - QSettings settings(OPENTRACK_ORG); - settings.setValue(OPENTRACK_CONFIG_FILENAME_KEY, remove_app_path(fileName)); - } - fill_profile_combobox(); - load_settings(); - } +void MainWindow::save() +{ + save_timer.stop(); + save_timer.start(5000); } -void MainWindow::save_mappings() { - pose.save_mappings(); +void MainWindow::maybe_save() +{ + if (save_timer.isActive()) + { + save_timer.stop(); + _save(); + } } -void MainWindow::save() { +void MainWindow::_save() { s.b->save(); save_mappings(); mem<QSettings> settings = group::ini_file(); @@ -143,29 +152,6 @@ void MainWindow::save() { #endif } -void MainWindow::saveAs() -{ - QString oldFile = group::ini_pathname(); - QString fileName = QFileDialog::getSaveFileName(this, tr("Save file"), - oldFile, - tr("Settings file (*.ini);;All Files (*)")); - set_working_directory(); - - if (fileName.isEmpty()) - return; - - (void) QFile::remove(fileName); - - { - (void) QFile::copy(oldFile, fileName); - QSettings settings(OPENTRACK_ORG); - settings.setValue (OPENTRACK_CONFIG_FILENAME_KEY, remove_app_path(fileName)); - } - - save(); - fill_profile_combobox(); -} - void MainWindow::load_mappings() { pose.load_mappings(); } @@ -175,16 +161,64 @@ void MainWindow::load_settings() { load_mappings(); } +void MainWindow::make_empty_config() +{ + QString name; + const QString dir = group::ini_directory(); + if (dir != "" && get_new_config_name_from_dialog(name)) + { + QFile filename(dir + "/" + name); + (void) filename.open(QFile::ReadWrite); + refresh_config_list(); + ui.iconcomboProfile->setCurrentText(name); + } +} + +void MainWindow::make_copied_config() +{ + const QString dir = group::ini_directory(); + const QString cur = group::ini_pathname(); + QString name; + if (cur != "" && dir != "" && get_new_config_name_from_dialog(name)) + { + const QString new_name = dir + "/" + name; + (void) QFile::remove(new_name); + (void) QFile::copy(cur, new_name); + refresh_config_list(); + ui.iconcomboProfile->setCurrentText(name); + } +} + +void MainWindow::open_config_directory() +{ + const QString path = group::ini_directory(); + if (path != "") + { + QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(path)); + } +} + extern "C" volatile const char* opentrack_version; -void MainWindow::fill_profile_combobox() +void MainWindow::refresh_config_list() { + if (work) + return; + + if (group::ini_list().size() == 0) + { + QFile filename(group::ini_directory() + "/" OPENTRACK_DEFAULT_CONFIG); + (void) filename.open(QFile::ReadWrite); + } + QStringList ini_list = group::ini_list(); set_title(); - QString current = QFileInfo(group::ini_pathname()).fileName(); + QString current = group::ini_filename(); + is_refreshing_profiles = true; ui.iconcomboProfile->clear(); for (auto x : ini_list) ui.iconcomboProfile->addItem(QIcon(":/images/settings16.png"), x); + is_refreshing_profiles = false; ui.iconcomboProfile->setCurrentText(current); } @@ -196,11 +230,10 @@ void MainWindow::updateButtonState(bool running, bool inertialp) ui.btnStopTracker->setEnabled ( running ); ui.iconcomboProtocol->setEnabled ( not_running ); ui.video_frame_label->setVisible(not_running || inertialp); - ui.btnSaveAs->setEnabled(not_running); - ui.btnLoad->setEnabled(not_running); + ui.profile_button->setEnabled(not_running); } -void MainWindow::bindKeyboardShortcuts() +void MainWindow::reload_options() { if (work) work->reload_shortcuts(); @@ -208,9 +241,6 @@ void MainWindow::bindKeyboardShortcuts() } void MainWindow::startTracker() { - s.b->save(); - load_settings(); - // tracker dtor needs run first work = nullptr; @@ -233,8 +263,8 @@ void MainWindow::startTracker() { work = std::make_shared<Work>(s, pose, libs, this, winId()); - bindKeyboardShortcuts(); - + reload_options(); + if (pProtocolDialog) pProtocolDialog->register_protocol(libs.pProtocol.get()); @@ -244,6 +274,8 @@ void MainWindow::startTracker() { // trackers take care of layout state updates const bool is_inertial = ui.video_frame->layout() == nullptr; updateButtonState(true, is_inertial); + + maybe_save(); } void MainWindow::stopTracker( ) { @@ -253,10 +285,7 @@ void MainWindow::stopTracker( ) { ui.pose_display->rotateBy(0, 0, 0, 0, 0, 0); if (pProtocolDialog) - { pProtocolDialog->unregister_protocol(); - pProtocolDialog = nullptr; - } work = nullptr; libs = SelectedLibraries(); @@ -311,7 +340,7 @@ void MainWindow::set_title(const QString& game_title_) QString game_title; if (game_title_ != "") game_title = " :: " + game_title_; - QString current = QFileInfo(group::ini_pathname()).fileName(); + QString current = group::ini_filename(); setWindowTitle(QStringLiteral("TrackHat ") + const_cast<const char*>(opentrack_version) + QStringLiteral(" :: ") + current + game_title); } @@ -322,14 +351,6 @@ void MainWindow::showHeadPose() work->tracker->get_raw_and_mapped_poses(mapped, raw); display_pose(mapped, raw); - -#if 0 - if (libs.pProtocol) - { - const QString name = libs.pProtocol->game_name(); - ui.game_name->setText(name); - } -#endif } template<typename t> @@ -380,9 +401,9 @@ bool mk_window(mem<t>* place, Args... params) } } -void MainWindow::showKeyboardShortcuts() { - if (mk_window<OptionsDialog, State&>(&shortcuts_widget, *this)) - connect(shortcuts_widget.get(), SIGNAL(reload()), this, SLOT(bindKeyboardShortcuts())); +void MainWindow::show_options_dialog() { + if (mk_window<OptionsDialog, State&>(&options_widget, static_cast<State&>(*this))) + connect(options_widget.get(), SIGNAL(reload()), this, SLOT(reload_options())); } void MainWindow::showCurveConfiguration() { @@ -393,40 +414,26 @@ void MainWindow::exit() { QCoreApplication::exit(0); } -QString MainWindow::remove_app_path(const QString full_path) -{ - QFileInfo path_info(full_path); - QString path = path_info.absolutePath(); - - QFileInfo app_path(QCoreApplication::applicationDirPath()); - QString app_prefix(app_path.absoluteFilePath()); - - if (path == app_prefix) - { - path = "."; - } - else if (path.startsWith(app_prefix + "/")) - { - path = "./" + path.mid(app_prefix.size() + 1); - } - - return path + "/" + path_info.fileName(); -} - -void MainWindow::profileSelected(int index) +void MainWindow::profileSelected(QString name) { - if (index == -1) + if (name == "" || is_refreshing_profiles) return; - + + const auto old_name = group::ini_filename(); + const auto new_name = name; + + if (old_name != new_name) { - QSettings settings(OPENTRACK_ORG); - settings.setValue (OPENTRACK_CONFIG_FILENAME_KEY, remove_app_path(QFileInfo(group::ini_pathname()).absolutePath() + "/" + - ui.iconcomboProfile->itemText(index))); - } + save(); - set_title(); + { + QSettings settings(OPENTRACK_ORG); + settings.setValue (OPENTRACK_CONFIG_FILENAME_KEY, new_name); + } - load_settings(); + set_title(); + load_settings(); + } } void MainWindow::shortcutRecentered() @@ -503,5 +510,5 @@ void MainWindow::maybe_start_profile_from_executable() void MainWindow::set_profile(const QString &profile) { QSettings settings(OPENTRACK_ORG); - settings.setValue(OPENTRACK_CONFIG_FILENAME_KEY, MainWindow::remove_app_path(profile)); + settings.setValue(OPENTRACK_CONFIG_FILENAME_KEY, profile); } diff --git a/facetracknoir/ui.h b/facetracknoir/ui.h index 2253ec80..1abd92ff 100644 --- a/facetracknoir/ui.h +++ b/facetracknoir/ui.h @@ -1,27 +1,3 @@ -/******************************************************************************* -* MainWindow This program is a private project of the some enthusiastic -* gamers from Holland, who don't like to pay much for -* head-tracking. -* -* Copyright (C) 2010 Wim Vriend (Developing) -* Ron Hendriks (Researching and Testing) -* -* Homepage -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 3 of the License, or (at your -* option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -* more details. -* -* You should have received a copy of the GNU General Public License along -* with this program; if not, see <http://www.gnu.org/licenses/>. -*********************************************************************************/ - /* Copyright (c) 2014-2015, Stanislaw Halik <sthalik@misaki.pl> * Permission to use, copy, modify, and/or distribute this @@ -39,12 +15,7 @@ #include <QTimer> #include <QSystemTrayIcon> #include <QString> - -#if !defined(_WIN32) -# include "qxt-mini/QxtGlobalShortcut" -#else -# include <windows.h> -#endif +#include <QMenu> #include "ui_main.h" @@ -69,12 +40,16 @@ class MainWindow : public QMainWindow, private State mem<QSystemTrayIcon> tray; QTimer pose_update_timer; QTimer det_timer; - mem<OptionsDialog> shortcuts_widget; + QTimer config_list_timer; + mem<OptionsDialog> options_widget; mem<MapWidget> mapping_widget; QShortcut kbd_quit; QPixmap no_feed_pixmap; mem<IProtocolDialog> pProtocolDialog; process_detector_worker det; + QMenu profile_menu; + bool is_refreshing_profiles; + QTimer save_timer; mem<dylib> current_protocol() { @@ -83,41 +58,44 @@ class MainWindow : public QMainWindow, private State void changeEvent(QEvent* e) override; - void createIconGroupBox(); void load_settings(); void updateButtonState(bool running, bool inertialp); - void fill_profile_combobox(); void display_pose(const double* mapped, const double* raw); void ensure_tray(); void set_title(const QString& game_title = QStringLiteral("")); -public slots: - void shortcutRecentered(); - void shortcutToggled(); - void shortcutZeroed(); - void bindKeyboardShortcuts(); + static bool get_new_config_name_from_dialog(QString &ret); + void set_profile(const QString& profile); + void maybe_save(); private slots: - void open(); + void _save(); void save(); - void saveAs(); void exit(); - void profileSelected(int index); + void profileSelected(QString name); void showProtocolSettings(); - void showKeyboardShortcuts(); + void show_options_dialog(); void showCurveConfiguration(); void showHeadPose(); void restore_from_tray(QSystemTrayIcon::ActivationReason); void maybe_start_profile_from_executable(); -public slots: + + void make_empty_config(); + void make_copied_config(); + void open_config_directory(); + void refresh_config_list(); + void startTracker(); void stopTracker(); + void reload_options(); +public slots: + void shortcutRecentered(); + void shortcutToggled(); + void shortcutZeroed(); public: MainWindow(); ~MainWindow(); void save_mappings(); void load_mappings(); - static QString remove_app_path(const QString full_path); static void set_working_directory(); - static void set_profile(const QString& profile); }; diff --git a/ftnoir_filter_accela/ftnoir_filter_accela.cpp b/ftnoir_filter_accela/ftnoir_filter_accela.cpp index 95b90a46..058599a3 100644 --- a/ftnoir_filter_accela/ftnoir_filter_accela.cpp +++ b/ftnoir_filter_accela/ftnoir_filter_accela.cpp @@ -12,25 +12,24 @@ #include "opentrack/plugin-api.hpp" static constexpr double rot_gains[][2] = { - { 2.66, 110 }, - { 2.0, 50 }, - { 1.66, 29 }, - { 1.33, 15 }, - { 1, 6 }, - { .66, 1.4 }, + { 2.66, 105 }, + { 2.0, 47 }, + { 1.66, 25 }, + { 1.33, 12 }, + { 1, 3.5 }, + { .66, 1 }, { .33, .4 }, { 0, 0 }, { -1, 0 } }; static constexpr double trans_gains[][2] = { - { 2.33, 400 }, - { 2, 150 }, - { 1.66, 60 }, - { 1.33, 20 }, - { 1, 2 }, - { .66, .6 }, - { .33, .2 }, + { 2, 400 }, + { 1.66, 120 }, + { 1.33, 40 }, + { 1, 10 }, + { .66, 2 }, + { .33, .6 }, { 0, 0 }, { -1, 0 } }; diff --git a/ftnoir_filter_kalman/ftnoir_filter_kalman.h b/ftnoir_filter_kalman/ftnoir_filter_kalman.h index a6f40bb7..a6f40bb7 100755..100644 --- a/ftnoir_filter_kalman/ftnoir_filter_kalman.h +++ b/ftnoir_filter_kalman/ftnoir_filter_kalman.h diff --git a/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp index a7bb4a39..486224c9 100644 --- a/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp +++ b/ftnoir_protocol_fsuipc/ftnoir_protocol_fsuipc.cpp @@ -39,7 +39,8 @@ FTNoIR_Protocol::FTNoIR_Protocol() FTNoIR_Protocol::~FTNoIR_Protocol() { - FSUIPCLib.unload(); + FSUIPC_Close(); + FSUIPCLib.unload(); } int FTNoIR_Protocol::scale2AnalogLimits( float x, float min_x, float max_x ) { @@ -126,6 +127,9 @@ void FTNoIR_Protocol::pose(const double *headpose ) { // FSUIPC_Process(&result); if (result == FSUIPC_ERR_SENDMSG) { + // FSUIPC checks for already open connections and returns FSUIPC_ERR_OPEN in that case + // the connection scope is global for the process. this is why above code doesn't + // leak resources or have logic errors. see: http://www.purebasic.fr/english/viewtopic.php?t=31112 FSUIPC_Close(); //timeout (1 second) so assume FS closed } } diff --git a/ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp b/ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp index 08ddd3b2..570c2e0d 100644 --- a/ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp +++ b/ftnoir_tracker_aruco/ftnoir_tracker_aruco.cpp @@ -134,6 +134,7 @@ void Tracker::run() cv::Vec3d rvec, tvec; cv::Mat intrinsics = cv::Mat::eye(3, 3, CV_32FC1); cv::Mat dist_coeffs = cv::Mat::zeros(5, 1, CV_32FC1); + bool otsu = false; while (!stop) { @@ -144,18 +145,20 @@ void Tracker::run() if (!camera.read(color)) continue; } + static constexpr int thres_param2 = 5; cv::Mat grayscale; cv::cvtColor(color, grayscale, cv::COLOR_RGB2GRAY); const int scale = grayscale.cols > 480 ? 2 : 1; - detector.setThresholdParams(box_sizes[box_idx], 5); + detector.setThresholdParams(box_sizes[box_idx], thres_param2); static constexpr double pi = 3.1415926f; const int w = grayscale.cols, h = grayscale.rows; - const double diag = sqrt(w * w + h * h)/w, diag_fov = static_cast<int>(s.fov) * pi / 180.; - const double fov = 2.*atan(tan(diag_fov/2.)/sqrt(1. + diag*diag)); - const float focal_length_w = .5 * w / tan(.5 * fov); - const float focal_length_h = focal_length_w; + const double diag_fov = static_cast<int>(s.fov) * pi / 180.; + const double fov_w = 2.*atan(tan(diag_fov/2.)/sqrt(1. + h/(double)w * h/(double)w)); + const double fov_h = 2.*atan(tan(diag_fov/2.)/sqrt(1. + w/(double)h * w/(double)h)); + const float focal_length_w = .5 * w / tan(.5 * fov_w); + const float focal_length_h = .5 * h / tan(.5 * fov_h); intrinsics.at<float> (0, 0) = focal_length_w; intrinsics.at<float> (1, 1) = focal_length_h; @@ -164,8 +167,8 @@ void Tracker::run() std::vector< aruco::Marker > markers; - const double size_min = 0.02; - const double size_max = 0.4; + const double size_min = 0.05; + const double size_max = 0.3; bool roi_valid = false; @@ -177,11 +180,12 @@ void Tracker::run() if (last_roi.width > 0 && last_roi.height) { - detector.setThresholdParams(box_sizes[box_idx], 5); + detector.setThresholdParams(box_sizes[box_idx], thres_param2); detector.setMinMaxSize(std::max(0.01, size_min * grayscale.cols / last_roi.width), std::min(1.0, size_max * grayscale.cols / last_roi.width)); - if (detector.detect(grayscale(last_roi), markers, cv::Mat(), cv::Mat(), -1, false), + cv::Mat grayscale_ = grayscale(last_roi).clone(); + if (detector.detect(grayscale_, markers, cv::Mat(), cv::Mat(), -1, false), markers.size() == 1 && markers[0].size() == 4) { failed = std::max(0., failed - dt); @@ -198,6 +202,8 @@ void Tracker::run() if (!roi_valid) { + otsu = !otsu; + detector._thresMethod = otsu ? aruco::MarkerDetector::FIXED_THRES : aruco::MarkerDetector::ADPT_THRES; failed += dt; if (failed > max_failed) { @@ -206,7 +212,7 @@ void Tracker::run() qDebug() << "aruco: box size now" << box_sizes[box_idx]; failed = 0; } - detector.setThresholdParams(box_sizes[box_idx], 5); + detector.setThresholdParams(box_sizes[box_idx], thres_param2); detector.setMinMaxSize(size_min, size_max); detector.detect(grayscale, markers, cv::Mat(), cv::Mat(), -1, false); } diff --git a/ftnoir_tracker_aruco/include/markerdetector.h b/ftnoir_tracker_aruco/include/markerdetector.h index ac120b18..8a7e75ca 100644 --- a/ftnoir_tracker_aruco/include/markerdetector.h +++ b/ftnoir_tracker_aruco/include/markerdetector.h @@ -277,6 +277,7 @@ private: * This function returns in candidates all the rectangles found in a thresolded image */ void detectRectangles(const cv::Mat &thresImg,vector<MarkerCandidate> & candidates); +public: //Current threshold method ThresholdMethods _thresMethod; //Threshold parameters @@ -297,7 +298,7 @@ private: cv::Mat grey,thres,thres2,reduced; //pointer to the function that analizes a rectangular region so as to detect its internal marker int (* markerIdDetector_ptrfunc)(const cv::Mat &in,int &nRotations); - +private: /** */ bool isInto(cv::Mat &contour,std::vector<cv::Point2f> &b); diff --git a/ftnoir_tracker_freepie-udp/ftnoir_tracker_freepie-udp.cpp b/ftnoir_tracker_freepie-udp/ftnoir_tracker_freepie-udp.cpp index 1525e3c4..12cf9bca 100644 --- a/ftnoir_tracker_freepie-udp/ftnoir_tracker_freepie-udp.cpp +++ b/ftnoir_tracker_freepie-udp/ftnoir_tracker_freepie-udp.cpp @@ -104,6 +104,7 @@ void TrackerImpl::run() { void TrackerImpl::start_tracker(QFrame*) { start(); + sock.moveToThread(this); } void TrackerImpl::data(double *data) diff --git a/ftnoir_tracker_hatire/ftnoir_tracker_hat_settings.cpp b/ftnoir_tracker_hatire/ftnoir_tracker_hat_settings.cpp index 80543e7a..0a4454e4 100644 --- a/ftnoir_tracker_hatire/ftnoir_tracker_hat_settings.cpp +++ b/ftnoir_tracker_hatire/ftnoir_tracker_hat_settings.cpp @@ -35,15 +35,11 @@ void TrackerSettings::load_ini() { -#ifdef OPENTRACK_API +#ifndef OPENTRACK_API QSettings settings(OPENTRACK_ORG); // Registry settings (in HK_USER) + QString currentFile = settings.value( "SettingsFile", QCoreApplication::applicationDirPath() + "/Settings/default.ini" ).toString(); #else - QSettings settings("opentrack"); // Registry settings (in HK_USER) -#endif -#ifdef OPENTRACK_API - QString currentFile = settings.value( OPENTRACK_CONFIG_FILENAME_KEY, QCoreApplication::applicationDirPath() + OPENTRACK_DEFAULT_CONFIG_PATH ).toString(); -#else - QString currentFile = settings.value( "SettingsFile", QCoreApplication::applicationDirPath() + "/Settings/default.ini" ).toString(); + QString currentFile = options::group::ini_pathname(); #endif QSettings iniFile( currentFile, QSettings::IniFormat ); // Application settings (in INI-file) @@ -101,19 +97,14 @@ void TrackerSettings::load_ini() iniFile.endGroup(); } - void TrackerSettings::save_ini() const { -#ifdef OPENTRACK_API +#ifndef OPENTRACK_API QSettings settings(OPENTRACK_ORG); // Registry settings (in HK_USER) + QString currentFile = settings.value( "SettingsFile", QCoreApplication::applicationDirPath() + "/Settings/default.ini" ).toString(); #else - QSettings settings("opentrack"); // Registry settings (in HK_USER) -#endif -#ifdef OPENTRACK_API - QString currentFile = settings.value( OPENTRACK_CONFIG_FILENAME_KEY, QCoreApplication::applicationDirPath() + OPENTRACK_DEFAULT_CONFIG_PATH ).toString(); -#else - QString currentFile = settings.value( "SettingsFile", QCoreApplication::applicationDirPath() + "/Settings/default.ini" ).toString(); + QString currentFile = options::group::ini_pathname(); #endif QSettings iniFile( currentFile, QSettings::IniFormat ); // Application settings (in INI-file) diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp index 45cf2f10..77fd7241 100644 --- a/ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp +++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.cpp @@ -82,6 +82,17 @@ static BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance if (stop) { + if (self->guid_to_check.size()) + { + QString guid = guid_to_string(pdidInstance->guidInstance); + if (guid != self->guid_to_check) + { + return DIENUM_CONTINUE; + } + else + qDebug() << "guid ok" << self->guid_to_check; + } + (void) self->g_pDI->CreateDevice( pdidInstance->guidInstance, &self->g_pJoystick, NULL); qDebug() << "device" << static_cast<QString>(self->s.joyid); } @@ -103,15 +114,31 @@ void FTNoIR_Tracker::start_tracker(QFrame* frame) goto fail; } + guid_to_check = s.guid; + if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, this, DIEDFL_ATTACHEDONLY))) { - qDebug() << "enum2"; + qDebug() << "enum1"; goto fail; } + if (!g_pJoystick && guid_to_check.size()) + { + guid_to_check = ""; + + if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, + EnumJoysticksCallback, + this, + DIEDFL_ATTACHEDONLY))) + { + qDebug() << "enum2"; + goto fail; + } + } + if (!g_pJoystick) { qDebug() << "ENODEV"; diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick.h b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.h index 0958246a..75305f07 100644 --- a/ftnoir_tracker_joystick/ftnoir_tracker_joystick.h +++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick.h @@ -14,6 +14,7 @@ #include <QList> #include <QMutex> #include <QFrame> +#include <QStringList> #include <cmath> #include "opentrack/plugin-api.hpp" #ifndef DIRECTINPUT_VERSION @@ -30,11 +31,12 @@ using namespace options; struct settings : opts { - value<QString> joyid; + value<QString> joyid, guid; value<int> joy_1, joy_2, joy_3, joy_4, joy_5, joy_6; settings() : opts("tracker-joystick"), joyid(b, "joy-id", ""), + guid(b, "joy-guid", ""), joy_1(b, "axis-map-1", 1), joy_2(b, "axis-map-2", 2), joy_3(b, "axis-map-3", 3), @@ -44,6 +46,18 @@ struct settings : opts { {} }; +template<typename = void> +QString guid_to_string(const GUID guid) +{ + char buf[40] = {0}; + wchar_t szGuidW[40] = {0}; + + StringFromGUID2(guid, szGuidW, 40); + WideCharToMultiByte(0, 0, szGuidW, -1, buf, 40, NULL, NULL); + + return QString(buf); +} + class FTNoIR_Tracker : public ITracker { public: @@ -59,6 +73,7 @@ public: DIDEVICEINSTANCE def; int iter; // XXX bad style settings s; + QString guid_to_check; static constexpr int AXIS_MAX = 65535; }; @@ -69,10 +84,14 @@ public: TrackerControls(); void register_tracker(ITracker *) {} void unregister_tracker() {} - QList<GUID> guids; Ui::UIJoystickControls ui; FTNoIR_Tracker* tracker; settings s; + struct joys { + QString name; + QString guid; + }; + QList<joys> _joys; private slots: void doOK(); void doCancel(); diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui index 3533f93d..88737023 100644 --- a/ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui +++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_controls.ui @@ -9,7 +9,7 @@ <rect> <x>0</x> <y>0</y> - <width>251</width> + <width>498</width> <height>303</height> </rect> </property> @@ -79,6 +79,12 @@ <layout class="QGridLayout" name="gridLayout"> <item row="0" column="1"> <widget class="QComboBox" name="joy_1"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>1</number> </property> @@ -131,6 +137,12 @@ </item> <item row="1" column="1"> <widget class="QComboBox" name="joy_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>2</number> </property> @@ -183,6 +195,12 @@ </item> <item row="2" column="1"> <widget class="QComboBox" name="joy_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>3</number> </property> @@ -235,6 +253,12 @@ </item> <item row="3" column="1"> <widget class="QComboBox" name="joy_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>4</number> </property> @@ -287,6 +311,12 @@ </item> <item row="4" column="1"> <widget class="QComboBox" name="joy_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>5</number> </property> @@ -339,6 +369,12 @@ </item> <item row="5" column="1"> <widget class="QComboBox" name="joy_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>6</number> </property> diff --git a/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp index 7200003c..0463933f 100644 --- a/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp +++ b/ftnoir_tracker_joystick/ftnoir_tracker_joystick_dialog.cpp @@ -4,9 +4,12 @@ static BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ) { auto self = ( TrackerControls* )pContext; + auto name = QString(pdidInstance->tszInstanceName); + auto guid = guid_to_string(pdidInstance->guidInstance); + TrackerControls::joys cur { name, guid }; + self->_joys.append(cur); - self->guids.push_back(pdidInstance->guidInstance); - self->ui.joylist->addItem(QString(pdidInstance->tszInstanceName)); + self->ui.joylist->addItem(name + " " + guid); return DIENUM_CONTINUE; } @@ -38,8 +41,6 @@ fin: g_pDI->Release(); } - tie_setting(s.joyid, ui.joylist); - tie_setting(s.joy_1, ui.joy_1); tie_setting(s.joy_2, ui.joy_2); tie_setting(s.joy_3, ui.joy_3); @@ -49,6 +50,11 @@ fin: } void TrackerControls::doOK() { + int idx = ui.joylist->currentIndex(); + joys def { "", "" }; + auto val = _joys.value(idx, def); + s.guid = val.guid; + s.joyid = val.name; s.b->save(); if (tracker) tracker->reload(); diff --git a/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp b/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp index 9cfd31eb..04f1b8d9 100644 --- a/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp +++ b/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp @@ -63,15 +63,15 @@ bool Tracker_PT::get_focal_length(float& ret) break; } - const float diag_fov = static_cast<int>(fov_) * pi / 180.f; + const double diag_fov = static_cast<int>(fov_) * pi / 180.f; QMutexLocker l(&camera_mtx); CamInfo info; const bool res = camera.get_info(info); if (res) { const int w = info.res_x, h = info.res_y; - const double diag = sqrt(w * w + h * h)/w; - const double fov = 2.*atan(tan(diag_fov/2.0)/sqrt(1. + diag*diag)); + const double diag = sqrt(1. + h/(double)w * h/(double)w); + const double fov = 2.*atan(tan(diag_fov/2.0)/diag); ret = .5 / tan(.5 * fov); return true; } @@ -105,6 +105,10 @@ void Tracker_PT::run() QMutexLocker lock(&mutex); std::vector<cv::Vec2f> points = point_extractor.extract_points(frame); + + // blobs are sorted in order of circularity + if (points.size() > PointModel::N_POINTS) + points.resize(PointModel::N_POINTS); bool success = points.size() == PointModel::N_POINTS; diff --git a/ftnoir_tracker_pt/point_extractor.cpp b/ftnoir_tracker_pt/point_extractor.cpp index 7174d719..fcdbbaed 100644 --- a/ftnoir_tracker_pt/point_extractor.cpp +++ b/ftnoir_tracker_pt/point_extractor.cpp @@ -28,198 +28,119 @@ std::vector<cv::Vec2f> PointExtractor::extract_points(cv::Mat& frame) cv::Mat frame_gray; cv::cvtColor(frame, frame_gray, cv::COLOR_RGB2GRAY); - int min_size = s.min_point_size; - int max_size = s.max_point_size; + const int region_size_min = s.min_point_size; + const int region_size_max = s.max_point_size; - unsigned int region_size_min = 3.14*min_size*min_size/4.0; - unsigned int region_size_max = 3.14*max_size*max_size/4.0; - - // testing indicates threshold difference of 45 from lowest to highest - // that's applicable to poor lighting conditions. - - static constexpr int diff = 20; - static constexpr int steps = 5; - static constexpr int successes = 5; - - int thres = s.threshold; - - struct blob { - std::vector<cv::Vec2d> pos; - std::vector<double> confids; - std::vector<double> areas; - - cv::Vec2d effective_pos() const - { - double x = 0; - double y = 0; - double norm = 0; - for (unsigned i = 0; i < pos.size(); i++) - { - const double w = confids[i] * areas[i]; - x += pos[i][0] * w; - y += pos[i][1] * w; - norm += w; - } - cv::Vec2d ret(x, y); - ret *= 1./norm; - return ret; - } - }; + const int thres = s.threshold; struct simple_blob { - double radius_2; + double radius; cv::Vec2d pos; double confid; bool taken; double area; - simple_blob(double radius, const cv::Vec2d& pos, double confid, double area) : radius_2(radius*radius), pos(pos), confid(confid), taken(false), area(area) + simple_blob(double radius, const cv::Vec2d& pos, double confid, double area) : radius(radius), pos(pos), confid(confid), taken(false), area(area) { //qDebug() << "radius" << radius << "pos" << pos[0] << pos[1] << "confid" << confid; } bool inside(const simple_blob& other) { cv::Vec2d tmp = pos - other.pos; - return tmp.dot(tmp) < radius_2; - } - static std::vector<blob> merge(std::vector<simple_blob>& blobs) - { -#ifdef DEBUG_EXTRACTION - static Timer t; - bool debug = t.elapsed_ms() > 100; - if (debug) t.start(); -#endif - - std::vector<blob> ret; - for (unsigned i = 0; i < blobs.size(); i++) - { - auto& b = blobs[i]; - if (b.taken) - continue; - b.taken = true; - blob b_; - b_.pos.push_back(b.pos); - b_.confids.push_back(b.confid); - b_.areas.push_back(b.area); - - for (unsigned j = i+1; j < blobs.size(); j++) - { - auto& b2 = blobs[j]; - if (b2.taken) - continue; - if (b.inside(b2) || b2.inside(b)) - { - b2.taken = true; - b_.pos.push_back(b2.pos); - b_.confids.push_back(b2.confid); - b_.areas.push_back(b2.area); - } - } - if (b_.pos.size() >= successes) - ret.push_back(b_); - } -#ifdef DEBUG_EXTRACTION - if (debug) - { - double diff = 0; - for (unsigned j = 0; j < ret.size(); j++) - { - auto& b = ret[j]; - cv::Vec2d pos = b.effective_pos(); - for (unsigned i = 0; i < b.pos.size(); i++) - { - auto tmp = pos - b.pos[i]; - diff += std::abs(tmp.dot(tmp)); - } - } - qDebug() << "diff" << diff; - } -#endif - return ret; + return sqrt(tmp.dot(tmp)) < radius; } }; // mask for everything that passes the threshold (or: the upper threshold of the hysteresis) cv::Mat frame_bin = cv::Mat::zeros(H, W, CV_8U); - const int min = std::max(0, thres - diff/2); - const int max = std::min(255, thres + diff/2); - const int step = std::max(1, diff / steps); - std::vector<simple_blob> blobs; - - // this code is based on OpenCV SimpleBlobDetector - for (int i = min; i < max; i += step) + std::vector<std::vector<cv::Point>> contours; { cv::Mat frame_bin_; - cv::threshold(frame_gray, frame_bin_, i, 255, cv::THRESH_BINARY); + cv::threshold(frame_gray, frame_bin_, thres, 255, cv::THRESH_BINARY); frame_bin.setTo(170, frame_bin_); - - std::vector<std::vector<cv::Point>> contours; cv::findContours(frame_bin_, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); - - int cnt = 0; - - for (auto& c : contours) + } + + int cnt = 0; + + for (auto& c : contours) + { + if (cnt++ > 30) + break; + + const auto m = cv::moments(cv::Mat(c)); + const double area = m.m00; + if (area == 0.) + continue; + const cv::Vec2d pos(m.m10 / m.m00, m.m01 / m.m00); + + double radius; +// following based on OpenCV SimpleBlobDetector { - if (cnt++ > 30) - break; - - auto m = cv::moments(cv::Mat(c)); - const double area = m.m00; - if (area == 0.) - continue; - cv::Vec2d pos(m.m10 / m.m00, m.m01 / m.m00); - if (area < region_size_min || area > region_size_max) - continue; - - double radius = 0; - + std::vector<double> dists; for (auto& k : c) { - cv::Vec2d pos_(k.x, k.y); - cv::Vec2d tmp = pos_ - pos; - radius = std::max(radius, sqrt(1e-2 + tmp.dot(tmp))); + dists.push_back(cv::norm(pos - cv::Vec2d(k.x, k.y))); } - double confid = 1; + std::sort(dists.begin(), dists.end()); + radius = (dists[(dists.size() - 1)/2] + dists[dists.size()/2])/2; + } + + if (radius < region_size_min || radius > region_size_max) + continue; + + double confid = 1; + { + double denominator = std::sqrt(std::pow(2 * m.mu11, 2) + std::pow(m.mu20 - m.mu02, 2)); + const double eps = 1e-2; + if (denominator > eps) { - double denominator = std::sqrt(std::pow(2 * m.mu11, 2) + std::pow(m.mu20 - m.mu02, 2)); - const double eps = 1e-2; - if (denominator > eps) - { - double cosmin = (m.mu20 - m.mu02) / denominator; - double sinmin = 2 * m.mu11 / denominator; - double cosmax = -cosmin; - double sinmax = -sinmin; - - double imin = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmin - m.mu11 * sinmin; - double imax = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmax - m.mu11 * sinmax; - confid = imin / imax; - } + double cosmin = (m.mu20 - m.mu02) / denominator; + double sinmin = 2 * m.mu11 / denominator; + double cosmax = -cosmin; + double sinmax = -sinmin; + + double imin = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmin - m.mu11 * sinmin; + double imax = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmax - m.mu11 * sinmax; + confid = imin / imax; } - blobs.push_back(simple_blob(radius, pos, confid, area)); } +// end SimpleBlobDetector + + { + char buf[64]; + sprintf(buf, "%.2fpx %.2fc", radius, confid); + cv::putText(frame, buf, cv::Point(pos[0]+30, pos[1]+20), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 255), 1); + } + + blobs.push_back(simple_blob(radius, pos, confid, area)); } // clear old points points.clear(); + + using b = const simple_blob; + std::sort(blobs.begin(), blobs.end(), [](b& b1, b& b2) {return b1.confid > b2.confid;}); - for (auto& b : simple_blob::merge(blobs)) + for (auto& b : blobs) { - auto pos = b.effective_pos(); - cv::Vec2f p((pos[0] - W/2)/W, -(pos[1] - H/2)/W); + cv::Vec2f p((b.pos[0] - W/2)/W, -(b.pos[1] - H/2)/W); points.push_back(p); } + // draw output image std::vector<cv::Mat> channels_; cv::split(frame, channels_); - // draw output image - cv::Mat frame_bin_ = frame_bin * .5; std::vector<cv::Mat> channels; - channels.push_back(channels_[0] + frame_bin_); - channels.push_back(channels_[1] - frame_bin_); - channels.push_back(channels_[2] - frame_bin_); - cv::merge(channels, frame); + { + cv::Mat frame_bin__ = frame_bin * .5; + channels.push_back(channels_[0] + frame_bin__); + channels.push_back(channels_[1] - frame_bin__); + channels.push_back(channels_[2] - frame_bin__); + cv::merge(channels, frame); + } return points; } diff --git a/ftnoir_tracker_rs/README.md b/ftnoir_tracker_rs/README.md new file mode 100644 index 00000000..5fca4392 --- /dev/null +++ b/ftnoir_tracker_rs/README.md @@ -0,0 +1,16 @@ +# Intel® RealSense™ plugin for opentrack +This is a tracker providing markerless 3D head tracking using the Intel® RealSense™ SDK and the Intel® RealSense™ front-facing cameras. + +More information on RealSense can be found on [Intel.com](http://www.intel.com/content/www/us/en/architecture-and-technology/realsense-overview.html) + +This tracker uses a separate process that gets data from the SDK. Its sources are under rs_impl and can be recompiled by calling build.bat, which depends on +Microsoft Visual C++ Compiler and the [Intel® RealSense™ SDK R4](https://software.intel.com/en-us/intel-realsense-sdk) + +# ISC License +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. diff --git a/ftnoir_tracker_rs/ftnoir_tracker_rs.cpp b/ftnoir_tracker_rs/ftnoir_tracker_rs.cpp new file mode 100644 index 00000000..12330fd2 --- /dev/null +++ b/ftnoir_tracker_rs/ftnoir_tracker_rs.cpp @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#include "ftnoir_tracker_rs.h" +#include "ftnoir_tracker_rs_controls.h" + +#include "opentrack/plugin-api.hpp" +#include <QMessageBox> + +RSTracker::RSTracker() : mPose{ 0,0,0, 0,0,0 } { + mThread.setObjectName("RSTrackerWorkerThread"); + + mRealSenseImplProcess.moveToThread(&mThread); + mSocket.moveToThread(&mThread); + + connect(&mRealSenseImplProcess, SIGNAL(finished(int)), + this, SLOT(rsImplProcessFinished(int)), Qt::QueuedConnection); + + qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError"); + connect(&mRealSenseImplProcess, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(rsImplProcessError(QProcess::ProcessError)), Qt::QueuedConnection); + + connect(&mSocket, SIGNAL(readyRead()), + this, SLOT(readPendingUdpPoseData()), Qt::DirectConnection); + + connect(&mThread, &QThread::started, + &mThread, [this]{ + mSocket.bind(QHostAddress::LocalHost, 4242, QUdpSocket::DontShareAddress); + mRealSenseImplProcess.start("opentrack-tracker-rs-impl.exe", QProcess::NotOpen); + }, Qt::DirectConnection); + + connect(&mThread, &QThread::finished, + &mThread, [this]{ + mRealSenseImplProcess.kill(); + mRealSenseImplProcess.waitForFinished(); + }, Qt::DirectConnection); +} + +void RSTracker::start_tracker(QFrame*) +{ + mThread.start(); +} + +void RSTracker::readPendingUdpPoseData(){ + double pose[6]; + + while(mSocket.hasPendingDatagrams()) { + mSocket.readDatagram((char*)pose, sizeof(pose)); + { + QMutexLocker foo(&mMutex); + memcpy(mPose, pose, sizeof(pose)); + } + } +} + +void RSTracker::rsImplProcessError(QProcess::ProcessError error){ + if(error == QProcess::FailedToStart){ + QMessageBox::warning(NULL, "RealSense Tracking Error", "Couldn't start the RealSense tracking module.\nMaybe opentrack-tracker-rs-impl.exe is missing.", QMessageBox::Ok); + } + else if(error == QProcess::Crashed){ + QMessageBox::warning(NULL, "RealSense Tracking Error", "The RealSense tracking module has crashed.", QMessageBox::Ok); + } +} + + +void RSTracker::rsImplProcessFinished(int exitCode){ + if(exitCode!=0){ + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText("RealSense Tracking Error"); + if(exitCode==-101){ //The implementation got an invalid handle from the RealSense SDK session/modules + msgBox.setInformativeText("Couldn't initialize RealSense tracking. Please install SDK Runtime R4."); + } + else { + msgBox.setInformativeText("Status code: " + QString::number(exitCode) + ".\n\nNote that you need the latest camera drivers and the SDK runtime R4 to be installed."); + } + QPushButton* triggerSdkInstallation = msgBox.addButton("Install Runtime", QMessageBox::ActionRole); + msgBox.addButton(QMessageBox::Ok); + msgBox.exec(); + + if(msgBox.clickedButton() == triggerSdkInstallation){ + bool pStarted = QProcess::startDetached("clientfiles\\intel_rs_sdk_runtime_websetup_6.0.21.6598.exe --finstall=core,face3d --fnone=all"); + if(!pStarted){ + QMessageBox::warning(0, "Intel® RealSense™ Runtime Installation", "Installation process failed to start.", QMessageBox::Ok); + } + } + } +} + +void RSTracker::data(double *data) +{ + QMutexLocker foo(&mMutex); + memcpy(data, mPose, sizeof(mPose)); +} + +RSTracker::~RSTracker() { + mThread.quit(); + mThread.wait(); +} + +QString RSTrackerMetaData::name() { + return QString("Intel® RealSense™ Technology"); +} + +QIcon RSTrackerMetaData::icon() { + return QIcon(":/images/intel-16x16.png"); +} + +OPENTRACK_DECLARE_TRACKER(RSTracker, RSTrackerControls, RSTrackerMetaData) diff --git a/ftnoir_tracker_rs/ftnoir_tracker_rs.h b/ftnoir_tracker_rs/ftnoir_tracker_rs.h new file mode 100644 index 00000000..7cda7370 --- /dev/null +++ b/ftnoir_tracker_rs/ftnoir_tracker_rs.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#pragma once + +#include "ui_ftnoir_tracker_rs_controls.h" +#include "opentrack/plugin-api.hpp" +#include <QProcess> +#include <QMutex> +#include <QThread> +#include <QUdpSocket> + +class RSTracker : protected QObject, public ITracker +{ + Q_OBJECT + +public: + RSTracker(); + ~RSTracker(); + void start_tracker(QFrame *) override; + void data(double *data) override; + +private: + QMutex mMutex; + QThread mThread; + QProcess mRealSenseImplProcess; + QUdpSocket mSocket; + double mPose[6]; + +private slots: + void rsImplProcessError(QProcess::ProcessError); + void rsImplProcessFinished(int); + void readPendingUdpPoseData(); + +}; + +class RSTrackerMetaData : public Metadata +{ +public: + QString name(); + QIcon icon(); +}; diff --git a/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.cpp b/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.cpp new file mode 100644 index 00000000..e5187bd1 --- /dev/null +++ b/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.cpp @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#include "ftnoir_tracker_rs_controls.h" + +#include <QProcess> +#include <QMessageBox> + +RSTrackerControls::RSTrackerControls() +{ + ui.setupUi(this); + connect(ui.triggerSDKInstallButton, SIGNAL(clicked(bool)), this, SLOT(doInstallRSRuntime())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); +} + +void RSTrackerControls::doInstallRSRuntime() +{ + bool processStarted = QProcess::startDetached("clientfiles\\intel_rs_sdk_runtime_websetup_6.0.21.6598.exe --finstall=core,face3d --fnone=all"); + if(processStarted){ + this->close(); + } + else{ + QMessageBox::warning(0, "Intel® RealSenseTM Runtime Installation", "Installation process failed to start.", QMessageBox::Ok); + } +} + +void RSTrackerControls::doOK() +{ + this->close(); +} + +void RSTrackerControls::doCancel() +{ + this->close(); +} diff --git a/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.h b/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.h new file mode 100644 index 00000000..010dac99 --- /dev/null +++ b/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.h @@ -0,0 +1,23 @@ +/* + * 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. + */ + +#include "ftnoir_tracker_rs.h" + +class RSTrackerControls : public ITrackerDialog +{ + Q_OBJECT +public: + RSTrackerControls(); + void register_tracker(ITracker *) {} + void unregister_tracker() {} +private: + Ui::UIRSControls ui; + private slots: + void doOK(); + void doCancel(); + void doInstallRSRuntime(); +}; diff --git a/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.ui b/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.ui new file mode 100644 index 00000000..834803d1 --- /dev/null +++ b/ftnoir_tracker_rs/ftnoir_tracker_rs_controls.ui @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UIRSControls</class> + <widget class="QWidget" name="UIRSControls"> + <property name="windowModality"> + <enum>Qt::NonModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>378</width> + <height>193</height> + </rect> + </property> + <property name="windowTitle"> + <string>RealSense 3D Tracker settings</string> + </property> + <property name="windowIcon"> + <iconset> + <normaloff>images/RS.png</normaloff>images/RS.png</iconset> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>There is no configuration available at the moment. + +The application will activate your camera for face tracking by the +Intel® RealSense™ SDK. By design, the application has no direct access +to any camera images. + +In order to use this tracker, you need a PC equipped with +an Intel® RealSense™ R200 camera and the RealSense™ SDK R4 runtime.</string> + </property> + </widget> + </item> + <item alignment="Qt::AlignHCenter|Qt::AlignVCenter"> + <widget class="QPushButton" name="triggerSDKInstallButton"> + <property name="text"> + <string>Install SDK Runtime R4</string> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>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/ftnoir_tracker_rs/images/RS.png b/ftnoir_tracker_rs/images/RS.png Binary files differnew file mode 100644 index 00000000..4ca11aac --- /dev/null +++ b/ftnoir_tracker_rs/images/RS.png diff --git a/ftnoir_tracker_rs/images/intel-16x16.png b/ftnoir_tracker_rs/images/intel-16x16.png Binary files differnew file mode 100644 index 00000000..e985ace1 --- /dev/null +++ b/ftnoir_tracker_rs/images/intel-16x16.png diff --git a/ftnoir_tracker_rs/redist/intel_rs_sdk_runtime_websetup_6.0.21.6598.exe b/ftnoir_tracker_rs/redist/intel_rs_sdk_runtime_websetup_6.0.21.6598.exe Binary files differnew file mode 100644 index 00000000..34ecc9df --- /dev/null +++ b/ftnoir_tracker_rs/redist/intel_rs_sdk_runtime_websetup_6.0.21.6598.exe diff --git a/ftnoir_tracker_rs/rs_impl/bin/opentrack-tracker-rs-impl.exe b/ftnoir_tracker_rs/rs_impl/bin/opentrack-tracker-rs-impl.exe Binary files differnew file mode 100644 index 00000000..1e2a57f1 --- /dev/null +++ b/ftnoir_tracker_rs/rs_impl/bin/opentrack-tracker-rs-impl.exe diff --git a/ftnoir_tracker_rs/rs_impl/build.bat b/ftnoir_tracker_rs/rs_impl/build.bat new file mode 100644 index 00000000..15206431 --- /dev/null +++ b/ftnoir_tracker_rs/rs_impl/build.bat @@ -0,0 +1,2 @@ +cd "%VS120COMNTOOLS%\..\..\VC" +vcvarsall x64 && cd %~dp0 && CL /nologo /Ox /DUNICODE /D_UNICODE /MT /I"%RSSDK_DIR%\opensource\include" ftnoir_tracker_rs_impl.cpp udp_sender.cpp "%RSSDK_DIR%\opensource\src\libpxc\libpxc.cpp" /link ADVAPI32.LIB Ws2_32.lib /SUBSYSTEM:CONSOLE /OUT:bin\opentrack-tracker-rs-impl.exe
\ No newline at end of file diff --git a/ftnoir_tracker_rs/rs_impl/ftnoir_tracker_rs_impl.cpp b/ftnoir_tracker_rs/rs_impl/ftnoir_tracker_rs_impl.cpp new file mode 100644 index 00000000..c1ba5c75 --- /dev/null +++ b/ftnoir_tracker_rs/rs_impl/ftnoir_tracker_rs_impl.cpp @@ -0,0 +1,151 @@ +/* + * 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. + */ + +#include "ftnoir_tracker_rs_impl.h" +#include <pxcsensemanager.h> +#include <pxcfaceconfiguration.h> + +PXCSenseManager* g_senseManager = NULL; +PXCFaceData* g_faceData = NULL; + +pxcStatus rs_tracker_set_configuration(PXCFaceModule *faceModule){ + pxcStatus retStatus; + PXCFaceConfiguration *faceConfig = NULL; + + if (faceModule == NULL) + return PXC_STATUS_HANDLE_INVALID; + + faceConfig = faceModule->CreateActiveConfiguration(); + if (faceConfig == NULL){ + return PXC_STATUS_HANDLE_INVALID; + } + + faceConfig->pose.isEnabled = true; + faceConfig->pose.maxTrackedFaces = 1; + faceConfig->pose.smoothingLevel = PXCFaceConfiguration::SmoothingLevelType::SMOOTHING_DISABLED; + faceConfig->strategy = PXCFaceConfiguration::STRATEGY_CLOSEST_TO_FARTHEST; + faceConfig->detection.isEnabled = false; + faceConfig->landmarks.isEnabled = false; + faceConfig->DisableAllAlerts(); + faceConfig->SetTrackingMode(PXCFaceConfiguration::FACE_MODE_COLOR_PLUS_DEPTH); + + retStatus = faceConfig->ApplyChanges(); + if (retStatus != PXC_STATUS_NO_ERROR){ + faceConfig->Release(); + return retStatus; + } + + faceConfig->Release(); + return PXC_STATUS_NO_ERROR; +} + +int rs_tracker_impl_start(){ + pxcStatus retStatus; + PXCFaceModule *faceModule = NULL; + + g_senseManager = PXCSenseManager::CreateInstance(); + if (g_senseManager == NULL){ + rs_tracker_impl_end(); + return PXC_STATUS_HANDLE_INVALID; + } + + retStatus = g_senseManager->EnableFace(); + if (retStatus != PXC_STATUS_NO_ERROR){ + rs_tracker_impl_end(); + return retStatus; + } + + faceModule = g_senseManager->QueryFace(); + if (faceModule == NULL){ + rs_tracker_impl_end(); + return PXC_STATUS_HANDLE_INVALID; + } + + retStatus = rs_tracker_set_configuration(faceModule); + if (retStatus != PXC_STATUS_NO_ERROR){ + rs_tracker_impl_end(); + return PXC_STATUS_HANDLE_INVALID; + } + + retStatus = g_senseManager->Init(); + if (retStatus != PXC_STATUS_NO_ERROR){ + rs_tracker_impl_end(); + return retStatus; + } + + g_faceData = faceModule->CreateOutput(); + if (g_faceData == NULL){ + rs_tracker_impl_end(); + return PXC_STATUS_HANDLE_INVALID; + } + + return PXC_STATUS_NO_ERROR; +} + +int rs_tracker_impl_update_pose(double *data){ + pxcStatus retStatus; + PXCFaceData::PoseEulerAngles angles; + PXCFaceData::HeadPosition headPosition; + PXCFaceData::Face *face = NULL; + PXCFaceData::PoseData *pose = NULL; + bool poseAnglesAvailable = false; + bool headPositionAvailable = false; + + if (g_senseManager != NULL && g_faceData != NULL + && (retStatus = g_senseManager->AcquireFrame(true, 16)) == PXC_STATUS_NO_ERROR){ + + retStatus = g_faceData->Update(); + if (retStatus != PXC_STATUS_NO_ERROR){ + rs_tracker_impl_end(); + return retStatus; + } + + pxcI32 numberOfDetectedFaces = g_faceData->QueryNumberOfDetectedFaces(); + for (pxcI32 i = 0; i < numberOfDetectedFaces; ++i) { + face = g_faceData->QueryFaceByIndex(0); + if (face == NULL) continue; + + pose = face->QueryPose(); + if (pose == NULL) continue; + + poseAnglesAvailable = pose->QueryPoseAngles(&angles); + if (!poseAnglesAvailable) continue; + + headPositionAvailable = pose->QueryHeadPosition(&headPosition); + if (!headPositionAvailable) continue; + + //TODO: use pxcI32 pose->QueryConfidence(); ? for data[6] or to filter here ? + + //x, y, z: cm + data[0] = headPosition.headCenter.x / 10.; + data[1] = headPosition.headCenter.y / 10.; + data[2] = headPosition.headCenter.z / 10.; + + //yaw, pitch, roll: degrees + data[3] = -angles.yaw; + data[4] = angles.pitch; + data[5] = -angles.roll; + } + + g_senseManager->ReleaseFrame(); + } + + return retStatus; +} + +int rs_tracker_impl_end(){ + if (g_faceData != NULL){ + g_faceData->Release(); + g_faceData = NULL; + } + + if (g_senseManager != NULL){ + g_senseManager->Release(); + g_senseManager = NULL; + } + return PXC_STATUS_NO_ERROR; +} diff --git a/ftnoir_tracker_rs/rs_impl/ftnoir_tracker_rs_impl.h b/ftnoir_tracker_rs/rs_impl/ftnoir_tracker_rs_impl.h new file mode 100644 index 00000000..0e4073d0 --- /dev/null +++ b/ftnoir_tracker_rs/rs_impl/ftnoir_tracker_rs_impl.h @@ -0,0 +1,19 @@ +/* + * 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. + */ + +#pragma once +#ifdef EXPORT_RS_IMPL +#define RSTRACKERIMPL_VISIBILITY __declspec( dllexport ) +#else +#define RSTRACKERIMPL_VISIBILITY +#endif + +extern "C" { + RSTRACKERIMPL_VISIBILITY int rs_tracker_impl_start(); + RSTRACKERIMPL_VISIBILITY int rs_tracker_impl_update_pose(double *pose); + RSTRACKERIMPL_VISIBILITY int rs_tracker_impl_end(); +} diff --git a/ftnoir_tracker_rs/rs_impl/udp_sender.cpp b/ftnoir_tracker_rs/rs_impl/udp_sender.cpp new file mode 100644 index 00000000..095edd8f --- /dev/null +++ b/ftnoir_tracker_rs/rs_impl/udp_sender.cpp @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#include "ftnoir_tracker_rs_impl.h" +#include <winsock2.h> +#include <Ws2tcpip.h> +#include <commctrl.h> +#include <cstdlib> + +#define UDP_PORT 4242 + +int main(){ + double pose[6] = { 0., 0., 0., 0., 0., 0. }; + struct sockaddr_in socketInfo; + SOCKET s = 0; + WSADATA wsa; + + int retStatus = rs_tracker_impl_start(); + if (retStatus != 0){ + exit(retStatus); + } + + if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) //init winsocketapi + exit(EXIT_FAILURE); + + memset(&socketInfo, 0, sizeof(socketInfo)); + socketInfo.sin_family = AF_INET; + socketInfo.sin_port = htons(UDP_PORT); + InetPton(AF_INET, L"127.0.0.1", &socketInfo.sin_addr.S_un.S_addr); + + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR) //create UDP socket + exit(EXIT_FAILURE); + + for(;;) { + retStatus = rs_tracker_impl_update_pose(pose); + if(retStatus == 0){ //no error + if (sendto(s, (char*)&pose, sizeof(pose), 0, (struct sockaddr *) &socketInfo, sizeof(socketInfo)) == SOCKET_ERROR) //send new data + exit(EXIT_FAILURE); + } + + if(retStatus != 0 && retStatus != -303)// -303=timeout and 0 are ok, else we've got to stop. + break; + } + + closesocket(s); + WSACleanup(); + + return retStatus; +} diff --git a/ftnoir_tracker_rs/rs_tracker.qrc b/ftnoir_tracker_rs/rs_tracker.qrc new file mode 100644 index 00000000..155a5bd1 --- /dev/null +++ b/ftnoir_tracker_rs/rs_tracker.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>images/RS.png</file> + <file>images/intel-16x16.png</file> + </qresource> +</RCC> diff --git a/ftnoir_tracker_udp/ftnoir_tracker_udp.cpp b/ftnoir_tracker_udp/ftnoir_tracker_udp.cpp index bc42f402..1610f917 100644 --- a/ftnoir_tracker_udp/ftnoir_tracker_udp.cpp +++ b/ftnoir_tracker_udp/ftnoir_tracker_udp.cpp @@ -37,6 +37,7 @@ void FTNoIR_Tracker::run() { void FTNoIR_Tracker::start_tracker(QFrame*) { start(); + sock.moveToThread(this); } void FTNoIR_Tracker::data(double *data) @@ -59,7 +60,10 @@ void FTNoIR_Tracker::data(double *data) }; for (int i = 0; i < 3; i++) - data[Yaw + i] += values[indices[i]]; + { + int k = std::min<unsigned>(sizeof(values)/sizeof(values[0]), std::max(0, indices[i])); + data[Yaw + i] += values[k]; + } } diff --git a/macosx/make-app-bundle.sh b/macosx/make-app-bundle.sh index f851fe77..68037b67 100644 --- a/macosx/make-app-bundle.sh +++ b/macosx/make-app-bundle.sh @@ -34,7 +34,7 @@ iconutil -c icns -o "$tmp/$APPNAME.app/Contents/Resources/$APPNAME.icns" "$tmp/$ rm -r "$tmp/$APPNAME.iconset" cd "$tmp" || exit 1 -rm -f "$output_dir/$APPNAME-$version.zip" -zip -9r "$output_dir/$APPNAME-$version.zip" "$APPNAME.app" || exit 1 +rm -f "$output_dir/$version-macosx.zip" +zip -9r "$output_dir/$version-macosx.zip" "$APPNAME.app" || exit 1 rm -rf "$tmp" -ls -lh "$output_dir/$APPNAME-$version.zip" +ls -lh "$output_dir/$version-macosx.zip" diff --git a/opentrack-compat/process-list.hpp b/opentrack-compat/process-list.hpp new file mode 100644 index 00000000..65735740 --- /dev/null +++ b/opentrack-compat/process-list.hpp @@ -0,0 +1,162 @@ +#pragma once + +#include <QDebug> +#include <QStringList> + +#if defined _WIN32 + +#include <windows.h> +#include <TlHelp32.h> + +template<typename = void> +static QStringList get_all_executable_names() +{ + QStringList ret; + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h == INVALID_HANDLE_VALUE) + return ret; + + PROCESSENTRY32 e; + e.dwSize = sizeof(e); + + if (Process32First(h, &e) != TRUE) + { + CloseHandle(h); + return ret; + } + + do { + ret.append(e.szExeFile); + } while (Process32Next(h, &e) == TRUE); + + CloseHandle(h); + + return ret; +} +#elif defined __APPLE__ +#include <libproc.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <cerrno> +#include <cstring> +#include <vector> + +template<typename = void> +static QStringList get_all_executable_names() +{ + QStringList ret; + std::vector<int> vec; + + while (true) + { + int numproc = proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0); + if (numproc == -1) + { + qDebug() << "proc_listpids numproc failed" << errno; + return ret; + } + vec.resize(numproc); + int cnt = proc_listpids(PROC_ALL_PIDS, 0, &vec[0], sizeof(int) * numproc); + + if (cnt <= numproc) + { + std::vector<char> arglist; + int mib[2] { CTL_KERN, KERN_ARGMAX }; + size_t sz = sizeof(int); + int maxarg = 0; + if (sysctl(mib, 2, &maxarg, &sz, NULL, 0) == -1) + { + qDebug() << "sysctl KERN_ARGMAX" << errno; + return ret; + } + arglist.resize(maxarg); + for (int i = 0; i < numproc; i++) + { + size_t maxarg_ = (size_t)maxarg; + int mib[3] { CTL_KERN, KERN_PROCARGS2, vec[i] }; + if (sysctl(mib, 3, &arglist[0], &maxarg_, NULL, 0) == -1) + { + //qDebug() << "sysctl KERN_PROCARGS2" << vec[i] << errno; + continue; + } + QStringList cmdline; + for (unsigned j = sizeof(int) + strlen(&arglist[sizeof(int)]); j < maxarg_; j++) + { + QString arg(&arglist[j]); + if (arg.size() != 0) + { + cmdline << arg; + j += arg.size(); + } + } + if (cmdline.size() > 0) + { + int idx = cmdline[0].lastIndexOf('/'); + if (idx != -1) + { + QString tmp = cmdline[0].mid(idx+1); + if (cmdline.size() > 1 && (tmp == "wine.bin" || tmp == "wine")) + { + idx = cmdline[1].lastIndexOf('/'); + if (idx == -1) + idx = cmdline[1].lastIndexOf('\\'); + if (idx != -1) + { + ret.append(cmdline[1].mid(idx+1)); + } + else + ret.append(cmdline[1]); + } + else + { + ret.append(tmp); + } + } + else + ret.append(cmdline[0]); + } + } + return ret; + } + } +} + +#elif defined __linux + +#include <proc/readproc.h> +#include <cerrno> +template<typename = void> +static QStringList get_all_executable_names() +{ + QStringList ret; + proc_t** procs = readproctab(PROC_FILLCOM); + if (procs == nullptr) + { + qDebug() << "readproctab" << errno; + return ret; + } + for (int i = 0; procs[i]; i++) + { + // note, wine sets argv[0] so no parsing like in OSX case + auto proc = procs[i]; + if (proc->cmdline && proc->cmdline[0]) + { + QString tmp(proc->cmdline[0]); + const int idx = std::max(tmp.lastIndexOf('\\'), tmp.lastIndexOf('/')); + tmp = tmp.mid(idx == -1 ? 0 : idx+1); + ret.append(tmp); + } + freeproc(procs[i]); + } + free(procs); + return ret; +} + +#else +template<typename = void> +static QStringList get_all_executable_names() +{ + return QStringList(); +} +#endif diff --git a/opentrack/main-settings.hpp b/opentrack/main-settings.hpp index 96520fe9..dd61143e 100644 --- a/opentrack/main-settings.hpp +++ b/opentrack/main-settings.hpp @@ -15,9 +15,11 @@ using namespace options; struct axis_opts { + pbundle b; value<bool> invert, altp; value<int> src; axis_opts(pbundle b, QString pfx, int idx) : + b(b), invert(b, n(pfx, "invert-sign"), false), altp(b, n(pfx, "alt-axis-sign"), false), src(b, n(pfx, "source-index"), idx) diff --git a/opentrack/mappings.hpp b/opentrack/mappings.hpp index 85d9900c..2391efd1 100644 --- a/opentrack/mappings.hpp +++ b/opentrack/mappings.hpp @@ -62,6 +62,7 @@ public: { axes[i].curve.loadSettings(*iniFile, axes[i].name1); axes[i].curveAlt.loadSettings(*iniFile, axes[i].name2); + axes[i].opts.b->reload(); } } void save_mappings() @@ -72,6 +73,7 @@ public: { axes[i].curve.saveSettings(*iniFile, axes[i].name1); axes[i].curveAlt.saveSettings(*iniFile, axes[i].name2); + axes[i].opts.b->save(); } } @@ -81,6 +83,7 @@ public: { axes[i].curve.invalidate_unsaved_settings(); axes[i].curveAlt.invalidate_unsaved_settings(); + axes[i].opts.b->reload(); } } }; diff --git a/opentrack/options.hpp b/opentrack/options.hpp index 9768bc43..f2c09479 100644 --- a/opentrack/options.hpp +++ b/opentrack/options.hpp @@ -30,6 +30,7 @@ #include <QCoreApplication> #include <QFileInfo> #include <QDir> +#include <QStandardPaths> #include <cinttypes> @@ -38,8 +39,8 @@ #include <memory> template<typename t> using mem = std::shared_ptr<t>; -#define OPENTRACK_CONFIG_FILENAME_KEY "settings-file" -#define OPENTRACK_DEFAULT_CONFIG_PATH "/settings/default.ini" +#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename" +#define OPENTRACK_DEFAULT_CONFIG "default.ini" #define OPENTRACK_ORG "TrackHat opentrack-2.3" namespace options { @@ -90,30 +91,30 @@ namespace options { public: group(const string& name) : name(name) { - QSettings conf(ini_pathname(), QSettings::IniFormat); + auto conf = ini_file(); auto q_name = QString::fromStdString(name); - conf.beginGroup(q_name); - for (auto& k_ : conf.childKeys()) + conf->beginGroup(q_name); + for (auto& k_ : conf->childKeys()) { auto tmp = k_.toUtf8(); string k(tmp); - kvs[k] = conf.value(k_); + kvs[k] = conf->value(k_); } - conf.endGroup(); + conf->endGroup(); } void save() { - QSettings s(ini_pathname(), QSettings::IniFormat); + auto s = ini_file(); auto q_name = QString::fromStdString(name); - s.beginGroup(q_name); + s->beginGroup(q_name); for (auto& i : kvs) { auto k = QString::fromStdString(i.first); - s.setValue(k, i.second); + s->setValue(k, i.second); } - s.endGroup(); - s.sync(); + s->endGroup(); + s->sync(); } template<typename t> @@ -132,22 +133,46 @@ namespace options { return kvs.count(s) != 0; } - static const QString ini_pathname() + static QString ini_directory() { + const auto dirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); + if (dirs.size() == 0) + return ""; + if (QDir(dirs[0]).mkpath(OPENTRACK_ORG)) + return dirs[0] + "/" OPENTRACK_ORG; + return ""; + } + + static QString ini_filename() + { + QSettings settings(OPENTRACK_ORG); + return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); + } + + static QString ini_pathname() + { + const auto dir = ini_directory(); + if (dir == "") + return ""; QSettings settings(OPENTRACK_ORG); - return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, QCoreApplication::applicationDirPath() + OPENTRACK_DEFAULT_CONFIG_PATH).toString(); + return dir + "/" + settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); } static const QStringList ini_list() { - QFileInfo info(group::ini_pathname()); - QDir settings_dir(info.dir()); + const auto dirname = ini_directory(); + if (dirname == "") + return QStringList(); + QDir settings_dir(dirname); return settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name ); } static const mem<QSettings> ini_file() { - return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat); + const auto pathname = ini_pathname(); + if (pathname != "") + return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat); + return std::make_shared<QSettings>(); } }; diff --git a/opentrack/plugin-api.hpp b/opentrack/plugin-api.hpp index 572b7f31..732dbb3d 100644 --- a/opentrack/plugin-api.hpp +++ b/opentrack/plugin-api.hpp @@ -42,7 +42,7 @@ signals: } // implement this in all plugins -// also you must link against "opentrack-api" in CMakeList.txt to avoid vtable link errors +// also you must link against "opentrack-api" in CMakeLists.txt to avoid vtable link errors struct Metadata { // plugin name to be displayed in the interface diff --git a/opentrack/plugin-support.hpp b/opentrack/plugin-support.hpp index 78443cae..cd7eb528 100644 --- a/opentrack/plugin-support.hpp +++ b/opentrack/plugin-support.hpp @@ -103,7 +103,7 @@ struct dylib { # if defined(__APPLE__) RTLD_LOCAL|RTLD_FIRST|RTLD_NOW # else - RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE + RTLD_LOCAL|RTLD_NOW // XXX RTLD_DEEPBIND on Linux? # endif ); @@ -184,6 +184,14 @@ struct dylib { std::cout.flush(); if (!get_metadata(lib, longName, icon)) continue; + using d = const mem<dylib>&; + if (std::any_of(ret.cbegin(), + ret.cend(), + [&](d a) {return a->type == lib->type && a->name == lib->name;})) + { + qDebug() << "Duplicate lib" << lib->filename; + continue; + } ret.push_back(lib); } } diff --git a/opentrack/simple-mat.hpp b/opentrack/simple-mat.hpp index 7432e665..e462812b 100644 --- a/opentrack/simple-mat.hpp +++ b/opentrack/simple-mat.hpp @@ -41,66 +41,84 @@ namespace { enum { P = a == 1 ? 1 : 3 }; enum { Q = a == 1 ? 3 : 1 }; }; + + template<typename num, int h, int w, typename...ts> + struct is_arglist_correct + { + enum { value = h * w == sizeof...(ts) }; + }; } template<typename num, int h_, int w_> -struct Mat +class Mat { +#ifdef __GNUC__ + __restrict +#endif num data[h_][w_]; + + static_assert(h_ > 0 && w_ > 0, "must have positive mat dimensions"); + + Mat(std::initializer_list<num>&& xs) = delete; + +public: + + // parameters w_ and h_ are rebound so that SFINAE occurs + // removing them causes a compile-time error -sh 20150811 template<int Q = w_> typename std::enable_if<equals<Q, 1, 0>::value, num>::type - __inline operator()(int i) const { return data[i][0]; } + inline operator()(int i) const { return data[i][0]; } template<int P = h_> typename std::enable_if<equals<P, 1, 1>::value, num>::type - __inline operator()(int i) const { return data[0][i]; } + inline operator()(int i) const { return data[0][i]; } template<int Q = w_> typename std::enable_if<equals<Q, 1, 2>::value, num&>::type - __inline operator()(int i) { return data[i][0]; } + inline operator()(int i) { return data[i][0]; } template<int P = h_> typename std::enable_if<equals<P, 1, 3>::value, num&>::type - __inline operator()(int i) { return data[0][i]; } + inline operator()(int i) { return data[0][i]; } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num>::type - __inline x() const { return operator()(0); } + inline x() const { return operator()(0); } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num>::type - __inline y() const { return operator()(1); } + inline y() const { return operator()(1); } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num>::type - __inline z() const { return operator()(2); } + inline z() const { return operator()(2); } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num>::type - __inline w() const { return operator()(3); } + inline w() const { return operator()(3); } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num&>::type - __inline x() { return operator()(0); } + inline x() { return operator()(0); } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num&>::type - __inline y() { return operator()(1); } + inline y() { return operator()(1); } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num&>::type - __inline z() { return operator()(2); } + inline z() { return operator()(2); } template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num&>::type - __inline w() { return operator()(3); } + inline w() { return operator()(3); } template<int R, int S, int P = h_, int Q = w_> typename std::enable_if<is_vector_pair<R, S, P, Q>::value, num>::type - __inline dot(const Mat<num, R, S>& p2) const { + dot(const Mat<num, R, S>& p2) const { num ret = 0; constexpr int len = vector_len<R, S>::value; for (int i = 0; i < len; i++) - ret += operator()(i) * p2.operator ()(i); + ret += operator()(i) * p2(i); return ret; } template<int R, int S, int P = h_, int Q = w_> typename std::enable_if<is_dim3<P, Q, R, S>::value, Mat<num, is_dim3<P, Q, R, S>::P, is_dim3<P, Q, R, S>::Q>>::type - __inline cross(const Mat<num, R, S>& p2) const + cross(const Mat<num, R, S>& p2) const { - return Mat<num, R, S>({y() * p2.z() - p2.y() * z(), - p2.x() * z() - x() * p2.z(), - x() * p2.y() - y() * p2.x()}); + return Mat<num, R, S>(y() * p2.z() - p2.y() * z(), + p2.x() * z() - x() * p2.z(), + x() * p2.y() - y() * p2.x()); } Mat<num, h_, w_> operator+(const Mat<num, h_, w_>& other) const @@ -108,7 +126,7 @@ struct Mat Mat<num, h_, w_> ret; for (int j = 0; j < h_; j++) for (int i = 0; i < w_; i++) - ret(j, i) = this->operator ()(j, i) + other(j, i); + ret(j, i) = data[j][i] + other.data[j][i]; return ret; } @@ -117,7 +135,7 @@ struct Mat Mat<num, h_, w_> ret; for (int j = 0; j < h_; j++) for (int i = 0; i < w_; i++) - ret(j, i) = this->operator ()(j, i) - other(j, i); + ret(j, i) = data[j][i] - other.data[j][i]; return ret; } @@ -126,7 +144,7 @@ struct Mat Mat<num, h_, w_> ret; for (int j = 0; j < h_; j++) for (int i = 0; i < w_; i++) - ret(j, i) = this->operator ()(j, i) + other; + ret(j, i) = data[j][i] + other; return ret; } @@ -135,7 +153,7 @@ struct Mat Mat<num, h_, w_> ret; for (int j = 0; j < h_; j++) for (int i = 0; i < w_; i++) - ret(j, i) = this->operator ()(j, i) - other; + ret(j, i) = data[j][i] - other; return ret; } @@ -144,7 +162,7 @@ struct Mat Mat<num, h_, w_> ret; for (int j = 0; j < h_; j++) for (int i = 0; i < w_; i++) - ret(j, i) = operator()(j, i) * other; + ret(j, i) = data[j][i] * other; return ret; } @@ -158,30 +176,41 @@ struct Mat num sum = num(0); for (int k = 0; k < h_; k++) - sum += data[j][k]*other.data[k][i]; + sum += data[j][k]*other(k, i); - ret.data[j][i] = sum; + ret(j, i) = sum; } return ret; } - __inline num operator()(int j, int i) const { return data[j][i]; } - __inline num& operator()(int j, int i) { return data[j][i]; } + inline num operator()(int j, int i) const { return data[j][i]; } + inline num& operator()(int j, int i) { return data[j][i]; } - Mat(std::initializer_list<num>&& list) + template<typename... ts, int h__ = h_, int w__ = w_, + typename = typename std::enable_if<is_arglist_correct<num, h__, w__, ts...>::value>::type> + Mat(ts const&... xs) { - auto iter = list.begin(); - for (int i = 0; i < h_; i++) - for (int j = 0; j < w_; j++) - data[i][j] = *iter++; + const std::initializer_list<num> init = { static_cast<num>(xs)... }; + auto iter = init.begin(); + for (int j = 0; j < h_; j++) + for (int i = 0; i < w_; i++) + data[j][i] = *iter++; + } + + template<typename t> + Mat(const t* xs) + { + for (int j = 0; j < h_; j++) + for (int i = 0; i < w_; i++) + data[j][i] = num(*xs++); } Mat() { for (int j = 0; j < h_; j++) for (int i = 0; i < w_; i++) - data[j][i] = 0; + data[j][i] = num(0); } Mat(const num* mem) @@ -191,6 +220,8 @@ struct Mat data[j][i] = mem[i*h_+j]; } + Mat(num* mem) : Mat(const_cast<const num*>(mem)) {} + // XXX add more operators as needed, third-party dependencies mostly // not needed merely for matrix algebra -sh 20141030 @@ -234,10 +265,10 @@ struct Mat if (std::abs(pitch_1) + std::abs(roll_1) + std::abs(yaw_1) > std::abs(pitch_2) + std::abs(roll_2) + std::abs(yaw_2)) { bool fix_neg_pitch = pitch_1 < 0; - return dmat<3, 1>({yaw_2, std::fmod(fix_neg_pitch ? -pi - pitch_1 : pitch_2, pi), roll_2}); + return dmat<3, 1>(yaw_2, std::fmod(fix_neg_pitch ? -pi - pitch_1 : pitch_2, pi), roll_2); } else - return dmat<3, 1>({yaw_1, pitch_1, roll_1}); + return dmat<3, 1>(yaw_1, pitch_1, roll_1); } // tait-bryan angles, not euler diff --git a/opentrack/tracker.cpp b/opentrack/tracker.cpp index e9d85f5d..58e911bf 100644 --- a/opentrack/tracker.cpp +++ b/opentrack/tracker.cpp @@ -52,14 +52,14 @@ double Tracker::map(double pos, Mapping& axis) void Tracker::t_compensate(const rmat& rmat, const double* xyz, double* output, bool rz) { // TY is really yaw axis. need swapping accordingly. - dmat<3, 1> tvec({ xyz[2], -xyz[0], -xyz[1] }); + dmat<3, 1> tvec( xyz[2], -xyz[0], -xyz[1] ); const dmat<3, 1> ret = rmat * tvec; if (!rz) - output[2] = ret(0, 0); + output[2] = ret(0); else output[2] = xyz[2]; - output[1] = -ret(2, 0); - output[0] = -ret(1, 0); + output[1] = -ret(2); + output[0] = -ret(1); } void Tracker::logic() @@ -81,7 +81,12 @@ void Tracker::logic() if (!zero_) for (int i = 0; i < 6; i++) { - value(i) = newpose[i]; + auto& axis = m(i); + int k = axis.opts.src; + if (k < 0 || k >= 6) + value(i) = 0; + else + value(i) = newpose[k]; raw(i) = newpose[i]; } else @@ -90,7 +95,7 @@ void Tracker::logic() for (int i = 0; i < 3; i++) { - raw(i+3) = value(i+3) = mat(i, 0) * r2d; + raw(i+3) = value(i+3) = mat(i) * r2d; raw(i) = value(i) = t_b[i]; } } @@ -102,7 +107,7 @@ void Tracker::logic() }; const rmat cam = rmat::euler_to_rmat(off); rmat r = rmat::euler_to_rmat(&value[Yaw]); - dmat<3, 1> t { value(0), value(1), value(2) }; + dmat<3, 1> t(value(0), value(1), value(2)); r = cam * r; @@ -122,19 +127,19 @@ void Tracker::logic() { centerp = false; for (int i = 0; i < 3; i++) - t_b[i] = t(i, 0); + t_b[i] = t(i); r_b = r; } { - double tmp[3] = { t(0, 0) - t_b[0], t(1, 0) - t_b[1], t(2, 0) - t_b[2] }; + double tmp[3] = { t(0) - t_b[0], t(1) - t_b[1], t(2) - t_b[2] }; t_compensate(cam, tmp, tmp, false); - const rmat m_ = r_b.t() * r; + const rmat m_ = r * r_b.t(); const dmat<3, 1> euler = rmat::rmat_to_euler(m_); for (int i = 0; i < 3; i++) { value(i) = tmp[i]; - value(i+3) = euler(i, 0) * r2d; + value(i+3) = euler(i) * r2d; } } @@ -144,7 +149,7 @@ void Tracker::logic() if (libs.pFilter) libs.pFilter->filter(tmp, value); } - + for (int i = 0; i < 6; i++) value(i) = map(value(i), m(i)); @@ -153,27 +158,14 @@ void Tracker::logic() value, value, s.tcomp_tz); - - for (int i = 0; i < 6; i++) - value[i] *= inverts[i] ? -1. : 1.; - - Pose output_pose_; for (int i = 0; i < 6; i++) - { - auto& axis = m(i); - int k = axis.opts.src; - if (k < 0 || k >= 6) - output_pose_(i) = 0; - else - output_pose_(i) = value(k); - } - + value[i] *= inverts[i] ? -1. : 1.; - libs.pProtocol->pose(output_pose_); + libs.pProtocol->pose(value); QMutexLocker foo(&mtx); - output_pose = output_pose_; + output_pose = value; raw_6dof = raw; } @@ -202,10 +194,6 @@ void Tracker::run() { } { - // do one last pass with origin pose - for (int i = 0; i < 6; i++) - newpose[i] = 0; - logic(); // filter may inhibit exact origin Pose p; libs.pProtocol->pose(p); diff --git a/opentrack/win32-shortcuts.cpp b/opentrack/win32-shortcuts.cpp index d4107949..d4107949 100755..100644 --- a/opentrack/win32-shortcuts.cpp +++ b/opentrack/win32-shortcuts.cpp diff --git a/opentrack/win32-shortcuts.h b/opentrack/win32-shortcuts.h index 3e824b89..3e824b89 100755..100644 --- a/opentrack/win32-shortcuts.h +++ b/opentrack/win32-shortcuts.h diff --git a/pose-widget/glwidget.cpp b/pose-widget/glwidget.cpp index 8fbe046e..2cb858d7 100644 --- a/pose-widget/glwidget.cpp +++ b/pose-widget/glwidget.cpp @@ -31,38 +31,37 @@ void GLWidget::paintEvent ( QPaintEvent * event ) { p.drawImage(event->rect(), texture); } -void GLWidget::rotateBy(double xAngle, double yAngle, double zAngle, double x, double y, double z) +void GLWidget::rotateBy(float xAngle, float yAngle, float zAngle, float x, float y, float z) { - double c1 = cos(yAngle / 57.295781); - double s1 = sin(yAngle / 57.295781); - double c2 = cos(xAngle / 57.295781); - double s2 = sin(xAngle / 57.295781); - double c3 = cos(zAngle / 57.295781); - double s3 = sin(zAngle / 57.295781); - - double foo[] = { - c2*c3, -c2*s3, s2, - c1*s3+c3*s1*s2, c1*c3-s1*s2*s3, -c2*s1, - s1*s3-c1*c3*s2, c3*s1+c1*s2*s3, c1*c2, - }; - - rotation = rmat(foo); - translation = vec3({x, y, z}); + float c1 = cos(yAngle / 57.295781); + float s1 = sin(yAngle / 57.295781); + float c2 = cos(xAngle / 57.295781); + float s2 = sin(xAngle / 57.295781); + float c3 = cos(zAngle / 57.295781); + float s3 = sin(zAngle / 57.295781); + + rotation = rmat(c2*c3, -c2*s3, s2, + c1*s3+c3*s1*s2, c1*c3-s1*s2*s3, -c2*s1, + s1*s3-c1*c3*s2, c3*s1+c1*s2*s3, c1*c2); + translation = vec3(x, y, z); update(); } class Triangle { + using num = GLWidget::num; + using vec2 = GLWidget::vec2; + using vec3 = GLWidget::vec3; public: Triangle(const vec2& p1, const vec2& p2, const vec2& p3) { origin = p1; - v0 = vec2({ p3.x() - p1.x(), p3.y() - p1.y() }); - v1 = vec2({ p2.x() - p1.x(), p2.y() - p1.y() }); + v0 = vec2(p3.x() - p1.x(), p3.y() - p1.y()); + v1 = vec2(p2.x() - p1.x(), p2.y() - p1.y()); dot00 = v0.dot(v0); dot01 = v0.dot(v1); dot11 = v1.dot(v1); @@ -70,28 +69,28 @@ public: } bool barycentric_coords(const vec2& px, vec2& uv) const { - vec2 v2 = px - origin; - double dot12 = v1.dot(v2); - double dot02 = v0.dot(v2); - double u = (dot11 * dot02 - dot01 * dot12) * invDenom; - double v = (dot00 * dot12 - dot01 * dot02) * invDenom; - uv = vec2({u, v}); + const vec2 v2 = px - origin; + const num dot12 = v1.dot(v2); + const num dot02 = v0.dot(v2); + const num u = (dot11 * dot02 - dot01 * dot12) * invDenom; + const num v = (dot00 * dot12 - dot01 * dot02) * invDenom; + uv = vec2(u, v); return (u >= 0) && (v >= 0) && (u + v <= 1); } private: - double dot00, dot01, dot11, invDenom; + num dot00, dot01, dot11, invDenom; vec2 v0, v1, origin; }; -static __inline vec3 normal(const vec3& p1, const vec3& p2, const vec3& p3) +inline GLWidget::vec3 GLWidget::normal(const vec3& p1, const vec3& p2, const vec3& p3) { vec3 u = p2 - p1; vec3 v = p3 - p1; vec3 tmp = u.cross(v); - double i = 1./sqrt(tmp.dot(tmp)); + num i = 1./sqrt(tmp.dot(tmp)); return tmp * i; } @@ -100,19 +99,16 @@ void GLWidget::project_quad_texture() { const int sx = width(), sy = height(); vec2 pt[4]; const vec3 corners[] = { - vec3({0., 0., 0.}), - vec3({sx-1., 0., 0.}), - vec3({0., sy-1., 0.}), - vec3({sx-1., sy-1., 0.}) + vec3(0., 0, 0), + vec3(sx-1, 0, 0), + vec3(0, sy-1, 0), + vec3(sx-1, sy-1, 0.) }; - for (int i = 0; i < 4; i++) { - pt[i] = project(vec3({corners[i].x() - sx/2., corners[i].y() - sy/2., 0})); - pt[i].x() += sx/2.; - pt[i].y() += sy/2.; - } + for (int i = 0; i < 4; i++) + pt[i] = project(vec3(corners[i].x() - sx/2, corners[i].y() - sy/2, 0)) + vec2(sx/2, sy/2); - vec3 normal1({0, 0, 1}); + vec3 normal1(0, 0, 1); vec3 normal2; { vec3 foo[3]; @@ -121,24 +117,20 @@ void GLWidget::project_quad_texture() { normal2 = normal(foo[0], foo[1], foo[2]); } - double dir = normal1.dot(normal2); + num dir = normal1.dot(normal2); QImage& tex = dir < 0 ? back : front; int ow = tex.width(), oh = tex.height(); - vec2 p2[4]; - - for (int i = 0; i < 4; i++) - p2[i] = vec2({pt[i].x(), pt[i].y()}); QImage texture(QSize(sx, sy), QImage::Format_RGB888); QColor bgColor = palette().color(QPalette::Current, QPalette::Window); texture.fill(bgColor); - const vec2 projected[2][3] = { { p2[0], p2[1], p2[2] }, { p2[3], p2[1], p2[2] } }; + const vec2 projected[2][3] = { { pt[0], pt[1], pt[2] }, { pt[3], pt[1], pt[2] } }; const vec2 origs[2][3] = { - { vec2({0., 0.}), vec2({ow-1., 0.}), vec2({0., oh-1.}) }, - { vec2({ow-1., oh-1.}), vec2({ow-1., 0.}), vec2({0., oh-1.}) } + { vec2(0, 0), vec2(ow-1, 0), vec2(0, oh-1) }, + { vec2(ow-1, oh-1), vec2(ow-1, 0), vec2(0, oh-1) } }; const Triangle triangles[2] = { Triangle(projected[0][0], projected[0][1], projected[0][2]), @@ -160,9 +152,11 @@ void GLWidget::project_quad_texture() { for (int y = 0; y < sy; y++) for (int x = 0; x < sx; x++) { - vec2 pos({(double)x, (double)y}); + vec2 pos(x, y); for (int i = 0; i < 2; i++) { vec2 uv; + // XXX knowing center of the lookup pos, + // we have symmetry so only one lookup is needed -sh 20150831 if (triangles[i].barycentric_coords(pos, uv)) { const int px = origs[i][0].x() @@ -172,9 +166,9 @@ void GLWidget::project_quad_texture() { + uv.x() * (origs[i][2].y() - origs[i][0].y()) + uv.y() * (origs[i][1].y() - origs[i][0].y()); - int r = orig[py * orig_pitch + px * orig_depth + 2]; - int g = orig[py * orig_pitch + px * orig_depth + 1]; - int b = orig[py * orig_pitch + px * orig_depth + 0]; + const unsigned char r = orig[py * orig_pitch + px * orig_depth + 2]; + const unsigned char g = orig[py * orig_pitch + px * orig_depth + 1]; + const unsigned char b = orig[py * orig_pitch + px * orig_depth + 0]; dest[y * dest_pitch + x * dest_depth + 0] = r; dest[y * dest_pitch + x * dest_depth + 1] = g; @@ -187,21 +181,21 @@ void GLWidget::project_quad_texture() { this->texture = texture; } -vec2 GLWidget::project(const vec3 &point) +GLWidget::vec2 GLWidget::project(const vec3 &point) { vec3 ret = rotation * point; - double z = std::max(.75, 1. + translation.z()/-60.); + num z = std::max<num>(.75, 1. + translation.z()/-60); int w = width(), h = height(); - double x = w * translation.x() / 2. / -40.; + num x = w * translation.x() / 2 / -40; if (std::abs(x) > w/2) x = x > 0 ? w/2 : w/-2; - double y = h * translation.y() / 2. / -40.; + num y = h * translation.y() / 2 / -40; if (std::abs(y) > h/2) y = y > 0 ? h/2 : h/-2; - return vec2 { z * (ret.x() + x), z * (ret.y() + y) }; + return vec2(z * (ret.x() + x), z * (ret.y() + y)); } -vec3 GLWidget::project2(const vec3 &point) +GLWidget::vec3 GLWidget::project2(const vec3 &point) { return rotation * point; } diff --git a/pose-widget/glwidget.h b/pose-widget/glwidget.h index 88961fbd..81517399 100644 --- a/pose-widget/glwidget.h +++ b/pose-widget/glwidget.h @@ -12,22 +12,24 @@ #include "opentrack/plugin-api.hpp" #include "opentrack/simple-mat.hpp" -typedef dmat<2, 1> vec2; -typedef dmat<3, 1> vec3; -typedef dmat<3, 3> rmat; - class GLWidget : public QWidget { public: + using num = float; + using vec2 = Mat<num, 2, 1>; + using vec3 = Mat<num, 3, 1>; + using rmat = Mat<num, 3, 3>; + GLWidget(QWidget *parent); ~GLWidget(); - void rotateBy(double xAngle, double yAngle, double zAngle, double x, double y, double z); + void rotateBy(float xAngle, float yAngle, float zAngle, float x, float y, float z); protected: void paintEvent ( QPaintEvent * event ) override; private: vec2 project(const vec3& point); vec3 project2(const vec3& point); void project_quad_texture(); + static inline vec3 normal(const vec3& p1, const vec3& p2, const vec3& p3); rmat rotation; vec3 translation; QImage front; diff --git a/qfunctionconfigurator/functionconfig.cpp b/qfunctionconfigurator/functionconfig.cpp index c97aec16..8301c7f9 100755..100644 --- a/qfunctionconfigurator/functionconfig.cpp +++ b/qfunctionconfigurator/functionconfig.cpp @@ -120,6 +120,7 @@ void Map::reload() { const n t2 = t*t; const n t3 = t*t*t; + // XXX we could solve for t instead -sh 20150811 const int x = .5 * ((2. * p1_x) + (-p0_x + p2_x) * t + (2. * p0_x - 5. * p1_x + 4. * p2_x - p3_x) * t2 + @@ -140,7 +141,7 @@ void Map::reload() { } } - num last = 0; + integral last = 0; for (int i = 0; i < sz; i++) { if (data[i] == integral_max) diff --git a/qfunctionconfigurator/qfunctionconfigurator.cpp b/qfunctionconfigurator/qfunctionconfigurator.cpp index a977af77..e62049db 100644 --- a/qfunctionconfigurator/qfunctionconfigurator.cpp +++ b/qfunctionconfigurator/qfunctionconfigurator.cpp @@ -40,6 +40,8 @@ void QFunctionConfigurator::drawBackground() painter.fillRect(rect(), QColor::fromRgb(204, 204, 204)); QColor bg_color(112, 154, 209); + if (!isEnabled()) + bg_color = QColor(176,176,180); painter.fillRect(pixel_bounds, bg_color); QFont font; @@ -47,7 +49,12 @@ void QFunctionConfigurator::drawBackground() painter.setFont(font); QFontMetrics metrics(font); - QPen pen(QColor(55, 104, 170, 127), 1, Qt::SolidLine); + QColor color__(176, 190, 209, 127); + + if (!isEnabled()) + color__ = QColor(70, 90, 100, 96); + + QPen pen(color__, 1, Qt::SolidLine); const int xstep = 10, ystep = 10; const double maxx = _config->maxInput(); @@ -124,13 +131,27 @@ void QFunctionConfigurator::drawFunction() QList<QPointF> points = _config->getPoints(); + const int alpha = !isEnabled() ? 64 : 120; for (int i = 0; i < points.size(); i++) { drawPoint(&painter, point_to_pixel(points[i]), - QColor(200, 200, 210, 120)); + QColor(200, 200, 210, alpha), + isEnabled() ? QColor(50, 100, 120, 200) : QColor(200, 200, 200, 96)); + } + + QColor color = spline_color; + + if (!isEnabled()) + { + const int avg = 176; + auto color_ = color; + color = QColor(color_.red() * .5 + avg * .5, + color_.green() * .5 + avg * .5, + color_.blue() * .5 + avg * .5, + 96); } - QPen pen(spline_color, 1.2, Qt::SolidLine); + QPen pen(color, 1.2, Qt::SolidLine); const double max = _config->maxInput(); const double step = std::max(1e-2, max / 1000.); @@ -149,7 +170,10 @@ void QFunctionConfigurator::paintEvent(QPaintEvent *e) QPainter p(this); if (_background.isNull()) + { + _draw_function = true; drawBackground(); + } if (_draw_function) { _draw_function = false; @@ -162,6 +186,8 @@ void QFunctionConfigurator::paintEvent(QPaintEvent *e) QPen pen(Qt::white, 1, Qt::SolidLine); QList<QPointF> points = _config->getPoints(); if (points.size() && moving_control_point_idx >= 0 && moving_control_point_idx < points.size()) { + if (points[0].x() > 1e-2) + points.prepend(QPointF(0, 0)); QPointF prev = point_to_pixel(points[0]); for (int i = 1; i < points.size(); i++) { auto tmp = point_to_pixel(points[i]); @@ -174,17 +200,17 @@ void QFunctionConfigurator::paintEvent(QPaintEvent *e) // Show that point on the graph, with some lines to assist. // This new feature is very handy for tweaking the curves! QPointF last; - if (_config->getLastPoint(last)) { + if (_config->getLastPoint(last) && isEnabled()) { QPointF pixel_pos = point_to_pixel(last); drawPoint(&p, pixel_pos, QColor(255, 0, 0, 120)); } } } -void QFunctionConfigurator::drawPoint(QPainter *painter, const QPointF &pos, QColor colBG ) +void QFunctionConfigurator::drawPoint(QPainter *painter, const QPointF &pos, QColor colBG, QColor border) { painter->save(); - painter->setPen(QColor(50, 100, 120, 200)); + painter->setPen(border); painter->setBrush( colBG ); painter->drawEllipse(QRectF(pos.x() - pointSize, pos.y() - pointSize, @@ -203,7 +229,7 @@ void QFunctionConfigurator::drawLine(QPainter *painter, const QPointF &start, co void QFunctionConfigurator::mousePressEvent(QMouseEvent *e) { - if (!_config) + if (!_config || !isEnabled()) return; QList<QPointF> points = _config->getPoints(); if (e->button() == Qt::LeftButton) { @@ -260,7 +286,7 @@ void QFunctionConfigurator::mousePressEvent(QMouseEvent *e) void QFunctionConfigurator::mouseMoveEvent(QMouseEvent *e) { - if (!_config) + if (!_config || !isEnabled()) return; QList<QPointF> points = _config->getPoints(); @@ -335,7 +361,7 @@ void QFunctionConfigurator::mouseMoveEvent(QMouseEvent *e) void QFunctionConfigurator::mouseReleaseEvent(QMouseEvent *e) { - if (!_config) + if (!_config || !isEnabled()) return; if (e->button() == Qt::LeftButton) { diff --git a/qfunctionconfigurator/qfunctionconfigurator.h b/qfunctionconfigurator/qfunctionconfigurator.h index 59c8ffeb..fab452a7 100644 --- a/qfunctionconfigurator/qfunctionconfigurator.h +++ b/qfunctionconfigurator/qfunctionconfigurator.h @@ -34,6 +34,11 @@ public: spline_color = color; update(); } + void force_redraw() + { + _background = QPixmap(); + update(); + } protected slots: void paintEvent(QPaintEvent *e); void mousePressEvent(QMouseEvent *e); @@ -42,7 +47,7 @@ protected slots: private: void drawBackground(); void drawFunction(); - void drawPoint(QPainter *painter, const QPointF &pt, QColor colBG ); + void drawPoint(QPainter *painter, const QPointF &pt, QColor colBG, QColor border = QColor(50, 100, 120, 200)); void drawLine(QPainter *painter, const QPointF &start, const QPointF &end, QPen& pen); bool point_within_pixel(const QPointF& pt, const QPointF& pixel); protected: |