diff options
Diffstat (limited to 'spline-widget/spline.cpp')
-rw-r--r-- | spline-widget/spline.cpp | 315 |
1 files changed, 127 insertions, 188 deletions
diff --git a/spline-widget/spline.cpp b/spline-widget/spline.cpp index 48b508ef..da67f0d4 100644 --- a/spline-widget/spline.cpp +++ b/spline-widget/spline.cpp @@ -10,31 +10,29 @@ #include <QMutexLocker> #include <QCoreApplication> #include <QPointF> -#include <QList> -#include <QtAlgorithms> -#include <QtAlgorithms> #include <QSettings> -#include <QPixmap> #include <QString> #include <algorithm> #include <cmath> #include <QDebug> +constexpr int spline::value_count; + void spline::setTrackingActive(bool blnActive) { activep = blnActive; } -spline::spline() : spline(0, 0) +spline::spline() : spline(0, 0, "") { } void spline::removeAllPoints() { - QMutexLocker foo(&_mutex); - cur.input.clear(); - reload(); + QMutexLocker l(&_mutex); + s->points = points_t(); + update_interp_data(); } void spline::setMaxInput(qreal max_input) @@ -61,30 +59,28 @@ qreal spline::maxOutput() const return max_y; } -spline::spline(qreal maxx, qreal maxy) : +// todo try non-recursive mtx +spline::spline(qreal maxx, qreal maxy, const QString& name) : + data(value_count, -1.f), _mutex(QMutex::Recursive), - max_x(0), - max_y(0), + max_x(maxx), + max_y(maxy), activep(false) { - setMaxInput(maxx); - setMaxOutput(maxy); - if (cur.input.size() == 0) - cur.input.push_back(QPointF(maxx, maxy)); - reload(); + set_bundle(options::make_bundle(name)); } -float spline::getValue(float x) +float spline::getValue(double x) { QMutexLocker foo(&_mutex); - float q = x * precision(); + float q = float(x * precision(s->points)); int xi = (int)q; float yi = getValueInternal(xi); float yiplus1 = getValueInternal(xi+1); float f = (q-xi); float ret = yiplus1 * f + yi * (1.0f - f); // at least do a linear interpolation. last_input_value.setX(std::fabs(x)); - last_input_value.setY(std::fabs(ret)); + last_input_value.setY(double(std::fabs(ret))); return ret; } @@ -100,244 +96,187 @@ float spline::getValueInternal(int x) float sign = x < 0 ? -1 : 1; x = abs(x); float ret; - unsigned sz = cur.data.size(); - if (sz == 0) - ret = 0; - else - ret = cur.data[std::min(unsigned(x), sz-1u)]; + ret = data[std::min(unsigned(x), unsigned(value_count)-1u)]; return ret * sign; } -static QPointF ensureInBounds(const QList<QPointF>& points, int i) +void spline::add_lone_point() +{ + points_t points; + points.push_back(QPointF(max_x, max_y)); + + s->points = points; +} + +QPointF spline::ensure_in_bounds(const QList<QPointF>& points, int i) { - int siz = points.size(); - if (siz == 0 || i < 0) + const int sz = points.size(); + + if (i < 0 || sz == 0) return QPointF(0, 0); - if (siz > i) + if (i < sz) return points[i]; - return points[siz - 1]; + return points[sz - 1]; } -static bool sortFn(const QPointF& one, const QPointF& two) +bool spline::sort_fn(const QPointF& one, const QPointF& two) { return one.x() < two.x(); } -void spline::reload() +void spline::update_interp_data() { - if (cur.input.size()) - { - std::stable_sort(cur.input.begin(), cur.input.end(), sortFn); + points_t points = s->points; - QList<QPointF> input = cur.input; - auto& data = cur.data; + if (points.size() == 0) + points.append(QPointF(max_x, max_y)); - data = std::vector<float>(value_count); - const float mult = precision(); - const unsigned mult_ = unsigned(mult * 30); + std::stable_sort(points.begin(), points.end(), sort_fn); - const unsigned sz = data.size(); + const double mult = precision(points); + const double mult_ = mult * 30; - for (unsigned i = 0; i < sz; i++) - data[i] = -1; + for (unsigned i = 0; i < value_count; i++) + data[i] = -1; - if (input.size() == 1 && input[0].x() > 1e-2) + if (points.size() == 1) + { + const double x = points[0].x(); + const double y = points[0].y(); + const int max = clamp(int(x * precision(points)), 0, value_count-1); + for (int k = 0; k <= max; k++) { - const float x = float(input[0].x()); - const float y = float(input[0].y()); - const unsigned max = unsigned(x * mult); - for (unsigned k = 0; k < max; k++) { - if (k < sz) - data[k] = y * k / max; - } + if (k < value_count) + data[unsigned(k)] = float(y * k / max); } - else if (input[0].x() > 1e-2) - input.prepend(QPointF(0, 0)); + } + else + { + if (points[0].x() > 1e-2) + points.push_front(QPointF(0, 0)); - for (int i = 0; i < int(sz); i++) + for (int i = 0; i < points.size(); i++) { - const QPointF p0 = ensureInBounds(input, i - 1); - const QPointF p1 = ensureInBounds(input, i); - const QPointF p2 = ensureInBounds(input, i + 1); - const QPointF p3 = ensureInBounds(input, i + 2); + const QPointF p0 = ensure_in_bounds(points, i - 1); + const QPointF p1 = ensure_in_bounds(points, i); + const QPointF p2 = ensure_in_bounds(points, i + 1); + const QPointF p3 = ensure_in_bounds(points, i + 2); - const float p0_x = p0.x(), p1_x = p1.x(), p2_x = p2.x(), p3_x = p3.x(); - const float p0_y = p0.y(), p1_y = p1.y(), p2_y = p2.y(), p3_y = p3.y(); + 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(); // multiplier helps fill in all the x's needed - const unsigned end = std::min(sz, unsigned(p2_x * mult_)); - const unsigned start = unsigned(p1_x * mult); + const unsigned end = std::min(unsigned(value_count), unsigned(p2_x * mult_)); + const unsigned start = std::max(0u, unsigned(p1_x * mult)); for (unsigned j = start; j < end; j++) { - const float t = (j - start) / (float) (end - start); - const float t2 = t*t; - const float t3 = t*t*t; - - const int x = .5f * ((2 * p1_x) + - (-p0_x + p2_x) * t + - (2 * p0_x - 5 * p1_x + 4 * p2_x - p3_x) * t2 + - (-p0_x + 3 * p1_x - 3 * p2_x + p3_x) * t3) - * mult; - - const float y = .5f * ((2 * p1_y) + - (-p0_y + p2_y) * t + - (2 * p0_y - 5 * p1_y + 4 * p2_y - p3_y) * t2 + - (-p0_y + 3 * p1_y - 3 * p2_y + p3_y) * t3); - - if (x >= 0 && x < (int)sz) - data[x] = y; + const double t = (j - start) / (double) (end - start); + const double t2 = t*t; + const double t3 = t*t*t; + + const int x = int(.5 * ((2 * p1_x) + + (-p0_x + p2_x) * t + + (2 * p0_x - 5 * p1_x + 4 * p2_x - p3_x) * t2 + + (-p0_x + 3 * p1_x - 3 * p2_x + p3_x) * t3) + * mult); + + const float y = float(.5 * ((2 * p1_y) + + (-p0_y + p2_y) * t + + (2 * p0_y - 5 * p1_y + 4 * p2_y - p3_y) * t2 + + (-p0_y + 3 * p1_y - 3 * p2_y + p3_y) * t3)); + + if (x >= 0 && x < value_count) + data[unsigned(x)] = y; } } + } - float last = 0; - for (unsigned i = 0; i < sz; i++) - { - if (data[i] < 0) - data[i] = last; - last = data[i]; - } + float last = 0; + for (unsigned i = 0; i < unsigned(value_count); i++) + { + if (data[i] < 0) + data[i] = last; + last = data[i]; } - else - cur.data.clear(); } void spline::removePoint(int i) { QMutexLocker foo(&_mutex); - if (i >= 0 && i < cur.input.size()) + + points_t points = s->points; + + if (i >= 0 && i < points.size()) { - cur.input.removeAt(i); - reload(); + points.erase(points.begin() + i); + s->points = points; + update_interp_data(); } } void spline::addPoint(QPointF pt) { QMutexLocker foo(&_mutex); - cur.input.append(pt); - reload(); - std::stable_sort(cur.input.begin(), cur.input.end(), sortFn); + + points_t points = s->points; + points.push_back(pt); + std::stable_sort(points.begin(), points.end(), sort_fn); + s->points = points; + update_interp_data(); } void spline::movePoint(int idx, QPointF pt) { QMutexLocker foo(&_mutex); - if (idx >= 0 && idx < cur.input.size()) + + points_t points = s->points; + + if (idx >= 0 && idx < points.size()) { - cur.input[idx] = pt; + points[idx] = pt; // we don't allow points to be reordered, but sort due to possible caller logic error - std::stable_sort(cur.input.begin(), cur.input.end(), sortFn); - reload(); + std::stable_sort(points.begin(), points.end(), sort_fn); + s->points = points; + update_interp_data(); } } -const QList<QPointF> spline::getPoints() +QList<QPointF> spline::getPoints() const { QMutexLocker foo(&_mutex); - return cur.input; + return s->points; } -void spline::invalidate_unsaved_settings() +void spline::reload() { - QMutexLocker foo(&_mutex); - cur = saved; - reload(); + if (s && s->b) + s->b->reload(); } -void spline::loadSettings(QSettings& settings, const QString& title) +void spline::save() { - QMutexLocker foo(&_mutex); - QPointF newPoint; - QList<QPointF> points; - settings.beginGroup(QString("Curves-%1").arg(title)); - - int max = settings.value("point-count", 0).toInt(); - - for (int i = 0; i < max; i++) - { - newPoint = QPointF(settings.value(QString("point-%1-x").arg(i), 0).toDouble(), - settings.value(QString("point-%1-y").arg(i), 0).toDouble()); - if (newPoint.x() > max_x) - newPoint.setX(max_x); - if (newPoint.y() > max_y) - newPoint.setY(max_y); - points.append(newPoint); - } - - settings.endGroup(); - - if (max == 0) - points.append(QPointF(maxInput(), maxOutput())); - - cur.input = points; - reload(); - saved = cur; + if (s && s->b) + s->b->save(); } -bool spline::State::operator==(const State& other) const +void spline::set_bundle(bundle b) { - if (input.size() != other.input.size()) - return false; - - const int sz = input.size(); - - using std::fabs; + if (b) + s = std::unique_ptr<settings>(new settings(b)); + else + s = std::unique_ptr<settings>(new settings(options::make_bundle(""))); - for (int i = 0; i < sz; i++) - { - const qreal eps = 1e-3; + last_input_value = QPointF(0, 0); + activep = false; - if (fabs(input[i].x() - other.input[i].x()) > eps || - fabs(input[i].y() - other.input[i].y()) > eps) - { - return false; - } - } - return true; + update_interp_data(); } -void spline::saveSettings(QSettings& settings, const QString& title) +int spline::precision(const QList<QPointF>& points) const { - QMutexLocker foo(&_mutex); - - if (cur == saved) - return; + if (points.size()) + return int(value_count / clamp(points[points.size() - 1].x(), 1, max_x)); - qDebug() << "spline-widget: saving" << title; - - settings.beginGroup(QStringLiteral("Curves-%1").arg(title)); - - if (cur.input.size() == 0) - cur.input.push_back(QPointF(max_x, max_y)); - - const int max = cur.input.size(); - settings.setValue("point-count", max); - - for (int i = 0; i < max; i++) - { - settings.setValue(QString("point-%1-x").arg(i), cur.input[i].x()); - settings.setValue(QString("point-%1-y").arg(i), cur.input[i].y()); - } - - for (int i = max; true; i++) - { - QString x = QString("point-%1-x").arg(i); - if (!settings.contains(x)) - break; - settings.remove(x); - settings.remove(QString("point-%1-y").arg(i)); - } - - saved = cur; - - settings.endGroup(); -} - - -int spline::precision() const -{ - if (cur.input.size()) - return (value_count-1) / std::max<float>(1.f, (cur.input[cur.input.size() - 1].x())); - return 1; + return value_count / clamp(int(max_x), 1, value_count); } |