summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2023-07-24 15:51:59 +0200
committerGitHub <noreply@github.com>2023-07-24 15:51:59 +0200
commitcdabb9f3ad3e13e3b76b983a7b3ea6c118fc114e (patch)
tree785511a0054881447511faaaacd9cdbb8c5e13fd
parent53bc18790411d7bcd1aae9b5684aa8241666f21e (diff)
parent8da9e2945e0da7a5954de4f38263aaec8ae84955 (diff)
Merge pull request #1666 from tombrazier/nm_filter
-rw-r--r--compat/hamilton-tools.cpp (renamed from filter-hamilton/hamilton-tools.cpp)36
-rw-r--r--compat/hamilton-tools.h76
-rw-r--r--filter-hamilton/ftnoir_filter_hamilton.cpp2
-rw-r--r--filter-hamilton/ftnoir_filter_hamilton.h2
-rw-r--r--filter-hamilton/hamilton-tools.h27
-rw-r--r--filter-nm/CMakeLists.txt1
-rw-r--r--filter-nm/ftnoir_filter_nm.cpp67
-rw-r--r--filter-nm/ftnoir_filter_nm.h81
-rw-r--r--filter-nm/ftnoir_filter_nm_dialog.cpp56
-rw-r--r--filter-nm/ftnoir_nm_filtercontrols.ui312
-rw-r--r--filter-nm/lang/nl_NL.ts62
-rw-r--r--filter-nm/lang/ru_RU.ts62
-rw-r--r--filter-nm/lang/stub.ts62
-rw-r--r--filter-nm/lang/zh_CN.ts62
14 files changed, 848 insertions, 60 deletions
diff --git a/filter-hamilton/hamilton-tools.cpp b/compat/hamilton-tools.cpp
index e18082a8..eb0ef984 100644
--- a/filter-hamilton/hamilton-tools.cpp
+++ b/compat/hamilton-tools.cpp
@@ -1,28 +1,7 @@
#include "hamilton-tools.h"
#include <cmath>
-double VectorLength(const tVector v)
-{
- return(sqrt(v.v[0]*v.v[0] + v.v[1]*v.v[1] + v.v[2]*v.v[2]));
-}
-
-double sqr(const double v) { return(v*v); }
-
-double VectorDistance(const double v1[], const tVector v2)
-{
- return(sqrt(sqr(v2.v[0]-v1[0])+sqr(v2.v[1]-v1[1])+sqr(v2.v[2]-v1[2])));
-}
-
-tVector Lerp(const tVector s, const double d[], const double alpha)
-{
- tVector V;
- V.v[0] = s.v[0] + (d[0] - s.v[0]) * alpha;
- V.v[1] = s.v[1] + (d[1] - s.v[1]) * alpha;
- V.v[2] = s.v[2] + (d[2] - s.v[2]) * alpha;
- return(V);
-}
-
-tQuat QuatFromAngleAxe(const double angle, const tVector axe)
+tQuat QuatFromAngleAxe(const double angle, const tVector& axe)
{
double a = TO_RAD * 0.5 * angle;
double d = sin(a) / VectorLength(axe);
@@ -35,7 +14,7 @@ tQuat QuatFromAngleAxe(const double angle, const tVector axe)
);
}
-tQuat QuatMultiply(const tQuat qL, const tQuat qR)
+tQuat QuatMultiply(const tQuat& qL, const tQuat& qR)
{
tQuat Q;
Q.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;
@@ -45,11 +24,6 @@ tQuat QuatMultiply(const tQuat qL, const tQuat qR)
return(Q);
}
-double AngleBetween(const tQuat S, const tQuat D)
-{
- return( TO_DEG * 2*acos(fabs(S.x*D.x + S.y*D.y + S.z*D.z + S.w*D.w)) );
-}
-
tQuat QuatFromYPR(const double YPR[])
{
tQuat Q, Qp, Qy;
@@ -61,7 +35,7 @@ tQuat QuatFromYPR(const double YPR[])
return(QuatMultiply(Qy, Q));
}
-void Normalize(tQuat Q)
+void Normalize(tQuat& Q)
{
double m = sqrt(Q.x*Q.x + Q.y*Q.y + Q.z*Q.z + Q.w*Q.w);
if (m > EPSILON)
@@ -75,7 +49,7 @@ void Normalize(tQuat Q)
else Q = tQuat(0, 0, 0, 1);
}
-tQuat Slerp(const tQuat S, const tQuat D, const double alpha)
+tQuat Slerp(const tQuat& S, const tQuat& D, const double alpha)
{
// calc cosine of half angle
double cosin = S.x*D.x + S.y*D.y + S.z*D.z + S.w*D.w;
@@ -117,7 +91,7 @@ tQuat Slerp(const tQuat S, const tQuat D, const double alpha)
return( Q );
}
-void QuatToYPR(const tQuat Q, double YPR[])
+void QuatToYPR(const tQuat& Q, double YPR[])
{
const double xx = Q.x * Q.x;
const double xy = Q.x * Q.y;
diff --git a/compat/hamilton-tools.h b/compat/hamilton-tools.h
new file mode 100644
index 00000000..885e9f79
--- /dev/null
+++ b/compat/hamilton-tools.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <algorithm>
+#include "export.hpp"
+#include "compat/math.hpp"
+
+constexpr double TO_RAD = (M_PI / 180);
+constexpr double TO_DEG = (180 / M_PI);
+constexpr double EPSILON = 1e-30;
+
+struct tVector
+{
+ double v[3];
+ tVector(double X = 0, double Y = 0, double Z = 0) {v[0]=X; v[1]=Y; v[2]=Z;}
+ tVector(const double V[]) {v[0]=V[0]; v[1]=V[1]; v[2]=V[2];}
+
+ void operator=(const tVector& other)
+ {
+ std::copy(other.v, other.v + 3, v);
+ }
+ inline const tVector operator+(const tVector& other) const
+ {
+ return tVector(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]);
+ }
+ void operator+=(const tVector& other)
+ {
+ v[0] += other.v[0];
+ v[1] += other.v[1];
+ v[2] += other.v[2];
+ }
+ const tVector operator-(const tVector& other) const
+ {
+ return tVector(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]);
+ }
+ void operator-=(const tVector& other)
+ {
+ v[0] -= other.v[0];
+ v[1] -= other.v[1];
+ v[2] -= other.v[2];
+ }
+ const tVector operator*(double scalar) const
+ {
+ return tVector(v[0] * scalar, v[1] * scalar, v[2] * scalar);
+ }
+ void operator*=(double scalar)
+ {
+ v[0] *= scalar;
+ v[1] *= scalar;
+ v[2] *= scalar;
+ }
+ const tVector operator/(double scalar) const
+ {
+ return *this * (1.0 / scalar);
+ }
+ void operator/= (double scalar)
+ {
+ *this *= 1.0 / scalar;
+ }
+};
+
+struct tQuat
+{
+ double x, y, z, w;
+ tQuat(double X = 0, double Y = 0, double Z = 0, double W = 1)
+ {x = X; y = Y; z = Z; w = W;}
+};
+
+inline double VectorLength(const tVector& v) { return sqrt(v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2]); }
+inline double VectorDistance(const tVector& v1, const tVector& v2) { return VectorLength(v2 - v1); }
+inline tVector Lerp(const tVector& s, const tVector& d, const double alpha) { return s + (d - s) * alpha; }
+tQuat OTR_COMPAT_EXPORT QuatFromYPR(const double YPR[]);
+tQuat OTR_COMPAT_EXPORT QuatMultiply(const tQuat& qL, const tQuat& qR);
+inline tQuat QuatDivide(const tQuat& qL, const tQuat& qR) { return QuatMultiply(qL, tQuat(-qR.x, -qR.y, -qR.z, qR.w)); }
+inline double AngleBetween(const tQuat& S, const tQuat& D) { return TO_DEG * 2 * acos(fabs(S.x * D.x + S.y * D.y + S.z * D.z + S.w * D.w)); }
+tQuat OTR_COMPAT_EXPORT Slerp(const tQuat& S, const tQuat& D, const double alpha);
+void OTR_COMPAT_EXPORT QuatToYPR(const tQuat& Q, double YPR[]);
diff --git a/filter-hamilton/ftnoir_filter_hamilton.cpp b/filter-hamilton/ftnoir_filter_hamilton.cpp
index 7bbc91de..c4429ecc 100644
--- a/filter-hamilton/ftnoir_filter_hamilton.cpp
+++ b/filter-hamilton/ftnoir_filter_hamilton.cpp
@@ -9,7 +9,7 @@
#include <cmath>
#include <QMutexLocker>
#include "api/plugin-api.hpp"
-#include "hamilton-tools.h"
+#include "compat/hamilton-tools.h"
hamilton::hamilton() = default;
diff --git a/filter-hamilton/ftnoir_filter_hamilton.h b/filter-hamilton/ftnoir_filter_hamilton.h
index b724d973..199eef80 100644
--- a/filter-hamilton/ftnoir_filter_hamilton.h
+++ b/filter-hamilton/ftnoir_filter_hamilton.h
@@ -13,7 +13,7 @@
#include <QMutex>
#include "options/options.hpp"
//#include "compat/timer.hpp"
-#include "hamilton-tools.h"
+#include "compat/hamilton-tools.h"
using namespace options;
diff --git a/filter-hamilton/hamilton-tools.h b/filter-hamilton/hamilton-tools.h
deleted file mode 100644
index 2e288225..00000000
--- a/filter-hamilton/hamilton-tools.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include "compat/math.hpp"
-constexpr double TO_RAD = (M_PI / 180);
-constexpr double TO_DEG = (180 / M_PI);
-constexpr double EPSILON = 1e-30;
-
-struct tVector
-{
- double v[3];
- tVector(double X = 0, double Y = 0, double Z = 0) {v[0]=X; v[1]=Y; v[2]=Z;}
- tVector(double V[]) {v[0]=V[0]; v[1]=V[1]; v[2]=V[2];}
-};
-
-struct tQuat
-{
- double x, y, z, w;
- tQuat(double X = 0, double Y = 0, double Z = 0, double W = 1)
- {x = X; y = Y; z = Z; w = W;}
-};
-
-double VectorDistance(const double v1[], const tVector v2);
-tVector Lerp (const tVector s, const double d[], const double alpha);
-tQuat QuatFromYPR (const double YPR[]);
-double AngleBetween (const tQuat S, const tQuat D);
-tQuat Slerp (const tQuat S, const tQuat D, const double alpha);
-void QuatToYPR (const tQuat Q, double YPR[]);
diff --git a/filter-nm/CMakeLists.txt b/filter-nm/CMakeLists.txt
new file mode 100644
index 00000000..49daaee2
--- /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 00000000..b222184b
--- /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 00000000..84775207
--- /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 00000000..f3626cae
--- /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 00000000..7e317e74
--- /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 00000000..540e2d51
--- /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 00000000..31e5d5cc
--- /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 00000000..3b960804
--- /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 00000000..92521e0f
--- /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>