summaryrefslogtreecommitdiffhomepage
path: root/spline
diff options
context:
space:
mode:
Diffstat (limited to 'spline')
-rw-r--r--spline/axis-opts.cpp3
-rw-r--r--spline/axis-opts.hpp10
-rw-r--r--spline/lang/de_DE.ts4
-rw-r--r--spline/lang/zh_CN.ts2
-rw-r--r--spline/spline-widget.cpp26
-rw-r--r--spline/spline.cpp97
-rw-r--r--spline/spline.hpp8
7 files changed, 103 insertions, 47 deletions
diff --git a/spline/axis-opts.cpp b/spline/axis-opts.cpp
index 489008e6..a2b4941d 100644
--- a/spline/axis-opts.cpp
+++ b/spline/axis-opts.cpp
@@ -31,7 +31,8 @@ axis_opts::axis_opts(QString pfx, Axis idx) :
axis_(idx),
zero(b_settings_window, n(pfx, "zero-pos"), 0),
src(b_settings_window, n(pfx, "source-index"), idx),
- invert(b_settings_window, n(pfx, "invert-sign"), false),
+ invert_post(b_settings_window, n(pfx, "invert-sign-post"), false),
+ invert_pre(b_settings_window, n(pfx, "invert-sign"), false),
altp(b_mapping_window, n(pfx, "alt-axis-sign"), false),
clamp_x_(b_mapping_window, n(pfx, "max-value"), get_max_x(idx)),
clamp_y_(b_mapping_window, n(pfx, "max-output-value"), get_max_y(idx))
diff --git a/spline/axis-opts.hpp b/spline/axis-opts.hpp
index c773dd61..437a5faa 100644
--- a/spline/axis-opts.hpp
+++ b/spline/axis-opts.hpp
@@ -29,7 +29,11 @@ public:
r15 = 15,
r10 = 10,
+ t600 = 600,
+ t300 = 300,
+ t150 = 150,
t100 = 100,
+ t75 = 75,
t30 = 30,
t20 = 20,
t15 = 15,
@@ -38,6 +42,10 @@ public:
o_r180 = -180,
o_r90 = -90,
o_t75 = -75,
+ o_t100 = -100,
+ o_t150 = -150,
+ o_t300 = -300,
+ o_t600 = -600,
x1000 = 1000,
};
@@ -47,7 +55,7 @@ public:
bundle b_mapping_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-mappings") };
value<double> zero;
value<int> src;
- value<bool> invert, altp;
+ value<bool> invert_pre, invert_post, altp;
value<max_clamp> clamp_x_, clamp_y_;
double max_clamp_x() const { return std::fabs(clamp_x_.to<double>()); }
double max_clamp_y() const { return std::fabs(clamp_y_.to<double>()); }
diff --git a/spline/lang/de_DE.ts b/spline/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/spline/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/spline/lang/zh_CN.ts b/spline/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/spline/lang/zh_CN.ts
+++ b/spline/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/spline/spline-widget.cpp b/spline/spline-widget.cpp
index 2429ffc3..46e2095c 100644
--- a/spline/spline-widget.cpp
+++ b/spline/spline-widget.cpp
@@ -1,17 +1,22 @@
#include "spline-widget.hpp"
#include "compat/math.hpp"
-#include "compat/macros.hpp"
#include <algorithm>
#include <QPainter>
+#include <QPainterPath>
#include <QPixmap>
#include <QString>
#include <QToolTip>
#include <QtEvents>
+#include <QPainterPath>
#include <QDebug>
+#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
+# define OTR_OBSOLETE_QT_WORKAROUND
+#endif
+
namespace spline_detail {
spline_widget::spline_widget(QWidget *parent) : QWidget(parent)
@@ -43,7 +48,7 @@ void spline_widget::set_config(base_spline* spl)
if (spl)
{
std::shared_ptr<base_settings> s = spl->get_settings();
- connection = connect(s.get(), &base_settings::recomputed,
+ connection = connect(&*s, &base_settings::recomputed,
this, [this] { reload_spline(); },
Qt::QueuedConnection);
}
@@ -78,10 +83,6 @@ bool spline_widget::is_preview_only() const
return preview_only;
}
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
-# define OTR_OBSOLETE_QT_WORKAROUND
-#endif
-
void spline_widget::drawBackground()
{
QPainter painter(&background_img);
@@ -111,12 +112,12 @@ void spline_widget::drawBackground()
const int ystep = (int)std::ceil(y_step_), xstep = (int)std::ceil(x_step_);
const double maxx = config->max_input();
const double maxy = config->max_output();
-
#ifndef OTR_OBSOLETE_QT_WORKAROUND
double space_width = metrics.horizontalAdvance(' ');
#else
- double space_width = metrics.averageCharWidth();
+ double space_width = metrics.boundingRect(' ').right();
#endif
+ painter.setPen(palette().text().color());
// vertical grid
for (int i = 0; i <= maxy; i += ystep)
@@ -152,11 +153,10 @@ void spline_widget::drawBackground()
#ifndef OTR_OBSOLETE_QT_WORKAROUND
double advance = metrics.horizontalAdvance(text);
#else
- double advance = rect.right();
+ double advance = metrics.boundingRect(text).right();
#endif
-
painter.drawText(QPointF(x - advance/2 - rect.left(),
- pixel_bounds.height() - rect.top() + rect.height()),
+ pixel_bounds.bottom() + metrics.lineSpacing()),
text);
}
}
@@ -631,8 +631,8 @@ QPointF spline_widget::pixel_to_point(const QPointF& point)
if (snap_y > 0)
y = snap(y, snap_y);
- x = clamp(x, 0, config->max_input());
- y = clamp(y, 0, config->max_output());
+ x = std::clamp(x, 0., config->max_input());
+ y = std::clamp(y, 0., config->max_output());
return { x, y };
}
diff --git a/spline/spline.cpp b/spline/spline.cpp
index 21044b34..466a9a7f 100644
--- a/spline/spline.cpp
+++ b/spline/spline.cpp
@@ -42,8 +42,10 @@ void spline::set_tracking_active(bool value) const
std::shared_ptr<settings> S;
{
QMutexLocker l(&mtx);
- S = s;
+ if (value == activep)
+ return;
activep = value;
+ S = s;
}
emit S->recomputed();
}
@@ -98,10 +100,10 @@ bool spline::get_last_value(QPointF& point)
double spline::get_value_internal(int x) const
{
- const float sign = signum(x);
+ const auto sign = (f)signum(x);
x = std::abs(x);
- const float ret_ = data[std::min(unsigned(x), value_count - 1)];
- return (double)(sign * clamp(ret_, 0, 1000));
+ const auto ret_ = data[std::min(unsigned(x), value_count - 1)];
+ return (double)(sign * std::clamp(ret_, (f)0, (f)1000));
}
void spline::ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y)
@@ -129,7 +131,7 @@ void spline::ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y)
int spline::element_count(const QList<QPointF>& points, double max_input)
{
- const unsigned sz = (unsigned)points.size();
+ const int sz = points.size();
for (int k = sz-1; k >= 0; k--)
{
const QPointF& pt = points[k];
@@ -139,7 +141,7 @@ int spline::element_count(const QList<QPointF>& points, double max_input)
return sz;
}
-bool spline::sort_fn(const QPointF& one, const QPointF& two)
+bool spline::sort_fn(QPointF one, QPointF two)
{
return one.x() < two.x();
}
@@ -148,14 +150,14 @@ void spline::update_interp_data() const
{
points_t list = points;
ensure_valid(list);
- const int sz = list.size();
+ int sz = list.size();
if (list.isEmpty())
list.prepend({ max_input(), max_output() });
const double c = bucket_size_coefficient(list);
const double c_ = c * c_interp;
- const float cf = (float)c, c_f = (float)c_;
+ const f cf = (f)c, c_f = (f)c_;
for (unsigned i = 0; i < value_count; i++)
data[i] = magic_fill_value;
@@ -165,15 +167,34 @@ void spline::update_interp_data() const
const QPointF& pt = list[0];
const double x = pt.x();
const double y = pt.y();
- const unsigned max = clamp(uround(x * c), 0, value_count-1);
+ const unsigned max = std::clamp((unsigned)iround(x * c), 1u, value_count-1);
for (unsigned k = 0; k <= max; k++)
- data[k] = float(y * k / max); // no need for bresenham
+ data[k] = f(y * k / max); // no need for bresenham
+ }
+ else if (sz == 2 && list[0].y() < 1e-6)
+ {
+ unsigned start = std::clamp((unsigned)iround(list[0].x() * c), 1u, value_count-1);
+ unsigned end = std::clamp((unsigned)iround(list[1].x() * c), 2u, value_count-1);
+ unsigned max = end - start;
+ for (unsigned x = 0; x < start; x++)
+ data[x] = 0;
+ for (unsigned x = 0; x < max; x++)
+ data[start + x] = (f)(list[1].y() * x / max);
}
else
{
- if (list[0].x() > 1e-2)
- list.push_front({});
+ if (list[0].x() > 1e-6)
+ {
+ double zero_pos = 0;
+ while (list.size() > 1 && list[0].y() <= 1e-6)
+ {
+ zero_pos = list[0].x();
+ list.pop_front();
+ }
+ list.push_front({zero_pos, 0});
+ sz = list.size();
+ }
// now this is hella expensive due to `c_interp'
for (int i = 0; i < sz; i++)
@@ -210,21 +231,32 @@ void spline::update_interp_data() const
const f t2 = t*t;
const f t3 = t*t*t;
- const unsigned x = unsigned(f(.5) * cf * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3));
- const float y = (float)(f(.5) * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3));
-
- int ret = std::fpclassify(y);
- if (ret == FP_NAN || ret == FP_INFINITE)
+ const auto x = (unsigned)(f(.5) * cf * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3));
+ const auto y = (f)(f(.5) * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3));
+
+ switch (int ret = std::fpclassify(y))
+ {
+ case FP_INFINITE:
+ case FP_NAN:
+ case FP_SUBNORMAL:
+ eval_once(qDebug() << "spline: fpclassify" << y
+ << "returned" << ret
+ << "for bundle" << s->b->name());
continue;
-
- if (x < value_count)
- data[x] = y;
+ case FP_ZERO:
+ case FP_NORMAL:
+ if (x < value_count)
+ data[x] = y;
+ break;
+ default:
+ unreachable();
+ }
}
}
}
- float maxy = (float)max_output();
- float last = 0;
+ auto maxy = (f)max_output();
+ auto last = (f)0;
#ifdef __clang__
# pragma clang diagnostic push
@@ -235,10 +267,21 @@ void spline::update_interp_data() const
{
if (data[i] == magic_fill_value)
data[i] = last;
- data[i] = clamp(data[i], 0, maxy);
+ data[i] = std::clamp(data[i], (f)0, (f)maxy);
last = data[i];
}
+ // make sure empty places stay empty (see #1341)
+ if (auto it = std::find_if(list.cbegin(), list.cend(),
+ [](QPointF x) { return x.x() >= (f)1e-6 && x.y() >= (f)1e-6; });
+ it != list.cend() && it != list.cbegin())
+ {
+ it--;
+ unsigned max = std::clamp((unsigned)iround(it->x() * c), 0u, value_count-1);
+
+ for (unsigned x = 0; x < max; x++)
+ data[x] = 0;
+ }
#ifdef __clang__
# pragma clang diagnostic pop
#endif
@@ -361,11 +404,11 @@ void spline::set_bundle(bundle b, const QString& axis_name, Axis axis)
S = s;
conn_points = QObject::connect(&s->points, value_::value_changed<QList<QPointF>>(),
- ctx.get(), [this] { invalidate_settings(); }, Qt::DirectConnection);
+ &*ctx, [this] { invalidate_settings(); }, Qt::DirectConnection);
conn_maxx = QObject::connect(&s->opts.clamp_x_, value_::value_changed<int>(),
- ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection);
+ &*ctx, [this](double) { invalidate_settings(); }, Qt::DirectConnection);
conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(),
- ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection);
+ &*ctx, [this](double) { invalidate_settings(); }, Qt::DirectConnection);
}
emit S->recomputed();
@@ -472,7 +515,7 @@ double spline::bucket_size_coefficient(const QList<QPointF>& points) const
const int sz = element_count(points, maxx);
const double last_x = sz ? points[sz - 1].x() : maxx;
- return clamp((value_count-1) / clamp(last_x, eps, maxx), 0., (value_count-1));
+ return std::clamp((value_count-1) / std::clamp(last_x, eps, maxx), 0., (value_count-1.));
}
void spline::disconnect_signals()
diff --git a/spline/spline.hpp b/spline/spline.hpp
index e4f64069..780442b9 100644
--- a/spline/spline.hpp
+++ b/spline/spline.hpp
@@ -105,12 +105,12 @@ struct OTR_SPLINE_EXPORT base_spline : base_spline_, spline_modify_mixin, spline
class OTR_SPLINE_EXPORT spline : public base_spline
{
- using f = float;
+ using f = double;
double bucket_size_coefficient(const QList<QPointF>& points) const;
void update_interp_data() const;
double get_value_internal(int x) const;
- static bool sort_fn(const QPointF& one, const QPointF& two);
+ static bool sort_fn(QPointF one, QPointF two);
static void ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y);
static int element_count(const QList<QPointF>& points, double max_input);
@@ -125,13 +125,13 @@ class OTR_SPLINE_EXPORT spline : public base_spline
std::shared_ptr<QObject> ctx { std::make_shared<QObject>() };
mutable QPointF last_input_value{-1, -1};
- mutable std::vector<float> data = std::vector<float>(value_count, magic_fill_value);
+ mutable std::vector<f> data = std::vector<f>(value_count, magic_fill_value);
mutable points_t points;
mutable axis_opts::max_clamp clamp_x = axis_opts::x1000, clamp_y = axis_opts::x1000;
mutable bool activep = false;
static constexpr unsigned value_count = 8192;
- static constexpr float magic_fill_value = -(1 << 24) + 1;
+ static constexpr f magic_fill_value = -(1 << 24) + 1;
static constexpr double c_interp = 5;
public: