diff options
Diffstat (limited to 'spline/spline.cpp')
-rw-r--r-- | spline/spline.cpp | 97 |
1 files changed, 70 insertions, 27 deletions
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() |