diff options
| author | Stanislaw Halik <sthalik@misaki.pl> | 2019-01-16 06:11:48 +0100 | 
|---|---|---|
| committer | Stanislaw Halik <sthalik@misaki.pl> | 2019-01-16 07:49:13 +0100 | 
| commit | 07b45ca4578ccaed91f7f3c70e82dc7ffbdf47ab (patch) | |
| tree | 0904b728158414937919f62714358725f52e7400 /spline | |
| parent | 1e04979c3452d4eac633677876a88f9411a1153d (diff) | |
spline: fix deadlock, logic error
Tracking rarely deadlocked when saving mappings.
Investigating it further also shown how a wrong bundle was used for
Accela's splines.
Diffstat (limited to 'spline')
| -rw-r--r-- | spline/axis-opts.cpp | 6 | ||||
| -rw-r--r-- | spline/axis-opts.hpp | 17 | ||||
| -rw-r--r-- | spline/spline-widget.cpp | 83 | ||||
| -rw-r--r-- | spline/spline-widget.hpp | 8 | ||||
| -rw-r--r-- | spline/spline.cpp | 295 | ||||
| -rw-r--r-- | spline/spline.hpp | 57 | 
6 files changed, 243 insertions, 223 deletions
diff --git a/spline/axis-opts.cpp b/spline/axis-opts.cpp index fa527877..489008e6 100644 --- a/spline/axis-opts.cpp +++ b/spline/axis-opts.cpp @@ -27,14 +27,14 @@ static max_clamp get_max_y(Axis k)  }  axis_opts::axis_opts(QString pfx, Axis idx) : +    prefix_(pfx), +    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),      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)), -    prefix_(pfx), -    axis_(idx) +    clamp_y_(b_mapping_window, n(pfx, "max-output-value"), get_max_y(idx))  {}  QString const& axis_opts::prefix() const { return prefix_; } diff --git a/spline/axis-opts.hpp b/spline/axis-opts.hpp index b4475502..c773dd61 100644 --- a/spline/axis-opts.hpp +++ b/spline/axis-opts.hpp @@ -9,8 +9,14 @@ namespace axis_opts_impl {  using namespace options; -struct OTR_SPLINE_EXPORT axis_opts final +class OTR_SPLINE_EXPORT axis_opts final  { +    QString prefix_; +    Axis axis_; + +    static inline QString n(QString const& pfx, QString const& name); + +public:      enum max_clamp      {          r180 = 180, @@ -37,8 +43,8 @@ struct OTR_SPLINE_EXPORT axis_opts final      };      // note, these two bundles can be the same value with no issues -    bundle b_settings_window = make_bundle("opentrack-ui"); -    bundle b_mapping_window = make_bundle("opentrack-mappings"); +    bundle b_settings_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-ui") }; +    bundle b_mapping_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-mappings") };      value<double> zero;      value<int> src;      value<bool> invert, altp; @@ -49,11 +55,6 @@ struct OTR_SPLINE_EXPORT axis_opts final      QString const& prefix() const;      Axis axis() const; -private: -    static inline QString n(QString const& pfx, QString const& name); - -    QString prefix_; -    Axis axis_;  };  } // ns axis_opts_impl diff --git a/spline/spline-widget.cpp b/spline/spline-widget.cpp index bfe8aff1..d329d8e2 100644 --- a/spline/spline-widget.cpp +++ b/spline/spline-widget.cpp @@ -1,10 +1,3 @@ -/* Copyright (c) 2012-2016 Stanislaw Halik <sthalik@misaki.pl> - * - * 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 "spline-widget.hpp"  #include "compat/math.hpp"  #include "compat/macros.hpp" @@ -19,7 +12,7 @@  #include <QDebug> -using namespace spline_detail; +namespace spline_detail {  spline_widget::spline_widget(QWidget *parent) : QWidget(parent)  { @@ -37,7 +30,7 @@ spline_widget::~spline_widget()      }  } -void spline_widget::setConfig(base_spline* spl) +void spline_widget::set_config(base_spline* spl)  {      if (connection)      { @@ -49,13 +42,13 @@ void spline_widget::setConfig(base_spline* spl)      if (spl)      { -        update_range(); - -        std::shared_ptr<base_spline::base_settings> s = spl->get_settings(); -        connection = connect(s.get(), &base_spline::base_settings::recomputed, +        std::shared_ptr<base_settings> s = spl->get_settings(); +        connection = connect(s.get(), &base_settings::recomputed,                               this, [this] { reload_spline(); },                               Qt::QueuedConnection);      } + +    reload_spline();  }  QColor spline_widget::colorBezier() const @@ -71,7 +64,7 @@ void spline_widget::setColorBezier(QColor const& color)  void spline_widget::force_redraw()  { -    background_img = QPixmap(); +    background_img = {};      repaint();  } @@ -146,7 +139,6 @@ void spline_widget::drawBackground()  #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)              metrics.horizontalAdvance(text);  #else -        // XXX remove after Linux upgrades -sh 20181225              metrics.width(text);  #endif @@ -162,7 +154,7 @@ void spline_widget::drawFunction()      QPainter painter(&spline_img);      painter.setRenderHint(QPainter::Antialiasing, true); -    const points_t points = config->get_points(); +    const points_t& points = config->get_points();      if (moving_control_point_idx >= 0 &&          moving_control_point_idx < points.size()) @@ -170,7 +162,7 @@ void spline_widget::drawFunction()          const QPen pen(Qt::white, 1, Qt::SolidLine, Qt::FlatCap);          const QPointF prev_ = point_to_pixel({});          QPointF prev(iround(prev_.x()), iround(prev_.y())); -        for (auto point : points) +        for (const auto& point : points)          {              const QPointF tmp = point_to_pixel(point);              drawLine(painter, prev, tmp, pen); @@ -219,9 +211,9 @@ void spline_widget::drawFunction()      for (double k = 0; k < maxx; k += step*3) // NOLINT      { -        const auto next_1 = (double) config->get_value_no_save(k + step*1); -        const auto next_2 = (double) config->get_value_no_save(k + step*2); -        const auto next_3 = (double) config->get_value_no_save(k + step*3); +        const auto next_1 = config->get_value_no_save(k + step*1); +        const auto next_2 = config->get_value_no_save(k + step*2); +        const auto next_3 = config->get_value_no_save(k + step*3);          QPointF b(clamp(point_to_pixel({k + step*1, next_1}))),                  c(clamp(point_to_pixel({k + step*2, next_2}))), @@ -235,14 +227,14 @@ void spline_widget::drawFunction()      QPointF prev = point_to_pixel({});      for (double i = 0; i < maxx; i += step) // NOLINT      { -        const auto val = (double) config->get_value_no_save(i); +        const auto val = config->get_value_no_save(i);          const QPointF cur = point_to_pixel({i, val});          painter.drawLine(prev, cur);          prev = cur;      }      {          const double maxx = config->max_input(); -        const double maxy = double(config->get_value_no_save(maxx)); +        const double maxy = config->get_value_no_save(maxx);          painter.drawLine(prev, point_to_pixel({ maxx, maxy }));      }  #endif @@ -281,10 +273,12 @@ void spline_widget::paintEvent(QPaintEvent *e)      if (background_img.size() != QSize(W, H))      { -        background_img = QPixmap(W, H); +        update_range(); + +        background_img = { W, H };          background_img.setDevicePixelRatio(dpr); -        draw_function = true;          drawBackground(); +        draw_function = true;      }      if (draw_function) @@ -333,7 +327,7 @@ void spline_widget::mousePressEvent(QMouseEvent *e)      moving_control_point_idx = -1; -    points_t points = config->get_points(); +    const points_t& points = config->get_points();      if (e->button() == Qt::LeftButton)      { @@ -409,7 +403,7 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e)      }      const int i = moving_control_point_idx; -    const points_t points = config->get_points(); +    const points_t& points = config->get_points();      const int sz = points.size();      if (i >= 0 && i < sz) @@ -501,7 +495,7 @@ void spline_widget::mouseReleaseEvent(QMouseEvent *e)  void spline_widget::reload_spline()  {      // don't recompute here as the value's about to be recomputed in the callee -    update_range(); +    background_img = {};      update();  } @@ -519,7 +513,7 @@ void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_)      double x = value.x(), y = value.y();      if (preview_only) -        y = (double)config->get_value_no_save(x); +        y = config->get_value_no_save(x);      const int x_ = iround(x), y_ = iround(y); @@ -563,14 +557,15 @@ void spline_widget::update_range()      const int mwl = 40, mhl = 20;      const int mwr = 15, mhr = 35; -    pixel_bounds = QRect(mwl, mhl, (w - mwl - mwr), (h - mhl - mhr)); -    c = { pixel_bounds.width() / config->max_input(), pixel_bounds.height() / config->max_output() }; -    draw_function = true; - -    background_img = QPixmap(); -    spline_img = QPixmap(); +    pixel_bounds = { mwl, mhl, (w - mwl - mwr), (h - mhl - mhr) }; +    c = { +        pixel_bounds.width() / std::fmax(1, config->max_input()), +        pixel_bounds.height() / std::fmax(1, config->max_output()) +    }; -    repaint(); +    draw_function = true; +    background_img = {}; +    spline_img = {};  }  bool spline_widget::point_within_pixel(const QPointF& pt, const QPointF& pixel) @@ -631,7 +626,7 @@ QPointF spline_widget::point_to_pixel(const QPointF& point)  void spline_widget::resizeEvent(QResizeEvent *)  { -    update_range(); +    reload_spline();  }  bool spline_widget::is_on_pt(const QPointF& pos, int* pt) @@ -643,7 +638,7 @@ bool spline_widget::is_on_pt(const QPointF& pos, int* pt)          return false;      } -    const points_t points = config->get_points(); +    const points_t& points = config->get_points();      for (int i = 0; i < points.size(); i++)      { @@ -659,3 +654,17 @@ bool spline_widget::is_on_pt(const QPointF& pos, int* pt)          *pt = -1;      return false;  } + +void spline_widget::changeEvent(QEvent* e) +{ +    switch (e->type()) +    { +    case QEvent::EnabledChange: +        background_img = {}; spline_img = {}; +        update(); +        break; +    default: +        break; +    } +} +} // ns spline_detail diff --git a/spline/spline-widget.hpp b/spline/spline-widget.hpp index a8adfce1..b0a76e2d 100644 --- a/spline/spline-widget.hpp +++ b/spline/spline-widget.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016 Stanislaw Halik <sthalik@misaki.pl> +/* Copyright (c) 2012-2019 Stanislaw Halik <sthalik@misaki.pl>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -34,12 +34,11 @@ class OTR_SPLINE_EXPORT spline_widget final : public QWidget      Q_PROPERTY(int x_step READ x_step WRITE set_x_step)      Q_PROPERTY(int y_step READ y_step WRITE set_y_step) -    using points_t = base_spline::points_t;  public:      explicit spline_widget(QWidget *parent = nullptr);      ~spline_widget() override; -    void setConfig(base_spline* spl); +    void set_config(base_spline* spl);      QColor colorBezier() const;      void setColorBezier(QColor const& color); @@ -78,6 +77,7 @@ private:      bool is_on_pt(const QPointF& pos, int* pt = nullptr);      void update_range(); +    void changeEvent(QEvent* e) override;      QPointF pixel_to_point(const QPointF& point);      QPointF point_to_pixel(const QPointF& point); @@ -103,7 +103,7 @@ private:      bool draw_function = true, preview_only = false;      // point's circle radius on the widget -    static constexpr inline int point_size_in_pixels_ = 4; +    static constexpr int point_size_in_pixels_ = 4;      const double point_size_in_pixels = point_size_in_pixels_ * std::fmax(1, devicePixelRatioF() * .66);  }; diff --git a/spline/spline.cpp b/spline/spline.cpp index 50812bad..3ec3b5dc 100644 --- a/spline/spline.cpp +++ b/spline/spline.cpp @@ -1,11 +1,3 @@ -/* Copyright (c) 2012-2016, Stanislaw Halik <sthalik@misaki.pl> - - * 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 "spline.hpp"  #include "compat/math.hpp" @@ -18,7 +10,6 @@  #include <QObject>  #include <QMutexLocker> -#include <QCoreApplication>  #include <QPointF>  #include <QSettings>  #include <QString> @@ -27,6 +18,7 @@  namespace spline_detail { +settings::~settings() = default;  base_spline_::~base_spline_() = default;  base_spline::~base_spline() = default;  spline_modify_mixin::~spline_modify_mixin() = default; @@ -40,7 +32,6 @@ spline::spline(const QString& name, const QString& axis_name, Axis axis)  spline::~spline()  {      QMutexLocker l(&mtx); -      disconnect_signals();  } @@ -48,46 +39,53 @@ spline::spline() : spline(QString{}, QString{}, Axis(-1)) {}  void spline::set_tracking_active(bool value)  { -    QMutexLocker l(&mtx); -    activep = value; +    std::shared_ptr<settings> S; +    { +        QMutexLocker l(&mtx); +        S = s; +        activep = value; +    } +    emit S->recomputed();  }  bundle spline::get_bundle()  { -    QMutexLocker l(&mtx); // avoid logic errors due to changes in value<t> data +    QMutexLocker l(&mtx);      return s->b;  }  void spline::clear()  { +    std::shared_ptr<settings> S;      {          QMutexLocker l(&mtx); +        S = s;          s->points = {}; +        points = {}; +        update_interp_data();      } - -    invalidate_settings(); +    emit S->recomputed();  } -float spline::get_value(double x) +double spline::get_value(double x) const  { -    QMutexLocker foo(&mtx); +    QMutexLocker l(&mtx); -    const float ret = get_value_no_save(x); -    last_input_value.setX(std::fabs(x)); -    last_input_value.setY(double(std::fabs(ret))); +    const double ret = get_value_no_save(x); +    last_input_value = { std::fabs(x), std::fabs((double)ret) };      return ret;  } -float spline::get_value_no_save(double x) const +double spline::get_value_no_save(double x) const  { -    QMutexLocker foo(&mtx); +    QMutexLocker l(&mtx); -    float  q  = float(x * bucket_size_coefficient(points)); +    double q  = x * bucket_size_coefficient(points);      int    xi = (int)q; -    float  yi = get_value_internal(xi); -    float  yiplus1 = get_value_internal(xi+1); -    float  f = (q-xi); -    float  ret = yiplus1 * f + yi * (1.0f - f); // at least do a linear interpolation. +    double yi = get_value_internal(xi); +    double yiplus1 = get_value_internal(xi+1); +    double f = (q-xi); +    double ret = yiplus1 * f + yi * (1 - f); // at least do a linear interpolation.      return ret;  } @@ -95,43 +93,47 @@ bool spline::get_last_value(QPointF& point)  {      QMutexLocker foo(&mtx);      point = last_input_value; -    return activep; +    return activep && point.y() >= 0;  } -float spline::get_value_internal(int x) const +double spline::get_value_internal(int x) const  { -    if (!validp) -    { -        update_interp_data(); -        validp = true; -    } -      const float sign = signum(x);      x = std::abs(x); -    const float ret_ = data[std::min(unsigned(x), unsigned(value_count)-1u)]; +    const float ret_ = data[std::min(unsigned(x), value_count - 1)];      return sign * clamp(ret_, 0, 1000);  } -QPointF spline::ensure_in_bounds(const QList<QPointF>& points, int i) +void spline::ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y)  {      const int sz = points.size();      if (i < 0 || sz == 0) -        return {}; - -    if (i < sz) -        return points[i]; - -    return points[sz - 1]; +    { +        x = 0; +        y = 0; +    } +    else if (i < sz) +    { +        const QPointF& pt = points[i]; +        x = (f)pt.x(); +        y = (f)pt.y(); +    } +    else +    { +        const QPointF& pt = points[sz - 1]; +        x = (f)pt.x(); +        y = (f)pt.y(); +    }  }  int spline::element_count(const QList<QPointF>& points, double max_input)  {      const unsigned sz = (unsigned)points.size(); -    for (unsigned k = 0; k < sz; k++) +    for (int k = sz-1; k >= 0; k--)      {          const QPointF& pt = points[k]; -        if (max_input > 1e-4 && pt.x() - 1e-2 > max_input) +        if (max_input > 1e-4 && pt.x() - 1e-4 >= max_input)              return k;      }      return sz; @@ -148,15 +150,11 @@ void spline::update_interp_data() const      ensure_valid(list);      const int sz = list.size(); -    const double maxx = max_input(); -      if (list.isEmpty()) -        list.prepend({ maxx, max_output() }); +        list.prepend({ max_input(), max_output() });      const double c = bucket_size_coefficient(list); -    const double c_interp = c * 30; - -    enum { magic_fill_value = -255 }; +    const double c_ = c * c_interp;      for (unsigned i = 0; i < value_count; i++)          data[i] = magic_fill_value; @@ -166,35 +164,35 @@ 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-2); +        const unsigned max = clamp(uround(x * c), 0, value_count-1);          for (unsigned k = 0; k <= max; k++)              data[k] = float(y * k / max); // no need for bresenham      }      else      { -        if (list[0].x() > 1e-2 && list[0].x() <= maxx) -            list.push_front(QPointF(0, 0)); +        if (list[0].x() > 1e-2) +            list.push_front({});          // now this is hella expensive due to `c_interp'          for (int i = 0; i < sz; i++)          { -            const QPointF p0 = ensure_in_bounds(list, i - 1); -            const QPointF p1 = ensure_in_bounds(list, i + 0); -            const QPointF p2 = ensure_in_bounds(list, i + 1); -            const QPointF p3 = ensure_in_bounds(list, i + 2); -            const double p0_x = p0.x(), p1_x = p1.x(), p2_x = p2.x(), p3_x = p3.x(); -            const double p0_y = p0.y(), p1_y = p1.y(), p2_y = p2.y(), p3_y = p3.y(); - -            const double cx[4] = { +            f p0_x, p1_x, p2_x, p3_x; +            f p0_y, p1_y, p2_y, p3_y; + +            ensure_in_bounds(list, i - 1, p0_x, p0_y); +            ensure_in_bounds(list, i + 0, p1_x, p1_y); +            ensure_in_bounds(list, i + 1, p2_x, p2_y); +            ensure_in_bounds(list, i + 2, p3_x, p3_y); + +            const f cx[4] = {                  2 * p1_x, // 1                  -p0_x + p2_x, // t                  2 * p0_x - 5 * p1_x + 4 * p2_x - p3_x, // t^2                  -p0_x + 3 * p1_x - 3 * p2_x + p3_x, // t3              }; -            const double cy[4] = -            { +            const f cy[4] = {                  2 * p1_y, // 1                  -p0_y + p2_y, // t                  2 * p0_y - 5 * p1_y + 4 * p2_y - p3_y, // t^2 @@ -202,36 +200,37 @@ void spline::update_interp_data() const              };              // multiplier helps fill in all the x's needed -            const unsigned end{int(c_interp * (p2_x - p1_x)) + 1u}; +            const unsigned end = (unsigned)(c_ * (p2_x - p1_x)) + 1; +            const f end_(end);              for (unsigned k = 0; k <= end; k++)              { -                const double t = k / double(end); -                const double t2 = t*t; -                const double t3 = t*t*t; +                const f t = k / end_; +                const f t2 = t*t; +                const f t3 = t*t*t; -                const int x = int(.5 * c * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3)); -                const float y = float(.5 * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3)); +                const unsigned x = unsigned(f(.5) * c * (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)); -                if (unsigned(x) < value_count) +                if (x < value_count)                      data[x] = y;              }          }      } -    double maxy = max_output(); +    float maxy = (float)max_output();      float last = 0;  #ifdef __clang__  #   pragma clang diagnostic push -#   pragma clang diagnostic ignored "-Wfloat-equal" +#   pragma clang diagnostic ignored "-Wfloat-equal" // stupid clang  #endif -    for (unsigned i = 0; i < unsigned(value_count); i++) +    for (unsigned i = 0; i < value_count; i++)      {          if (data[i] == magic_fill_value)              data[i] = last; -        data[i] = clamp(data[i], 0, (float)maxy); +        data[i] = clamp(data[i], 0, maxy);          last = data[i];      } @@ -242,58 +241,72 @@ void spline::update_interp_data() const  void spline::remove_point(int i)  { -    QMutexLocker foo(&mtx); +    std::shared_ptr<settings> S; +    { +        QMutexLocker foo(&mtx); +        S = s; -    const int sz = element_count(points, max_input()); +        const int sz = element_count(points, max_input()); -    if (i >= 0 && i < sz) -    { -        points.erase(points.begin() + i); -        s->points = points; -        validp = false; +        if (i >= 0 && i < sz) +        { +            points.erase(points.begin() + i); +            s->points = points; +            update_interp_data(); +        }      } + +    emit S->recomputed();  }  void spline::add_point(QPointF pt)  { -    QMutexLocker foo(&mtx); - -    points.push_back(pt); -    std::stable_sort(points.begin(), points.end(), sort_fn); -    s->points = points; -    validp = false; +    std::shared_ptr<settings> S; +    { +        QMutexLocker foo(&mtx); +        S = s; +        points.push_back(pt); +        std::stable_sort(points.begin(), points.end(), sort_fn); +        s->points = points; +        update_interp_data(); +    } +    emit S->recomputed();  }  void spline::add_point(double x, double y)  { -    add_point(QPointF(x, y)); +    add_point({ x, y });  }  void spline::move_point(int idx, QPointF pt)  { -    QMutexLocker foo(&mtx); +    std::shared_ptr<settings> S; +    { +        QMutexLocker foo(&mtx); +        S = s; -    const int sz = element_count(points, max_input()); +        const int sz = element_count(points, max_input()); -    if (idx >= 0 && idx < sz) -    { -        points[idx] = pt; -        std::stable_sort(points.begin(), points.end(), sort_fn); -        s->points = points; -        validp = false; +        if (idx >= 0 && idx < sz) +        { +            points[idx] = pt; +            std::stable_sort(points.begin(), points.end(), sort_fn); +            s->points = points; +            update_interp_data(); +        }      } +    emit S->recomputed();  } -const base_spline_::points_t& spline::get_points() const +const points_t& spline::get_points() const  { -    QMutexLocker foo(&mtx);      return points;  }  int spline::get_point_count() const  {      QMutexLocker foo(&mtx); -    return element_count(points, s->opts.clamp_x_); +    return element_count(points, clamp_x);  }  void spline::reload() @@ -308,66 +321,69 @@ void spline::save()      s->b->save();  } +void spline::invalidate_settings_() +{ +    points = s->points; +    clamp_x = s->opts.clamp_x_; +    clamp_y = s->opts.clamp_y_; +    update_interp_data(); +} +  void spline::invalidate_settings()  { +    std::shared_ptr<settings> S;      {          QMutexLocker l(&mtx); -        validp = false; -        points = s->points; +        S = s; +        invalidate_settings_();      } -    emit s->recomputed(); +    emit S->recomputed();  }  void spline::set_bundle(bundle b, const QString& axis_name, Axis axis)  { -    QMutexLocker l(&mtx); +    if (!b) +        b = make_bundle({}); + +    std::shared_ptr<settings> S; -    // gets called from ctor hence the need for nullptr checks -    // the sentinel settings/bundle objects don't need any further branching once created -    if (!s || s->b != b)      { -        disconnect_signals(); +        QMutexLocker l(&mtx); -        if (!b) -            b = make_bundle(QString{}); +        disconnect_signals();          s = std::make_shared<settings>(b, axis_name, axis); - -        conn_changed = QObject::connect(b.get(), &bundle_::changed, -                                        s.get(), [&] { invalidate_settings(); }, Qt::QueuedConnection); -        // this isn't strictly necessary for the spline but helps the widget -        conn_maxx = QObject::connect(&s->opts.clamp_x_, value_::value_changed<int>(), -                                     ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection); -        conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(), -                                     ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection); - -        invalidate_settings(); +        invalidate_settings_(); +        S = s; + +        conn_points = QObject::connect(&s->points, value_::value_changed<QList<QPointF>>(), +                                       ctx.get(), [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); +        conn_maxy   = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(), +                                       ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection);      } + +    emit S->recomputed();  }  double spline::max_input() const  {      QMutexLocker l(&mtx); -    using m = axis_opts::max_clamp; -    const value<m>& clamp = s->opts.clamp_x_; -    if (clamp == m::x1000 && !points.empty()) -        return points[points.size() - 1].x(); -    return std::fabs(clamp.to<double>()); +    if (clamp_x == axis_opts::x1000) +        return std::fmax(1, points.empty() ? 0 : points[points.size() - 1].x()); +    return std::fabs((double)clamp_x);  }  double spline::max_output() const  {      QMutexLocker l(&mtx); -    using m = axis_opts::max_clamp; -    const value<m>& clamp = s->opts.clamp_y_; -    if (clamp == m::x1000 && !points.empty()) -        return points[points.size() - 1].y(); -    return std::fabs(clamp.to<double>()); +    if (clamp_y == axis_opts::x1000 && !points.empty()) +        return std::fmax(1, points.empty() ? 0 : points[points.size() - 1].y()); +    return std::fabs((double)clamp_y);  }  void spline::ensure_valid(points_t& list) const  { -    QMutexLocker foo(&mtx); -      std::stable_sort(list.begin(), list.end(), sort_fn);      const unsigned sz = (unsigned)list.size(); @@ -438,14 +454,15 @@ std::shared_ptr<const base_settings> spline::get_settings() const  double spline::bucket_size_coefficient(const QList<QPointF>& points) const  {      constexpr double eps = 1e-4; -      const double maxx = max_input();      if (maxx < eps)          return 0; -    // needed to fill the buckets up to the last control point. -    // space between that point and max_x doesn't matter. +    // buckets are only used between 0 and the rightmost point's +    // x coordinate. even if `clamp_x' is set to a much larger value, +    // buckets retain more precision lower the x coordinate of the +    // last point.      const int sz = element_count(points, maxx);      const double last_x = sz ? points[sz - 1].x() : maxx; @@ -455,19 +472,17 @@ double spline::bucket_size_coefficient(const QList<QPointF>& points) const  void spline::disconnect_signals()  { -    if (conn_changed) +    if (conn_points)      { -        QObject::disconnect(conn_changed); conn_changed = {}; +        QObject::disconnect(conn_points); conn_points = {};          QObject::disconnect(conn_maxx); conn_maxx = {};          QObject::disconnect(conn_maxy); conn_maxy = {};      }  }  settings::settings(bundle const& b, const QString& axis_name, Axis idx): -    b(b ? b : make_bundle("")), +    b(b ? b : make_bundle({})),      opts(axis_name, idx)  {} -settings::~settings() = default; -  } // ns spline_detail diff --git a/spline/spline.hpp b/spline/spline.hpp index e79dd8cc..a78db8dd 100644 --- a/spline/spline.hpp +++ b/spline/spline.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, Stanislaw Halik <sthalik@misaki.pl> +/* Copyright (c) 2012-2019, Stanislaw Halik <sthalik@misaki.pl>   * Permission to use, copy, modify, and/or distribute this   * software for any purpose with or without fee is hereby granted, @@ -17,7 +17,6 @@  #include <vector>  #include <limits>  #include <memory> -#include <functional>  #include <QObject>  #include <QPointF> @@ -26,6 +25,7 @@  namespace spline_detail { +using points_t = QList<QPointF>;  using namespace options;  class OTR_SPLINE_EXPORT base_settings : public QObject @@ -49,10 +49,9 @@ public:  struct OTR_SPLINE_EXPORT base_spline_  {      virtual ~base_spline_(); -    base_spline_& operator=(const base_spline_&) = default; -    virtual float get_value(double x) = 0; -    virtual float get_value_no_save(double x) const = 0; +    virtual double get_value(double x) const = 0; +    virtual double get_value_no_save(double x) const = 0;      [[nodiscard]] virtual bool get_last_value(QPointF& point) = 0;      virtual void set_tracking_active(bool value) = 0; @@ -60,18 +59,14 @@ struct OTR_SPLINE_EXPORT base_spline_      virtual double max_input() const = 0;      virtual double max_output() const = 0; -    using points_t = QList<QPointF>; - -    virtual points_t const& get_points() const = 0; +    virtual const points_t& get_points() const = 0;      virtual int get_point_count() const = 0;  };  struct OTR_SPLINE_EXPORT spline_settings_mixin  { -    using base_settings = spline_detail::base_settings; - -    virtual std::shared_ptr<spline_detail::base_settings> get_settings() = 0; -    virtual std::shared_ptr<const spline_detail::base_settings> get_settings() const = 0; +    virtual std::shared_ptr<base_settings> get_settings() = 0; +    virtual std::shared_ptr<const base_settings> get_settings() const = 0;      virtual ~spline_settings_mixin();  }; @@ -94,31 +89,34 @@ struct OTR_SPLINE_EXPORT base_spline : base_spline_, spline_modify_mixin, spline  class OTR_SPLINE_EXPORT spline : public base_spline  { +    using f = float; +      double bucket_size_coefficient(const QList<QPointF>& points) const;      void update_interp_data() const; -    float get_value_internal(int x) const; +    double get_value_internal(int x) const;      static bool sort_fn(const QPointF& one, const QPointF& two); -    static QPointF ensure_in_bounds(const QList<QPointF>& points, int i); +    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);      void disconnect_signals(); +    void invalidate_settings_();      mutex mtx { mutex::Recursive }; -    std::shared_ptr<spline_detail::settings> s; -    QMetaObject::Connection conn_changed, conn_maxx, conn_maxy; -    mutable std::vector<float> data = std::vector<float>(value_count, float(-16)); -    mutable QPointF last_input_value; +    std::shared_ptr<settings> s; +    QMetaObject::Connection conn_points, conn_maxx, conn_maxy;      std::shared_ptr<QObject> ctx { std::make_shared<QObject>() }; -    // cached s->points +    mutable QPointF last_input_value{-1, -1}; +    mutable std::vector<float> data = std::vector<float>(value_count, magic_fill_value);      mutable points_t points; - -    static constexpr inline std::size_t value_count = 4096; - +    mutable axis_opts::max_clamp clamp_x = axis_opts::x1000, clamp_y = axis_opts::x1000;      mutable bool activep = false; -    mutable bool validp = false; + +    static constexpr unsigned value_count = 8192; +    static constexpr float magic_fill_value = -(1 << 24) + 1; +    static constexpr double c_interp = 5;  public:      void invalidate_settings(); @@ -134,11 +132,10 @@ public:      spline(const QString& name, const QString& axis_name, Axis axis);      ~spline() override; -    spline& operator=(const spline&) = default;      spline(const spline&) = default; -    float get_value(double x) override; -    float get_value_no_save(double x) const override; +    double get_value(double x) const override; +    double get_value_no_save(double x) const override;      [[nodiscard]] bool get_last_value(QPointF& point) override;      void add_point(QPointF pt) override; @@ -147,18 +144,16 @@ public:      void remove_point(int i) override;      void clear() override; -    points_t const& get_points() const override; +    const points_t& get_points() const override;      void set_tracking_active(bool value) override;      bundle get_bundle();      void ensure_valid(points_t& in_out) const; -    std::shared_ptr<spline_detail::base_settings> get_settings() override; -    std::shared_ptr<const spline_detail::base_settings> get_settings() const override; +    std::shared_ptr<base_settings> get_settings() override; +    std::shared_ptr<const base_settings> get_settings() const override;      int get_point_count() const override; - -    using settings = spline_detail::settings;  };  } // ns spline_detail  | 
