diff options
Diffstat (limited to 'spline')
-rw-r--r-- | spline/axis-opts.cpp | 3 | ||||
-rw-r--r-- | spline/axis-opts.hpp | 10 | ||||
-rw-r--r-- | spline/lang/de_DE.ts | 4 | ||||
-rw-r--r-- | spline/lang/zh_CN.ts | 2 | ||||
-rw-r--r-- | spline/spline-widget.cpp | 26 | ||||
-rw-r--r-- | spline/spline.cpp | 97 | ||||
-rw-r--r-- | spline/spline.hpp | 8 |
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: |