diff options
Diffstat (limited to 'filter-nm')
| -rw-r--r-- | filter-nm/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | filter-nm/ftnoir_filter_nm.cpp | 67 | ||||
| -rw-r--r-- | filter-nm/ftnoir_filter_nm.h | 81 | ||||
| -rw-r--r-- | filter-nm/ftnoir_filter_nm_dialog.cpp | 56 | ||||
| -rw-r--r-- | filter-nm/ftnoir_nm_filtercontrols.ui | 312 | ||||
| -rw-r--r-- | filter-nm/lang/nl_NL.ts | 62 | ||||
| -rw-r--r-- | filter-nm/lang/ru_RU.ts | 62 | ||||
| -rw-r--r-- | filter-nm/lang/stub.ts | 62 | ||||
| -rw-r--r-- | filter-nm/lang/zh_CN.ts | 62 |
9 files changed, 765 insertions, 0 deletions
diff --git a/filter-nm/CMakeLists.txt b/filter-nm/CMakeLists.txt new file mode 100644 index 000000000..49daaee2d --- /dev/null +++ b/filter-nm/CMakeLists.txt @@ -0,0 +1 @@ +otr_module(filter-nm) diff --git a/filter-nm/ftnoir_filter_nm.cpp b/filter-nm/ftnoir_filter_nm.cpp new file mode 100644 index 000000000..b222184b7 --- /dev/null +++ b/filter-nm/ftnoir_filter_nm.cpp @@ -0,0 +1,67 @@ +/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#include "ftnoir_filter_nm.h" +#include "compat/math-imports.hpp" +#include "compat/macros.h" + +#include "api/plugin-api.hpp" +#include "opentrack/defs.hpp" + +#include <algorithm> + +filter_nm::filter_nm() +{ +} + +void filter_nm::filter(const double* input, double* output) +{ + tVector position = { input[TX], input[TY], input[TZ] }; + tQuat rotation = QuatFromYPR(input + Yaw); + + // order of axes: x, y, z, yaw, pitch, roll + if (unlikely(first_run)) + { + first_run = false; + t.start(); + + last_pos_speed = tVector(); + last_rot_speed = tQuat(); + last_pos_out = position; + last_rot_out = rotation; + } + else + { + const double dt = t.elapsed_seconds(); + t.start(); + + const tVector pos_speed = (position - last_pos_in) / dt; + const double pos_tau = 1. / *s.pos_responsiveness; + double alpha = dt / (dt + pos_tau); + last_pos_speed += (pos_speed - last_pos_speed) * alpha; + const double factor_pos = min(1.0, VectorLength(last_pos_speed) / (*s.pos_drift_speed * 3.0)); + alpha *= factor_pos * factor_pos; + last_pos_out += (position - last_pos_out) * alpha; + + const tQuat rot_delta = QuatDivide(rotation, last_rot_in); + constexpr double ms_per_s = 1000.0; // angular speed quaternions need to be small to work so use °/ms + const tQuat rot_speed = Slerp(tQuat(), rot_delta, 1.0 / ms_per_s / dt ); + const double rot_tau = 1. / *s.rot_responsiveness; + alpha = dt / (dt + rot_tau); + last_rot_speed = Slerp(last_rot_speed, rot_speed, alpha); + const double angular_speed = AngleBetween(tQuat(), last_rot_speed) * ms_per_s; + const double factor_rot = min(1.0, angular_speed / (*s.rot_drift_speed * 3.0)); + alpha *= factor_rot * factor_rot; + last_rot_out = Slerp(last_rot_out, rotation, alpha); + } + + last_pos_in = position; + last_rot_in = rotation; + std::copy(last_pos_out.v, last_pos_out.v + 3, output + TX); + QuatToYPR(last_rot_out, &output[Yaw]); +} + +OPENTRACK_DECLARE_FILTER(filter_nm, dialog_nm, nmDll) diff --git a/filter-nm/ftnoir_filter_nm.h b/filter-nm/ftnoir_filter_nm.h new file mode 100644 index 000000000..84775207c --- /dev/null +++ b/filter-nm/ftnoir_filter_nm.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net> + * + * 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. + */ +#pragma once + +#include "ui_ftnoir_nm_filtercontrols.h" + +#include "api/plugin-api.hpp" +#include "compat/timer.hpp" +#include "compat/hamilton-tools.h" +#include "options/options.hpp" + +using namespace options; + +struct settings_nm : opts +{ + value<slider_value> pos_responsiveness; + value<slider_value> rot_responsiveness; + value<slider_value> pos_drift_speed; + value<slider_value> rot_drift_speed; + + settings_nm() : + opts("nm-filter"), + pos_responsiveness(value<slider_value>(b, "pos-responsiveness", { 13.0, .0, 20.0 })), + rot_responsiveness(value<slider_value>(b, "rot-responsiveness", { 16.0, .0, 20.0 })), + pos_drift_speed(value<slider_value>(b, "pos-drift-speed", { 5.0, 0.1, 50.0 })), + rot_drift_speed(value<slider_value>(b, "rot-drift-speed", { 7.0, 0.1, 50.0 })) + { + } +}; + +struct filter_nm : IFilter +{ + filter_nm(); + void filter(const double* input, double* output) override; + void center() override { first_run = true; } + module_status initialize() override { return status_ok(); } + +private: + tVector last_pos_in; + tQuat last_rot_in; + tVector last_pos_out; + tQuat last_rot_out; + tVector last_pos_speed; + tQuat last_rot_speed; + Timer t; + settings_nm s; + bool first_run = true; +}; + +class dialog_nm : public IFilterDialog +{ + Q_OBJECT +public: + dialog_nm(); + void register_filter(IFilter*) override {} + void unregister_filter() override {} + void save() override; + void reload() override; + bool embeddable() noexcept override { return true; } + void set_buttons_visible(bool x) override; + +private: + Ui::UICdialog_nm ui; + settings_nm s; + +private slots: + void doOK(); + void doCancel(); +}; + +class nmDll : public Metadata +{ + Q_OBJECT + + QString name() override { return tr("NaturalMovement"); } + QIcon icon() override { return QIcon(":/images/filter-16.png"); } +}; diff --git a/filter-nm/ftnoir_filter_nm_dialog.cpp b/filter-nm/ftnoir_filter_nm_dialog.cpp new file mode 100644 index 000000000..f3626caeb --- /dev/null +++ b/filter-nm/ftnoir_filter_nm_dialog.cpp @@ -0,0 +1,56 @@ +/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#include "ftnoir_filter_nm.h" + +using namespace options; + +dialog_nm::dialog_nm() +{ + ui.setupUi(this); + + tie_setting(s.pos_responsiveness, ui.pos_responsiveness_slider); + tie_setting(s.pos_responsiveness, ui.pos_responsiveness, [](double x) + { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); + + tie_setting(s.rot_responsiveness, ui.rot_responsiveness_slider); + tie_setting(s.rot_responsiveness, ui.rot_responsiveness, [](double x) + { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); + + tie_setting(s.pos_drift_speed, ui.pos_drift_speed_slider); + tie_setting(s.pos_drift_speed, ui.pos_drift_speed, [](double x) + { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); + + tie_setting(s.rot_drift_speed, ui.rot_drift_speed_slider); + tie_setting(s.rot_drift_speed, ui.rot_drift_speed, [](double x) + { return QStringLiteral("%1").arg(x, 0, 'f', 2); }); +} + +void dialog_nm::doOK() +{ + save(); + close(); +} + +void dialog_nm::doCancel() +{ + close(); +} + +void dialog_nm::save() +{ + s.b->save(); +} + +void dialog_nm::reload() +{ + s.b->reload(); +} + +void dialog_nm::set_buttons_visible(bool x) +{ + ui.buttonBox->setVisible(x); +} diff --git a/filter-nm/ftnoir_nm_filtercontrols.ui b/filter-nm/ftnoir_nm_filtercontrols.ui new file mode 100644 index 000000000..7e317e740 --- /dev/null +++ b/filter-nm/ftnoir_nm_filtercontrols.ui @@ -0,0 +1,312 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UICdialog_nm</class> + <widget class="QDialog" name="UICdialog_nm"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>438</width> + <height>559</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Responsiveness</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Rotation</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="pos_responsiveness"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>10.0</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="rot_responsiveness"> + <property name="minimumSize"> + <size> + <width>30</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>10.0</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QSlider" name="rot_responsiveness_slider"> + <property name="maximum"> + <number>40</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QSlider" name="pos_responsiveness_slider"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximum"> + <number>40</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Position</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Drift speeds</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="2"> + <widget class="QLabel" name="label_14"> + <property name="text"> + <string>mm/s</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Position</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="pos_drift_speed"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>50</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="3"> + <widget class="QSlider" name="rot_drift_speed_slider"> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>500</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Rotation</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="rot_drift_speed"> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>100</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="2"> + <widget class="QLabel" name="label_24"> + <property name="text"> + <string>°/s</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QSlider" name="pos_drift_speed_slider"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>500</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="6" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_15"> + <property name="text"> + <string>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="5" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>pos_responsiveness_slider</tabstop> + <tabstop>rot_responsiveness_slider</tabstop> + <tabstop>pos_drift_speed_slider</tabstop> + <tabstop>rot_drift_speed_slider</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>UICdialog_nm</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>UICdialog_nm</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/filter-nm/lang/nl_NL.ts b/filter-nm/lang/nl_NL.ts new file mode 100644 index 000000000..540e2d51b --- /dev/null +++ b/filter-nm/lang/nl_NL.ts @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="nl_NL"> +<context> + <name>UICdialog_nm</name> + <message> + <source>Dialog</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>°/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>mm/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Responsiveness</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>10.0</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Drift speeds</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>50</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>100</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Position</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>nmDll</name> + <message> + <source>NaturalMovement</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/filter-nm/lang/ru_RU.ts b/filter-nm/lang/ru_RU.ts new file mode 100644 index 000000000..31e5d5ccb --- /dev/null +++ b/filter-nm/lang/ru_RU.ts @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> + <name>UICdialog_nm</name> + <message> + <source>Dialog</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>°/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>mm/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Responsiveness</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>10.0</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Drift speeds</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>50</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>100</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Position</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>nmDll</name> + <message> + <source>NaturalMovement</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/filter-nm/lang/stub.ts b/filter-nm/lang/stub.ts new file mode 100644 index 000000000..3b9608046 --- /dev/null +++ b/filter-nm/lang/stub.ts @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="stub"> +<context> + <name>UICdialog_nm</name> + <message> + <source>Dialog</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>°/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>mm/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Responsiveness</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>10.0</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Drift speeds</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>50</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>100</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Position</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>nmDll</name> + <message> + <source>NaturalMovement</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/filter-nm/lang/zh_CN.ts b/filter-nm/lang/zh_CN.ts new file mode 100644 index 000000000..92521e0fe --- /dev/null +++ b/filter-nm/lang/zh_CN.ts @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="zh_CN"> +<context> + <name>UICdialog_nm</name> + <message> + <source>Dialog</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>°/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>mm/s</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Responsiveness</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>10.0</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Drift speeds</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>50</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>100</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Rotation</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Position</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>nmDll</name> + <message> + <source>NaturalMovement</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> |
