summaryrefslogtreecommitdiffhomepage
path: root/spline/spline.cpp
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2018-10-29 09:03:41 +0100
committerStanislaw Halik <sthalik@misaki.pl>2018-10-29 08:25:58 +0000
commit57f4c28c8e293f9ba7275786fe502dd966e344b9 (patch)
treeb9b70f3b7bf19930740fcbc567172e7c6cffeb99 /spline/spline.cpp
parent698733a531c3d85056e74d4a3f3ae1f671bd229d (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.cpp98
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;
}