diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2018-10-29 09:03:41 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2018-10-29 08:25:58 +0000 |
commit | 57f4c28c8e293f9ba7275786fe502dd966e344b9 (patch) | |
tree | b9b70f3b7bf19930740fcbc567172e7c6cffeb99 /spline/spline.cpp | |
parent | 698733a531c3d85056e74d4a3f3ae1f671bd229d (diff) |
spline: try fix rare infinite loop
- fix floats not equal to themselves infinite loop; check if any
elements were removed instead
- do sort in-place to avoid potentially sorting twice
in `update_interp_data'
- simplify lerp loop
- define magic value
Diffstat (limited to 'spline/spline.cpp')
-rw-r--r-- | spline/spline.cpp | 98 |
1 files changed, 52 insertions, 46 deletions
diff --git a/spline/spline.cpp b/spline/spline.cpp index d8cd19b2..c240ebbd 100644 --- a/spline/spline.cpp +++ b/spline/spline.cpp @@ -157,36 +157,33 @@ void spline::update_interp_data() const double maxx = max_input(); - if (sz == 0) + if (list.isEmpty()) list.prepend({ maxx, max_output() }); - std::stable_sort(list.begin(), list.begin() + sz, sort_fn); - const double c = bucket_size_coefficient(list); const double c_interp = c * 30; + enum { magic_fill_value = -255 }; + for (unsigned i = 0; i < value_count; i++) - data[i] = -16; + data[i] = magic_fill_value; - if (sz < 2) + if (sz < 2) // lerp only { - if (list[0].x() - 1e-2 < maxx) - { - const double x = list[0].x(); - const double y = list[0].y(); - const unsigned max = (unsigned)clamp(iround(x * c), 1, value_count-1); - for (unsigned k = 0; k <= max; k++) - { - if (k < value_count) - data[unsigned(k)] = float(y * k / max); - } - } + 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); + + 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)); + // 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); @@ -232,7 +229,7 @@ void spline::update_interp_data() float last = 0; for (unsigned i = 0; i < unsigned(value_count); i++) { - if (data[i] == -16) + if (data[i] == magic_fill_value) data[i] = last; last = data[i]; } @@ -311,9 +308,11 @@ void spline::invalidate_settings() // we're holding the mutex to allow signal disconnection in spline dtor // before this slot gets called for the next time - QMutexLocker l(&_mutex); - validp = false; - points = s->points; + { + QMutexLocker l(&_mutex); + validp = false; + points = s->points; + } emit s->recomputed(); } @@ -332,12 +331,12 @@ void spline::set_bundle(bundle b, const QString& axis_name, Axis axis) s = std::make_shared<settings>(b, axis_name, axis); conn_changed = QObject::connect(b.get(), &bundle_::changed, - s.get(), [&] { invalidate_settings(); }); + 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(); }); + ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection); conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(), - ctx.get(), [&](double) { invalidate_settings(); }); + ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection); invalidate_settings(); } @@ -363,57 +362,64 @@ double spline::max_output() const return std::fabs(clamp.to<double>()); } -void spline::ensure_valid(points_t& points_) +void spline::ensure_valid(points_t& list) { QMutexLocker foo(&_mutex); - QList<QPointF> list = points_; - // storing to s->points fires bundle::changed and that leads to an infinite loop // thus, only store if we can't help it std::stable_sort(list.begin(), list.end(), sort_fn); const int sz = list.size(); - QList<QPointF> ret_, tmp; - ret_.reserve(sz), tmp.reserve(sz); + QList<QPointF> all_points, tmp; + all_points.reserve(sz), tmp.reserve(sz); const double maxx = max_input(), maxy = max_output(); for (int i = 0; i < sz; i++) { - QPointF& pt(list[i]); + QPointF& pt{list[i]}; - const bool overlap = progn( - for (int j = 0; j < i; j++) + bool overlap = false; + for (int j = i+1; j < sz; j++) + { + const QPointF& pt2{list[j]}; + const QPointF tmp(pt - pt2); + const double dist_sq = QPointF::dotProduct(tmp, tmp); + constexpr double min_dist = 1e-4; + if (dist_sq < min_dist) { - const QPointF& pt2(list[j]); - const QPointF tmp(pt - pt2); - const double dist_sq = QPointF::dotProduct(tmp, tmp); - const double overlap = maxx / 500.; - if (dist_sq < overlap * overlap) - return true; + overlap = true; + break; } - return false; - ); + } if (!overlap) - tmp.append(pt); - - if (pt.x() - 1e-2 < maxx && pt.x() >= 0 && - pt.y() - 1e-2 < maxy && pt.y() >= 0 && !overlap) { - ret_.push_back(pt); + tmp.append(pt); // all points total + + // points within selected limit, for use in `update_interp_data' + if (pt.x() - 1e-4 <= maxx && pt.x() >= 0) + all_points.push_back(pt); } } - if (ret_ != points_) + // size check guards against livelock with value<t>/bundle_ signals + + // points that are within bounds + if (tmp.size() < points.size()) { points = std::move(tmp); + // can't stuff there unconditionally + // fires signals from `value<t>' and `bundle_' otherwise s->points = points; - points_ = std::move(ret_); } + if (all_points.size() < list.size()) + // all points that don't overlap, after clamping + list = std::move(all_points); + last_input_value = {}; activep = false; } |