diff options
Diffstat (limited to 'spline-widget')
-rw-r--r-- | spline-widget/spline-widget.cpp | 74 | ||||
-rw-r--r-- | spline-widget/spline-widget.hpp | 22 | ||||
-rw-r--r-- | spline-widget/spline.cpp | 315 | ||||
-rw-r--r-- | spline-widget/spline.hpp | 68 |
4 files changed, 237 insertions, 242 deletions
diff --git a/spline-widget/spline-widget.cpp b/spline-widget/spline-widget.cpp index 1f049339..85477ba7 100644 --- a/spline-widget/spline-widget.cpp +++ b/spline-widget/spline-widget.cpp @@ -5,8 +5,6 @@ * copyright notice and this permission notice appear in all copies. */ -#include "compat/options.hpp" -using namespace options; #include "spline-widget.hpp" #include <QPainter> #include <QPaintEvent> @@ -28,13 +26,21 @@ spline_widget::spline_widget(QWidget *parent) : setMouseTracking(true); } -void spline_widget::setConfig(spline* config, const QString& name) +void spline_widget::setConfig(spline* config, bundle b) { - mem<QSettings> iniFile = group::ini_file(); - if (name != "") - config->loadSettings(*iniFile, name); + if (config && config->get_bundle() && config->get_bundle() != b) + { + QObject::disconnect(connection); + connection = QMetaObject::Connection(); + } + + if (config && config->get_bundle() != b) + config->set_bundle(b); _config = config; - _draw_function = true; + if (b) + connection = connect(b.get(), &bundle_type::reloading, + this, &spline_widget::reload_spline, + Qt::QueuedConnection); update_range(); update(); } @@ -76,11 +82,11 @@ void spline_widget::drawBackground() QPen pen(color__, 1, Qt::SolidLine); const int xstep = 10, ystep = 10; - const qreal maxx = _config->maxInput() + ystep; - const qreal maxy = _config->maxOutput() + xstep; + const qreal maxx = _config->maxInput(); + const qreal maxy = _config->maxOutput(); // horizontal grid - for (int i = 0; i < maxy; i += xstep) + for (int i = 0; i <= maxy; i += xstep) { const qreal y = pixel_bounds.height() - i * c.y() + pixel_bounds.y(); drawLine(&painter, @@ -95,7 +101,7 @@ void spline_widget::drawBackground() } // vertical grid - for (int i = 0; i < maxx; i += ystep) + for (int i = 0; i <= maxx; i += ystep) { const qreal x = pixel_bounds.x() + i * c.x(); drawLine(&painter, @@ -120,7 +126,7 @@ void spline_widget::drawFunction() QPainter painter(&_function); painter.setRenderHint(QPainter::Antialiasing, true); - QList<QPointF> points = _config->getPoints(); + points_t points = _config->getPoints(); const int alpha = !isEnabled() ? 64 : 120; if (!_preview_only) @@ -158,14 +164,14 @@ void spline_widget::drawFunction() QPointF prev = point_to_pixel(QPointF(0, 0)); for (qreal i = 0; i < max; i += step) { - const qreal val = qreal(_config->getValue(float(i))); + const qreal val = qreal(_config->getValue(i)); QPointF cur = point_to_pixel(QPointF(i, val)); painter.drawLine(prev, cur); prev = cur; } { - const qreal val = _config->getValue(float(max)); + const qreal val = qreal(_config->getValue(max)); QPointF last = point_to_pixel(QPointF(max, val)); painter.drawLine(prev, last); } @@ -183,7 +189,8 @@ void spline_widget::paintEvent(QPaintEvent *e) drawBackground(); } - if (_draw_function) { + if (_draw_function) + { _draw_function = false; drawFunction(); } @@ -193,13 +200,13 @@ void spline_widget::paintEvent(QPaintEvent *e) if (_config) { QPen pen(Qt::white, 1, Qt::SolidLine); - QList<QPointF> points = _config->getPoints(); + points_t points = _config->getPoints(); if (points.size() && moving_control_point_idx >= 0 && moving_control_point_idx < points.size()) { if (points[0].x() > 1e-2) - points.prepend(QPointF(0, 0)); + points.push_front(QPointF(0, 0)); QPointF prev = point_to_pixel(points[0]); for (int i = 1; i < points.size(); i++) { @@ -245,7 +252,7 @@ void spline_widget::mousePressEvent(QMouseEvent *e) { if (!_config || !isEnabled()) return; - QList<QPointF> points = _config->getPoints(); + points_t points = _config->getPoints(); if (e->button() == Qt::LeftButton) { bool bTouchingPoint = false; @@ -257,7 +264,7 @@ void spline_widget::mousePressEvent(QMouseEvent *e) if (point_within_pixel(points[i], e->pos())) { bTouchingPoint = true; - moving_control_point_idx = i; + moving_control_point_idx = int(i); break; } } @@ -269,7 +276,7 @@ void spline_widget::mousePressEvent(QMouseEvent *e) for (int i = 0; i < points.size(); i++) { const QPointF pt = point_to_pixel(points[i]); - const auto x = pt.x() - pos.x(); + const qreal x = pt.x() - pos.x(); if (point_closeness_limit * point_closeness_limit >= x * x) { too_close = true; @@ -313,11 +320,13 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e) if (!_config || !isEnabled()) return; - QList<QPointF> points = _config->getPoints(); + points_t points = _config->getPoints(); - if (moving_control_point_idx != -1 && + if (moving_control_point_idx >= 0 && moving_control_point_idx < points.size()) { + const int idx = moving_control_point_idx; + setCursor(Qt::ClosedHandCursor); bool overlap = false; @@ -328,9 +337,9 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e) for (int i = 0; i < 2; i++) { bool bad = false; - if (moving_control_point_idx + 1 < points.size()) + if (idx + 1 < points.size()) { - auto other = points[moving_control_point_idx+1]; + auto other = points[idx+1]; auto other_pix = point_to_pixel(other); bad = pix.x() + point_closeness_limit > other_pix.x(); if (i == 0 && bad) @@ -341,9 +350,9 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e) else overlap |= bad; } - if (moving_control_point_idx != 0) + if (idx != 0) { - auto other = points[moving_control_point_idx-1]; + auto other = points[idx-1]; auto other_pix = point_to_pixel(other); bad = pix.x() - point_closeness_limit < other_pix.x(); if (i == 0 && bad) @@ -360,8 +369,8 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e) if (!overlap) { - points[moving_control_point_idx] = new_pt; - _config->movePoint(moving_control_point_idx, new_pt); + points[idx] = new_pt; + _config->movePoint(int(idx), new_pt); _draw_function = true; update(); } @@ -406,6 +415,15 @@ void spline_widget::mouseReleaseEvent(QMouseEvent *e) } } +void spline_widget::reload_spline() +{ + if (_config && _config->get_bundle() != nullptr) + { + _config->set_bundle(_config->get_bundle()); + update_range(); + } +} + void spline_widget::update_range() { if (!_config) diff --git a/spline-widget/spline-widget.hpp b/spline-widget/spline-widget.hpp index 1dbdb8e1..9ad4f598 100644 --- a/spline-widget/spline-widget.hpp +++ b/spline-widget/spline-widget.hpp @@ -9,22 +9,29 @@ #pragma once +#include "spline.hpp" +#include "api/plugin-api.hpp" +#include "options/options.hpp" #include <QWidget> #include <QtGui> +#include <QMetaObject> #include <QPointF> -#include "spline.hpp" -#include "api/plugin-api.hpp" +#include <QDebug> +using namespace options; -class SPLINE_WIDGET_EXPORT spline_widget : public QWidget +class SPLINE_WIDGET_EXPORT spline_widget final : public QWidget { Q_OBJECT Q_PROPERTY(QColor colorBezier READ colorBezier WRITE setColorBezier) Q_PROPERTY(bool is_preview_only READ is_preview_only WRITE set_preview_only) + + using points_t = spline::points_t; public: spline_widget(QWidget *parent = 0); spline* config(); - void setConfig(spline* config, const QString &name); + + void setConfig(spline* config, bundle b); QColor colorBezier() const { @@ -49,6 +56,7 @@ protected slots: void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; + void reload_spline(); private: void drawBackground(); void drawFunction(); @@ -68,13 +76,17 @@ private: // bounds of the rectangle user can interact with QRectF pixel_bounds; - int moving_control_point_idx; QPointF c; QColor spline_color; QPixmap _background; QPixmap _function; + + QMetaObject::Connection connection; + + int moving_control_point_idx; + int snap_x, snap_y; bool _draw_function, _preview_only; 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); } diff --git a/spline-widget/spline.hpp b/spline-widget/spline.hpp index 26775402..4f6531b9 100644 --- a/spline-widget/spline.hpp +++ b/spline-widget/spline.hpp @@ -8,15 +8,16 @@ #pragma once -#include <QtGlobal> -#include <QList> +#include <QObject> #include <QPointF> #include <QString> -#include <QSettings> #include <QMutex> #include <vector> #include <limits> +#include <memory> #include "compat/qcopyable-mutex.hpp" +#include "options/options.hpp" +using namespace options; #ifdef BUILD_spline_widget # define SPLINE_WIDGET_EXPORT Q_DECL_EXPORT @@ -24,47 +25,72 @@ # define SPLINE_WIDGET_EXPORT Q_DECL_IMPORT #endif -class SPLINE_WIDGET_EXPORT spline +class SPLINE_WIDGET_EXPORT spline final { private: - int precision() const; - void reload(); - float getValueInternal(int x); - - struct State + struct settings { - QList<QPointF> input; - std::vector<float> data; - bool operator==(const State& s) const; + bundle b; + value<QList<QPointF>> points; + settings(bundle b) : + b(b), + points(b, "points", QList<QPointF>()) + {} }; + ptr<settings> s; + + std::vector<float> data; + using interp_data_t = decltype(data); + + static constexpr int value_count = 10000; + + int precision(const QList<QPointF>& points) const; + void update_interp_data(); + float getValueInternal(int x); + void add_lone_point(); + static bool sort_fn(const QPointF& one, const QPointF& two); + + static QPointF ensure_in_bounds(const QList<QPointF>& points, int i); + MyMutex _mutex; QPointF last_input_value; - State cur, saved; qreal max_x, max_y; volatile bool activep; - static constexpr int value_count = 10000; + template<typename t, typename u, typename w> + static inline auto clamp(t val, u min, w max) -> decltype (val * min * max) + { + if (val > max) + return max; + if (val < min) + return min; + return val; + } + public: + void reload(); + void save(); + void set_bundle(bundle b); + qreal maxInput() const; qreal maxOutput() const; spline(); - spline(qreal maxx, qreal maxy); + spline(qreal maxx, qreal maxy, const QString& name); - float getValue(float x); + float getValue(double x); bool getLastPoint(QPointF& point); void removePoint(int i); void removeAllPoints(); void addPoint(QPointF pt); void movePoint(int idx, QPointF pt); - const QList<QPointF> getPoints(); + QList<QPointF> getPoints() const; void setMaxInput(qreal MaxInput); void setMaxOutput(qreal MaxOutput); - void saveSettings(QSettings& settings, const QString& title); - void loadSettings(QSettings& settings, const QString& title); - void invalidate_unsaved_settings(); - void setTrackingActive(bool blnActive); + bundle get_bundle() { return s->b; } + + using points_t = decltype(s->points.get()); }; |