From 783739a2f3599e00869f907ed1747491b35486b1 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Tue, 28 Mar 2017 10:49:19 +0200 Subject: rename spline-widget -> spline Adjust usages. --- CMakeLists.txt | 2 +- filter-accela/CMakeLists.txt | 2 +- filter-accela/accela-settings.hpp | 2 +- filter-accela/ftnoir_filter_accela_dialog.cpp | 2 +- gui/CMakeLists.txt | 2 +- gui/mapping-window.cpp | 2 +- gui/mapping-window.ui | 2 +- logic/CMakeLists.txt | 2 +- logic/mappings.hpp | 2 +- logic/tracker.h | 2 +- migration/CMakeLists.txt | 2 +- spline-widget/CMakeLists.txt | 2 - .../broken/qfunctionconfiguratorplugin.cpp | 111 ---- spline-widget/broken/qfunctionconfiguratorplugin.h | 36 -- spline-widget/export.hpp | 27 - spline-widget/lang/nl_NL.ts | 4 - spline-widget/lang/ru_RU.ts | 4 - spline-widget/lang/stub.ts | 4 - spline-widget/spline-widget.cpp | 652 --------------------- spline-widget/spline-widget.hpp | 106 ---- spline-widget/spline.cpp | 469 --------------- spline-widget/spline.hpp | 108 ---- spline/CMakeLists.txt | 2 + spline/broken/qfunctionconfiguratorplugin.cpp | 111 ++++ spline/broken/qfunctionconfiguratorplugin.h | 36 ++ spline/export.hpp | 27 + spline/lang/nl_NL.ts | 4 + spline/lang/ru_RU.ts | 4 + spline/lang/stub.ts | 4 + spline/spline-widget.cpp | 652 +++++++++++++++++++++ spline/spline-widget.hpp | 106 ++++ spline/spline.cpp | 469 +++++++++++++++ spline/spline.hpp | 108 ++++ tracker-tobii-eyex/CMakeLists.txt | 6 +- tracker-tobii-eyex/tobii-eyex-dialog.ui | 2 +- tracker-tobii-eyex/tobii-settings.hpp | 4 +- 36 files changed, 1540 insertions(+), 1540 deletions(-) delete mode 100644 spline-widget/CMakeLists.txt delete mode 100644 spline-widget/broken/qfunctionconfiguratorplugin.cpp delete mode 100644 spline-widget/broken/qfunctionconfiguratorplugin.h delete mode 100644 spline-widget/export.hpp delete mode 100644 spline-widget/lang/nl_NL.ts delete mode 100644 spline-widget/lang/ru_RU.ts delete mode 100644 spline-widget/lang/stub.ts delete mode 100644 spline-widget/spline-widget.cpp delete mode 100644 spline-widget/spline-widget.hpp delete mode 100644 spline-widget/spline.cpp delete mode 100644 spline-widget/spline.hpp create mode 100644 spline/CMakeLists.txt create mode 100644 spline/broken/qfunctionconfiguratorplugin.cpp create mode 100644 spline/broken/qfunctionconfiguratorplugin.h create mode 100644 spline/export.hpp create mode 100644 spline/lang/nl_NL.ts create mode 100644 spline/lang/ru_RU.ts create mode 100644 spline/lang/stub.ts create mode 100644 spline/spline-widget.cpp create mode 100644 spline/spline-widget.hpp create mode 100644 spline/spline.cpp create mode 100644 spline/spline.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f519ebb..074e4011 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,7 @@ file(GLOB opentrack-subprojects "x-plane-plugin/${C}" "csv/${C}" "pose-widget/${C}" - "spline-widget/${C}" + "spline/${C}" "qxt-mini/${C}" "macosx/${C}" "cv/${C}" diff --git a/filter-accela/CMakeLists.txt b/filter-accela/CMakeLists.txt index 61286764..ef6424eb 100644 --- a/filter-accela/CMakeLists.txt +++ b/filter-accela/CMakeLists.txt @@ -1,2 +1,2 @@ otr_module(filter-accela) -target_link_libraries(opentrack-filter-accela opentrack-spline-widget) +target_link_libraries(opentrack-filter-accela opentrack-spline) diff --git a/filter-accela/accela-settings.hpp b/filter-accela/accela-settings.hpp index 008fc797..6b264f2e 100644 --- a/filter-accela/accela-settings.hpp +++ b/filter-accela/accela-settings.hpp @@ -2,7 +2,7 @@ #include "options/options.hpp" using namespace options; -#include "spline-widget/spline.hpp" +#include "spline/spline.hpp" struct settings_accela : opts { diff --git a/filter-accela/ftnoir_filter_accela_dialog.cpp b/filter-accela/ftnoir_filter_accela_dialog.cpp index 51789f61..637bee86 100644 --- a/filter-accela/ftnoir_filter_accela_dialog.cpp +++ b/filter-accela/ftnoir_filter_accela_dialog.cpp @@ -10,7 +10,7 @@ #include #include #include "api/plugin-api.hpp" -#include "spline-widget/spline-widget.hpp" +#include "spline/spline-widget.hpp" #include dialog_accela::dialog_accela() diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 25181fce..3cd17e24 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -20,7 +20,7 @@ set_target_properties(opentrack-user-interface PROPERTIES target_link_libraries(opentrack-user-interface opentrack-migration opentrack-logic - opentrack-spline-widget + opentrack-spline opentrack-pose-widget opentrack-version ) diff --git a/gui/mapping-window.cpp b/gui/mapping-window.cpp index 007e3217..5f61d31f 100644 --- a/gui/mapping-window.cpp +++ b/gui/mapping-window.cpp @@ -8,7 +8,7 @@ #include "mapping-window.hpp" #include "logic/main-settings.hpp" -#include "spline-widget/spline-widget.hpp" +#include "spline/spline-widget.hpp" MapWidget::MapWidget(Mappings& m) : m(m) { diff --git a/gui/mapping-window.ui b/gui/mapping-window.ui index 14628be3..4ef77cb8 100644 --- a/gui/mapping-window.ui +++ b/gui/mapping-window.ui @@ -588,7 +588,7 @@ spline_widget QWidget -
spline-widget/spline-widget.hpp
+
spline/spline-widget.hpp
diff --git a/logic/CMakeLists.txt b/logic/CMakeLists.txt index ddbb9509..f3f128af 100644 --- a/logic/CMakeLists.txt +++ b/logic/CMakeLists.txt @@ -1,5 +1,5 @@ otr_module(logic BIN) -target_link_libraries(opentrack-logic opentrack-spline-widget) +target_link_libraries(opentrack-logic opentrack-spline) if(NOT WIN32) target_link_libraries(opentrack-logic opentrack-qxt-mini) else() diff --git a/logic/mappings.hpp b/logic/mappings.hpp index fdbac460..6d028c10 100644 --- a/logic/mappings.hpp +++ b/logic/mappings.hpp @@ -10,7 +10,7 @@ #include "export.hpp" #include "options/options.hpp" using namespace options; -#include "spline-widget/spline.hpp" +#include "spline/spline.hpp" #include "main-settings.hpp" struct OPENTRACK_LOGIC_EXPORT Map final diff --git a/logic/tracker.h b/logic/tracker.h index 6ba5dc70..6b9b07ee 100644 --- a/logic/tracker.h +++ b/logic/tracker.h @@ -16,7 +16,7 @@ #include "compat/euler.hpp" #include "selected-libraries.hpp" -#include "spline-widget/spline.hpp" +#include "spline/spline.hpp" #include "main-settings.hpp" #include "options/options.hpp" #include "tracklogger.hpp" diff --git a/migration/CMakeLists.txt b/migration/CMakeLists.txt index ff4dfae2..9a7f7dc9 100644 --- a/migration/CMakeLists.txt +++ b/migration/CMakeLists.txt @@ -1,2 +1,2 @@ otr_module(migration BIN) -target_link_libraries(opentrack-migration opentrack-logic opentrack-spline-widget) +target_link_libraries(opentrack-migration opentrack-logic opentrack-spline) diff --git a/spline-widget/CMakeLists.txt b/spline-widget/CMakeLists.txt deleted file mode 100644 index 5cc25ab8..00000000 --- a/spline-widget/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -otr_module(spline-widget NO-COMPAT BIN) -target_link_libraries(opentrack-spline-widget opentrack-options opentrack-compat) diff --git a/spline-widget/broken/qfunctionconfiguratorplugin.cpp b/spline-widget/broken/qfunctionconfiguratorplugin.cpp deleted file mode 100644 index 1a9da10a..00000000 --- a/spline-widget/broken/qfunctionconfiguratorplugin.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (c) 2011-2012 Stanislaw Halik - * Adapted to FaceTrackNoIR by Wim Vriend. - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - */ -#include "qfunctionconfigurator.h" - -#include -#include "qfunctionconfiguratorplugin.h" - - -QFunctionConfiguratorPlugin::QFunctionConfiguratorPlugin(QObject *parent) - : QObject(parent) -{ - initialized = false; -} - -void QFunctionConfiguratorPlugin::initialize(QDesignerFormEditorInterface *) -{ - if (initialized) - return; - - initialized = true; -} - -bool QFunctionConfiguratorPlugin::isInitialized() const -{ - return initialized; -} - -QWidget *QFunctionConfiguratorPlugin::createWidget(QWidget *parent) -{ - return new QFunctionConfigurator(parent); -} - -QString QFunctionConfiguratorPlugin::name() const -{ - return "QFunctionConfigurator"; -} - -QString QFunctionConfiguratorPlugin::group() const -{ - return "My Plugins"; -} - -QIcon QFunctionConfiguratorPlugin::icon() const -{ - return QIcon(); -} - -QString QFunctionConfiguratorPlugin::toolTip() const -{ - return QString(); -} - -QString QFunctionConfiguratorPlugin::whatsThis() const -{ - return QString(); -} - -bool QFunctionConfiguratorPlugin::isContainer() const -{ - return false; -} - -QString QFunctionConfiguratorPlugin::domXml() const -{ - return "\n" - " \n" - " \n" - " 0\n" - " 0\n" - " 161\n" - " 220\n" - " \n" - " \n" - " \n" - " \n" - " 255\n" - " 170\n" - " 0\n" - " \n" - " \n" - " \n" - " Input Yaw (degr.)\n" - " \n" - " \n" - " Output Yaw (degr.)\n" - " \n" - " \n" - " 50\n" - " \n" - " \n" - " 180\n" - " \n" - " \n" - " 2\n" - " \n" - " \n" - " 1\n" - " \n" - "\n"; -} - -QString QFunctionConfiguratorPlugin::includeFile() const -{ - return "qfunctionconfigurator.h"; -} - -Q_EXPORT_PLUGIN2(qfunctionconfigurator, QFunctionConfiguratorPlugin) diff --git a/spline-widget/broken/qfunctionconfiguratorplugin.h b/spline-widget/broken/qfunctionconfiguratorplugin.h deleted file mode 100644 index ca68e0e2..00000000 --- a/spline-widget/broken/qfunctionconfiguratorplugin.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) 2011-2012 Stanislaw Halik - * Adapted to FaceTrackNoIR by Wim Vriend. - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - */ -#ifndef QFUNCTIONCONFIGURATORPLUGIN_H -#define QFUNCTIONCONFIGURATORPLUGIN_H - -#include - -class QFunctionConfiguratorPlugin : public QObject, public QDesignerCustomWidgetInterface -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface" FILE "analogclock.json") - Q_INTERFACES(QDesignerCustomWidgetInterface) - -public: - QFunctionConfiguratorPlugin(QObject *parent = 0); - - bool isContainer() const; - bool isInitialized() const; - QIcon icon() const; - QString domXml() const; - QString group() const; - QString includeFile() const; - QString name() const; - QString toolTip() const; - QString whatsThis() const; - QWidget *createWidget(QWidget *parent); - void initialize(QDesignerFormEditorInterface *core); -private: - bool initialized; -}; - -#endif // QFUNCTIONCONFIGURATORPLUGIN_H diff --git a/spline-widget/export.hpp b/spline-widget/export.hpp deleted file mode 100644 index 4875b4c1..00000000 --- a/spline-widget/export.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#ifdef BUILD_spline_widget -# ifdef _WIN32 -# define OPENTRACK_SPLINE_LINKAGE __declspec(dllexport) -# else -# define OPENTRACK_SPLINE_LINKAGE -# endif - -# ifndef _MSC_VER -# define OPENTRACK_SPLINE_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_SPLINE_LINKAGE -# else -# define OPENTRACK_SPLINE_EXPORT OPENTRACK_SPLINE_LINKAGE -# endif -#else - #ifdef _WIN32 - # define OPENTRACK_SPLINE_LINKAGE __declspec(dllimport) - #else - # define OPENTRACK_SPLINE_LINKAGE - #endif - - #ifndef _MSC_VER - # define OPENTRACK_SPLINE_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_SPLINE_LINKAGE - #else - # define OPENTRACK_SPLINE_EXPORT OPENTRACK_SPLINE_LINKAGE - #endif -#endif diff --git a/spline-widget/lang/nl_NL.ts b/spline-widget/lang/nl_NL.ts deleted file mode 100644 index 9e739505..00000000 --- a/spline-widget/lang/nl_NL.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/spline-widget/lang/ru_RU.ts b/spline-widget/lang/ru_RU.ts deleted file mode 100644 index f62cf2e1..00000000 --- a/spline-widget/lang/ru_RU.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/spline-widget/lang/stub.ts b/spline-widget/lang/stub.ts deleted file mode 100644 index 6401616d..00000000 --- a/spline-widget/lang/stub.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/spline-widget/spline-widget.cpp b/spline-widget/spline-widget.cpp deleted file mode 100644 index c7dd5e5a..00000000 --- a/spline-widget/spline-widget.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/* Copyright (c) 2012-2016 Stanislaw Halik - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - */ - -#include "compat/util.hpp" -#include "spline-widget.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -spline_widget::spline_widget(QWidget *parent) : - QWidget(parent), - _config(nullptr), - snap_x(0), - snap_y(0), - _x_step(10), - _y_step(10), - moving_control_point_idx(-1), - _draw_function(true), - _preview_only(false) -{ - setMouseTracking(true); - setFocusPolicy(Qt::ClickFocus); - setCursor(Qt::ArrowCursor); -} - -spline_widget::~spline_widget() -{ - if (connection) - QObject::disconnect(connection); -} - -void spline_widget::setConfig(spline* spl) -{ - if (spl != _config) - { - if (connection) - { - QObject::disconnect(connection); - connection = QMetaObject::Connection(); - } - - if (spl) - { - mem s = spl->get_settings(); - connection = connect(s.get(), &spline::settings::recomputed, - this, [this]() { reload_spline(); }, - Qt::QueuedConnection); - } - - _config = spl; - _background = QPixmap(); - update_range(); - } -} - -QColor spline_widget::colorBezier() const -{ - return spline_color; -} - -void spline_widget::setColorBezier(QColor color) -{ - spline_color = color; - update(); -} - -void spline_widget::force_redraw() -{ - _background = QPixmap(); - update(); -} - -void spline_widget::set_preview_only(bool val) -{ - _preview_only = val; -} - -bool spline_widget::is_preview_only() const -{ - return _preview_only; -} - -void spline_widget::drawBackground() -{ - _background = QPixmap(width(), height()); - - QPainter painter(&_background); - - painter.fillRect(rect(), palette().color(QPalette::Light)); - - QColor bg_color(112, 154, 209); - if (!isEnabled() && !_preview_only) - bg_color = QColor(176,176,180); - painter.fillRect(pixel_bounds, bg_color); - - QFont font; - font.setPointSize(8); - painter.setFont(font); - QFontMetrics metrics(font); - - QColor color__(176, 190, 209, 127); - - if (!isEnabled()) - color__ = QColor(70, 90, 100, 96); - - const QPen pen(color__, 1, Qt::SolidLine, Qt::FlatCap); - - const int ystep = _y_step, xstep = _x_step; - const qreal maxx = _config->max_input(); - const qreal maxy = _config->max_output(); - - // horizontal grid - for (int i = 0; i <= maxy; i += ystep) - { - const int y = int(pixel_bounds.height() - i * c.y() + pixel_bounds.y()); - drawLine(painter, - QPoint(pixel_bounds.x(), y), - QPoint(pixel_bounds.x() + pixel_bounds.width(), y), - pen); - painter.drawText(QRectF(10, - y - metrics.height()/2., - pixel_bounds.left(), - metrics.height()), - QString::number(i)); - } - - // vertical grid - for (int i = 0; i <= maxx; i += xstep) - { - const int x = iround(pixel_bounds.x() + i * c.x()); - drawLine(painter, - QPoint(x, pixel_bounds.y()), - QPoint(x, pixel_bounds.y() + pixel_bounds.height()), - pen); - const QString text = QString::number(i); - painter.drawText(QRectF(x - metrics.width(text)/2., - pixel_bounds.height() + 10 + metrics.height(), - metrics.width(text), - metrics.height()), - text); - } -} - -void spline_widget::drawFunction() -{ - _function = _background; - QPainter painter(&_function); - painter.setRenderHint(QPainter::Antialiasing, true); - - const points_t points = _config->get_points(); - - if (moving_control_point_idx >= 0 && - moving_control_point_idx < points.size()) - { - const QPen pen(Qt::white, 1, Qt::SolidLine, Qt::FlatCap); - const QPointF prev_ = point_to_pixel(QPointF(0, 0)); - QPoint prev(iround(prev_.x()), iround(prev_.y())); - for (int i = 0; i < points.size(); i++) - { - const QPointF tmp_ = point_to_pixel(points[i]); - const QPoint tmp(iround(tmp_.x()), iround(tmp_.y())); - drawLine(painter, prev, tmp, pen); - prev = tmp; - } - } - - const QColor color = progn - ( - if (!isEnabled() && !_preview_only) - { - QColor color(spline_color); - const int avg = int(float(color.red() + color.green() + color.blue())/3); - return QColor(int(float(color.red() + avg) * .5f), - int(float(color.green() + avg) * .5f), - int(float(color.blue() + avg) * .5f), - 96); - } - else - { - QColor color(spline_color); - color.setAlphaF(color.alphaF() * .9); - return color; - } - ); - - painter.setPen(QPen(color, 1.75, Qt::SolidLine, Qt::FlatCap)); - -//#define DEBUG_SPLINE -#ifndef DEBUG_SPLINE - static constexpr double step_ = 3./3; - - const double maxx = _config->max_input(); - const double step = step_ / c.x(); - - QPainterPath path; - - path.moveTo(point_to_pixel(QPointF(0, 0))); - - for (double k = 0; k < maxx; k += step*3) - { - const float next_1(_config->get_value_no_save(k + step*1)); - const float next_2(_config->get_value_no_save(k + step*2)); - const float next_3(_config->get_value_no_save(k + step*3)); - - const QPointF b(point_to_pixel_(QPointF(k + step*1, qreal(next_1)))), - c(point_to_pixel_(QPointF(k + step*2, qreal(next_2)))), - d(point_to_pixel_(QPointF(k + step*3, qreal(next_3)))); - - path.cubicTo(b, c, d); - } - - painter.drawPath(path); -#else - static constexpr int line_length_pixels = 3; - const qreal max = _config->maxInput(); - const qreal step = clamp(line_length_pixels / c.x(), 5e-2, max); - QPointF prev = point_to_pixel(QPoint(0, 0)); - for (qreal i = 0; i < max; i += step) - { - const qreal val = qreal(_config->get_value_no_save(i)); - const QPointF cur = point_to_pixel(QPointF(i, val)); - painter.drawLine(prev, cur); - prev = cur; - } - { - const qreal maxx = _config->maxInput(); - const qreal maxy = qreal(_config->get_value_no_save(maxx)); - painter.drawLine(QPointF(prev), point_to_pixel_(QPointF(maxx, maxy))); - } -#endif - - const int alpha = !isEnabled() ? 64 : 120; - if (!_preview_only) - { - for (int i = 0; i < points.size(); i++) - { - drawPoint(painter, - point_to_pixel(points[i]), - QColor(200, 200, 210, alpha), - isEnabled() ? QColor(50, 100, 120, 200) : QColor(200, 200, 200, 96)); - } - } -} - -void spline_widget::paintEvent(QPaintEvent *e) -{ - if (!_config) - return; - - QPainter p(this); - - if (_background.isNull()) - { - _draw_function = true; - drawBackground(); - } - - if (_draw_function) - { - _draw_function = false; - drawFunction(); - } - - p.drawPixmap(e->rect(), _function); - - // If the Tracker is active, the 'Last Point' it requested is recorded. - // Show that point on the graph, with some lines to assist. - // This new feature is very handy for tweaking the curves! - QPointF last; - if (_config->get_last_value(last) && isEnabled()) - drawPoint(p, point_to_pixel(last), QColor(255, 0, 0, 120)); -} - -void spline_widget::drawPoint(QPainter& painter, const QPointF& pos, const QColor& colBG, const QColor& border) -{ - painter.save(); - painter.setPen(QPen(border, 1, Qt::SolidLine, Qt::PenCapStyle::FlatCap)); - painter.setBrush(colBG); - painter.drawEllipse(QRectF(pos.x() - point_size, - pos.y() - point_size, - point_size*2, point_size*2)); - painter.restore(); -} - -void spline_widget::drawLine(QPainter& painter, const QPoint& start, const QPoint& end, const QPen& pen) -{ - painter.save(); - painter.setPen(pen); - painter.setBrush(Qt::NoBrush); - painter.drawLine(start, end); - painter.restore(); -} - -void spline_widget::mousePressEvent(QMouseEvent *e) -{ - if (!_config || !isEnabled() || !is_in_bounds(e->pos())) - { - clearFocus(); - return; - } - - const int point_pixel_closeness_limit = get_closeness_limit(); - - points_t points = _config->get_points(); - if (e->button() == Qt::LeftButton) - { - bool bTouchingPoint = false; - moving_control_point_idx = -1; - for (int i = 0; i < points.size(); i++) - { - if (point_within_pixel(points[i], e->pos())) - { - bTouchingPoint = true; - moving_control_point_idx = int(i); - break; - } - } - if (!bTouchingPoint) - { - bool too_close = false; - const QPoint pos = e->pos(); - - for (int i = 0; i < points.size(); i++) - { - const QPointF pt = point_to_pixel(points[i]); - const qreal x = std::fabs(pt.x() - pos.x()); - if (point_pixel_closeness_limit >= x) - { - too_close = true; - break; - } - } - - if (!too_close) - { - _config->add_point(pixel_coord_to_point(e->pos())); - show_tooltip(e->pos()); - } - } - } - - if (e->button() == Qt::RightButton) - { - if (_config) - { - int found_pt = -1; - for (int i = 0; i < points.size(); i++) - { - if (point_within_pixel(points[i], e->pos())) - { - found_pt = i; - break; - } - } - - if (found_pt != -1) - { - _config->remove_point(found_pt); - } - moving_control_point_idx = -1; - } - } - _draw_function = true; - update(); -} - -void spline_widget::mouseMoveEvent(QMouseEvent *e) -{ - if (_preview_only && _config) - { - show_tooltip(e->pos()); - clearFocus(); - return; - } - - if (!_config || !isEnabled() || !isActiveWindow() || (moving_control_point_idx != -1 && !hasFocus())) - { - clearFocus(); - return; - } - - const int i = moving_control_point_idx; - const points_t points = _config->get_points(); - const int sz = points.size(); - - if (i >= 0 && i < sz) - { - const int point_pixel_closeness_limit = get_closeness_limit(); - bool overlap = false; - - const QPoint pix = progn( - // takes snap into account - const QPointF pix_ = point_to_pixel(pixel_coord_to_point(e->pos())); - return QPoint(int(pix_.x()), int(pix_.y())); - ); - - QPointF new_pt = pixel_coord_to_point(e->pos()); - - if (i + 1 < points.size()) - { - const QPointF other_pix = point_to_pixel(points[i+1]); - overlap |= other_pix.x() - pix.x() < point_pixel_closeness_limit; - } - if (i != 0) - { - const QPointF other_pix = point_to_pixel(points[i-1]); - overlap |= pix.x() - other_pix.x() < point_pixel_closeness_limit; - } - - if (overlap) - new_pt.setX(points[i].x()); - - _config->move_point(i, new_pt); - _draw_function = true; - - setCursor(Qt::ClosedHandCursor); - show_tooltip(pix, new_pt); - update(); - } - else if (sz) - { - int i; - bool is_on_point = is_on_pt(e->pos(), &i); - - if (is_on_point) - { - setCursor(Qt::CrossCursor); - show_tooltip(e->pos(), points[i]); - } - else - { - setCursor(Qt::ArrowCursor); - if (is_in_bounds(e->pos())) - show_tooltip(e->pos()); - else - QToolTip::hideText(); - } - } -} - -void spline_widget::mouseReleaseEvent(QMouseEvent *e) -{ - if (!_config || !isEnabled() || !isActiveWindow() || !hasFocus()) - { - clearFocus(); - return; - } - - if (e->button() == Qt::LeftButton) - { - { - if (is_on_pt(e->pos(), nullptr)) - setCursor(Qt::CrossCursor); - else - setCursor(Qt::ArrowCursor); - } - moving_control_point_idx = -1; - _draw_function = true; - - if (is_in_bounds(e->pos())) - show_tooltip(e->pos()); - else - QToolTip::hideText(); - - update(); - } -} - -void spline_widget::reload_spline() -{ - // don't recompute here as the value's about to be recomputed in the callee - update_range(); -} - -int spline_widget::get_closeness_limit() -{ - return std::max(iround(snap_x * c.x()), 1); -} - -void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_) -{ - const QPointF value = QPoint(0, 0) == value_ ? pixel_coord_to_point(pos) : value_; - - double x = value.x(), y = value.y(); - - if (_preview_only) - y = _config->get_value_no_save(x); - - const int x_ = iround(x), y_ = iround(y); - - using std::fabs; - - if (fabs(x_ - x) < 1e-3) - x = x_; - if (fabs(y_ - y) < 1e-3) - y = y_; - - const bool is_fusion = QStringLiteral("fusion") == QApplication::style()->objectName(); - const int add_x = (is_fusion ? 25 : 0), add_y = (is_fusion ? 15 : 0); - - const QPoint pix(int(pos.x()) + add_x, int(pos.y()) + add_y); - - QToolTip::showText(mapToGlobal(pix), - QStringLiteral("value: %1x%2").arg(x).arg(y), - this, - rect(), - 0); -} - -bool spline_widget::is_in_bounds(const QPoint& pos) const -{ - static constexpr int grace = point_size * 3; - static constexpr int bottom_grace = int(point_size * 1.5); - return (pos.x() + grace > pixel_bounds.left() && - pos.x() - grace < pixel_bounds.right() && - pos.y() + grace > pixel_bounds.top() && - pos.y() - bottom_grace < pixel_bounds.bottom()); -} - -void spline_widget::update_range() -{ - if (!_config) - return; - - const int w = width(), h = height(); - const int mwl = 40, mhl = 20; - const int mwr = 15, mhr = 35; - - pixel_bounds = QRect(mwl, mhl, (w - mwl - mwr), (h - mhl - mhr)); - c = QPointF(pixel_bounds.width() / _config->max_input(), pixel_bounds.height() / _config->max_output()); - _draw_function = true; - - _background = QPixmap(); - _function = QPixmap(); - - update(); -} - -bool spline_widget::point_within_pixel(const QPointF& pt, const QPoint &pixel) -{ - const QPointF tmp = pixel - point_to_pixel(pt); - return QPointF::dotProduct(tmp, tmp) < point_size * point_size; -} - -void spline_widget::focusOutEvent(QFocusEvent* e) -{ - if (moving_control_point_idx != -1) - QToolTip::hideText(); - moving_control_point_idx = -1; - _draw_function = true; - lower(); - setCursor(Qt::ArrowCursor); - e->accept(); -} - -QPointF spline_widget::pixel_coord_to_point(const QPoint& point) -{ - qreal x = (point.x() - pixel_bounds.x()) / c.x(); - qreal y = (pixel_bounds.height() - point.y() + pixel_bounds.y()) / c.y(); - - static constexpr int c = 1000; - - if (snap_x > 0) - { - x += snap_x * .5; - x -= std::fmod(x, snap_x); - // truncate after few decimal places to reduce rounding errors. - // round upward to nearest. - x = int(x * c + .5/c) / double(c); - } - if (snap_y > 0) - { - y += snap_y * .5; - y -= std::fmod(y, snap_y); - // idem - y = int(y * c + .5/c) / double(c); - } - - if (x < 0) - x = 0; - if (x > _config->max_input()) - x = _config->max_input(); - - if (y < 0) - y = 0; - if (y > _config->max_output()) - y = _config->max_output(); - - return QPointF(x, y); -} - -QPointF spline_widget::point_to_pixel_(const QPointF& point) -{ - return QPointF(pixel_bounds.x() + point.x() * c.x(), - pixel_bounds.y() + pixel_bounds.height() - point.y() * c.y()); -} - -QPoint spline_widget::point_to_pixel(const QPointF& point) -{ - QPointF pt(point_to_pixel_(point)); - - return QPoint(iround(pt.x()), iround(pt.y())); -} - -void spline_widget::resizeEvent(QResizeEvent *) -{ - update_range(); -} - -bool spline_widget::is_on_pt(const QPoint& pos, int* pt) -{ - if (!_config) - { - if (pt) - *pt = -1; - return false; - } - - const points_t points = _config->get_points(); - - for (int i = 0; i < points.size(); i++) - { - if (point_within_pixel(points[i], pos)) - { - if (pt) - *pt = i; - return true; - } - } - - if (pt) - *pt = -1; - return false; -} diff --git a/spline-widget/spline-widget.hpp b/spline-widget/spline-widget.hpp deleted file mode 100644 index 4d5a7680..00000000 --- a/spline-widget/spline-widget.hpp +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright (c) 2012-2016 Stanislaw Halik - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - */ - -// Adapted to FaceTrackNoIR by Wim Vriend. - -#pragma once - -#include "spline.hpp" -#include "api/plugin-api.hpp" -#include "options/options.hpp" -using namespace options; - -#include "export.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -class OPENTRACK_SPLINE_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) - Q_PROPERTY(int x_step READ x_step WRITE set_x_step) - Q_PROPERTY(int y_step READ y_step WRITE set_y_step) - - using points_t = spline::points_t; -public: - spline_widget(QWidget *parent = 0); - ~spline_widget(); - - spline* config(); - void setConfig(spline* spl); - - QColor colorBezier() const; - void setColorBezier(QColor color); - - void force_redraw(); - void set_preview_only(bool val); - bool is_preview_only() const; - - double x_step() { return _x_step; } - double y_step() { return _y_step; } - void set_x_step(double val) { _x_step = val; } - void set_y_step(double val) { _y_step = val; } - - void set_snap(double x, double y) { snap_x = x; snap_y = y; } - void get_snap(double& x, double& y) const { x = snap_x; y = snap_y; } -public slots: - void reload_spline(); -protected slots: - void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; -private: - int get_closeness_limit(); - void show_tooltip(const QPoint& pos, const QPointF& value = QPointF(0, 0)); - bool is_in_bounds(const QPoint& pos) const; - - void drawBackground(); - void drawFunction(); - void drawPoint(QPainter& painter, const QPointF& pt, const QColor& colBG, const QColor& border = QColor(50, 100, 120, 200)); - void drawLine(QPainter& painter, const QPoint& start, const QPoint& end, const QPen& pen); - bool point_within_pixel(const QPointF& pt, const QPoint& pixel); - - void focusOutEvent(QFocusEvent*e) override; - void resizeEvent(QResizeEvent *) override; - - bool is_on_pt(const QPoint& pos, int* pt = nullptr); - void update_range(); - QPointF pixel_coord_to_point(const QPoint& point); - - QPointF point_to_pixel_(const QPointF& point); - QPoint point_to_pixel(const QPointF& point); - - QPointF c; - spline* _config; - - QPixmap _background; - QPixmap _function; - QColor spline_color; - - // bounds of the rectangle user can interact with - QRect pixel_bounds; - - QMetaObject::Connection connection; - - double snap_x, snap_y; - double _x_step, _y_step; - int moving_control_point_idx; - bool _draw_function, _preview_only; - - static constexpr int point_size = 4; -}; diff --git a/spline-widget/spline.cpp b/spline-widget/spline.cpp deleted file mode 100644 index 2db23c14..00000000 --- a/spline-widget/spline.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/* Copyright (c) 2012-2016, Stanislaw Halik - - * Permission to use, copy, modify, and/or distribute this - * software for any purpose with or without fee is hereby granted, - * provided that the above copyright notice and this permission - * notice appear in all copies. - */ - -#include "spline.hpp" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -constexpr int spline::value_count; - -spline::spline(qreal maxx, qreal maxy, const QString& name) : - s(nullptr), - data(value_count, -1.f), - _mutex(QMutex::Recursive), - max_x(maxx), - max_y(maxy), - activep(false), - validp(false) -{ - set_bundle(options::make_bundle(name)); -} - -spline::~spline() -{ - QMutexLocker l(&_mutex); - - if (connection) - { - QObject::disconnect(connection); - connection = QMetaObject::Connection(); - } -} - -spline::spline() : spline(0, 0, "") {} - -void spline::set_tracking_active(bool value) -{ - activep = value; -} - -bundle spline::get_bundle() -{ - return s->b; -} - -void spline::clear() -{ - QMutexLocker l(&_mutex); - s->points = points_t(); - validp = false; -} - -void spline::set_max_input(qreal max_input) -{ - QMutexLocker l(&_mutex); - max_x = max_input; - recompute(); -} - -void spline::set_max_output(qreal max_output) -{ - QMutexLocker l(&_mutex); - max_y = max_output; - recompute(); -} - -qreal spline::max_input() const -{ - QMutexLocker l(&_mutex); - return max_x; -} - -qreal spline::max_output() const -{ - QMutexLocker l(&_mutex); - return max_y; -} - -float spline::get_value(double x) -{ - QMutexLocker foo(&_mutex); - - const float ret = get_value_no_save(x); - last_input_value.setX(std::fabs(x)); - last_input_value.setY(double(std::fabs(ret))); - return ret; -} - -float spline::get_value_no_save(double x) const -{ - return const_cast(*this).get_value_no_save_internal(x); -} - -float spline::get_value_no_save_internal(double x) -{ - QMutexLocker foo(&_mutex); - - if (max_x > 0) - x = std::min(max_x, x); - - float q = float(x * precision(s->points)); - int xi = (int)q; - float yi = get_value_internal(xi); - float yiplus1 = get_value_internal(xi+1); - float f = (q-xi); - float ret = yiplus1 * f + yi * (1.0f - f); // at least do a linear interpolation. - return ret; -} - -DEFUN_WARN_UNUSED bool spline::get_last_value(QPointF& point) -{ - QMutexLocker foo(&_mutex); - point = last_input_value; - return activep; -} - -float spline::get_value_internal(int x) -{ - if (!validp) - { - update_interp_data(); - validp = true; - } - - float sign = x < 0 ? -1 : 1; - x = std::abs(x); - float ret; - ret = data[std::min(unsigned(x), unsigned(value_count)-1u)]; - return ret * sign; -} - -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& points, double max_x, int i) -{ - const int sz = element_count(points, max_x); - - if (i < 0 || sz == 0) - return QPointF(0, 0); - - if (i < sz) - return points[i]; - - return points[sz - 1]; -} - -int spline::element_count(const QList& points, double max_x) -{ - if (!(max_x > 0)) - return points.size(); - else - { - const unsigned sz = points.size(); - for (unsigned i = 0; i < sz; i++) - { - if (!(points[i].x() <= max_x)) - return i; - } - return points.size(); - } -} - -bool spline::sort_fn(const QPointF& one, const QPointF& two) -{ - return one.x() <= two.x(); -} - -void spline::update_interp_data() -{ - points_t points = s->points; - - int sz = element_count(points, max_x); - - if (sz == 0) - points.prepend(QPointF(max_x, max_y)); - - std::stable_sort(points.begin(), points.begin() + sz, sort_fn); - - const double mult = precision(points); - const double mult_ = mult * 30; - - for (unsigned i = 0; i < value_count; i++) - data[i] = -1; - - if (sz < 2) - { - if (points[0].x() - 1e-2 <= max_x) - { - const double x = points[0].x(); - const double y = points[0].y(); - const int max = clamp(int(x * precision(points)), 1, value_count-1); - for (int k = 0; k <= max; k++) - { - if (k < value_count) - data[unsigned(k)] = float(y * k / max); - } - } - } - else - { - if (points[0].x() > 1e-2 && points[0].x() <= max_x) - points.push_front(QPointF(0, 0)); - - for (int i = 0; i < sz; i++) - { - const QPointF p0 = ensure_in_bounds(points, max_x, i - 1); - const QPointF p1 = ensure_in_bounds(points, max_x, i + 0); - const QPointF p2 = ensure_in_bounds(points, max_x, i + 1); - const QPointF p3 = ensure_in_bounds(points, max_x, i + 2); - 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(); - - const double cx[4] = { - 2 * p1_x, // 1 - -p0_x + p2_x, // t - 2 * p0_x - 5 * p1_x + 4 * p2_x - p3_x, // t^2 - -p0_x + 3 * p1_x - 3 * p2_x + p3_x, // t3 - }; - - const double cy[4] = - { - 2 * p1_y, // 1 - -p0_y + p2_y, // t - 2 * p0_y - 5 * p1_y + 4 * p2_y - p3_y, // t^2 - -p0_y + 3 * p1_y - 3 * p2_y + p3_y, // t3 - }; - - // multiplier helps fill in all the x's needed - 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 double t = (j - start) / (double) (end - start); - const double t2 = t*t; - const double t3 = t*t*t; - - const int x = iround(.5 * mult * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3)); - const float y = float(.5 * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3)); - - if (x >= 0 && x < value_count) - data[unsigned(x)] = y; - } - } - } - - float last = 0; - for (unsigned i = 0; i < unsigned(value_count); i++) - { - if (data[i] < 0) - data[i] = last; - last = data[i]; - } -} - -void spline::remove_point(int i) -{ - QMutexLocker foo(&_mutex); - - points_t points = s->points; - const int sz = element_count(points, max_x); - - if (i >= 0 && i < sz) - { - points.erase(points.begin() + i); - s->points = points; - validp = false; - } -} - -void spline::add_point(QPointF pt) -{ - QMutexLocker foo(&_mutex); - - points_t points = s->points; - points.push_back(pt); - std::stable_sort(points.begin(), points.end(), sort_fn); - s->points = points; - validp = false; -} - -void spline::add_point(double x, double y) -{ - add_point(QPointF(x, y)); -} - -void spline::move_point(int idx, QPointF pt) -{ - QMutexLocker foo(&_mutex); - - points_t points = s->points; - - const int sz = element_count(points, max_x); - - if (idx >= 0 && idx < sz) - { - points[idx] = pt; - // we don't allow points to be reordered, but sort due to possible caller logic error - std::stable_sort(points.begin(), points.end(), sort_fn); - s->points = points; - validp = false; - } -} - -QList spline::get_points() const -{ - QMutexLocker foo(&_mutex); - return s->points; -} - -int spline::get_point_count() const -{ - QMutexLocker foo(&_mutex); - return element_count(s->points, max_x);; -} - -void spline::reload() -{ - QMutexLocker foo(&_mutex); - s->b->reload(); -} - -void spline::save(QSettings& settings) -{ - QMutexLocker foo(&_mutex); - s->b->save_deferred(settings); -} - -void spline::save() -{ - save(*group::ini_file()); -} - -void spline::set_bundle(bundle b) -{ - QMutexLocker l(&_mutex); - - // gets called from ctor hence the need for nullptr checks - // the sentinel settings/bundle objects don't need any further branching once created - if (!s || s->b != b) - { - s = std::make_shared(b); - - if (connection) - QObject::disconnect(connection); - - if (b) - { - connection = QObject::connect(b.get(), &bundle_::changed, - s.get(), [&]() { - // we're holding the mutex to allow signal disconnection in spline dtor - // before this slot gets called for the next time - - // spline isn't a QObject and the connection context is incorrect - - QMutexLocker l(&_mutex); - recompute(); - emit s->recomputed(); - }, - Qt::QueuedConnection); - } - - recompute(); - - emit s->recomputed(); - } -} - -void spline::recompute() -{ - QMutexLocker foo(&_mutex); - - QList list = s->points; - - // storing to s->points fires bundle::changed and that leads to an infinite loop - // only store if we can't help it - std::stable_sort(list.begin(), list.end(), sort_fn); - - if (list != s->points) - s->points = list; - - const int sz = list.size(); - - QList ret_list; - ret_list.reserve(sz); - - for (int i = 0; i < sz; i++) - { - QPointF& pt(list[i]); - - const bool overlap = progn( - for (int j = 0; j < i; j++) - { - QPointF& pt2(list[j]); - const double dist_sq = (pt.x() - pt2.x())*(pt.x() - pt2.x()); - static constexpr double overlap = .6; - if (dist_sq < overlap * overlap) - return true; - } - return false; - ); - if (!overlap) - ret_list.push_back(pt); - } - - if (ret_list != s->points) - s->points = ret_list; - - last_input_value = QPointF(0, 0); - activep = false; - validp = false; -} - -// the return value is only safe to use with no spline::set_bundle calls -mem spline::get_settings() -{ - QMutexLocker foo(&_mutex); - return s; -} - -mem spline::get_settings() const -{ - QMutexLocker foo(&_mutex); - return s; -} - -double spline::precision(const QList& points) const -{ - // this adjusts the memoized range to the largest X value. empty space doesn't take value_count discrete points. - const int sz = element_count(points, max_x); - if (sz) - return clamp(value_count / clamp(points[sz - 1].x(), 1., max_x), 0., double(value_count)); - - return value_count / clamp(max_x, 1., double(value_count)); -} - -namespace spline_detail { - -settings::settings(bundle b): - b(b ? b : make_bundle("")), - points(b, "points", QList()) -{} - -settings::~settings() -{ -} - -} diff --git a/spline-widget/spline.hpp b/spline-widget/spline.hpp deleted file mode 100644 index 77b2bdf9..00000000 --- a/spline-widget/spline.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (c) 2012-2016, Stanislaw Halik - - * Permission to use, copy, modify, and/or distribute this - * software for any purpose with or without fee is hereby granted, - * provided that the above copyright notice and this permission - * notice appear in all copies. - */ - -#pragma once - -#include "compat/qcopyable-mutex.hpp" -#include "options/options.hpp" -#include "compat/util.hpp" -using namespace options; - -#include "export.hpp" - -#include -#include -#include - -#include -#include -#include -#include - -namespace spline_detail { - -class OPENTRACK_SPLINE_EXPORT settings final : public QObject -{ - Q_OBJECT -public: - bundle b; - value> points; - settings(bundle b); - ~settings() override; -signals: - void recomputed() const; -}; - -} - -class OPENTRACK_SPLINE_EXPORT spline final -{ - double precision(const QList& points) const; - void update_interp_data(); - float get_value_internal(int x); - void add_lone_point(); - float get_value_no_save_internal(double x); - static bool sort_fn(const QPointF& one, const QPointF& two); - - static QPointF ensure_in_bounds(const QList& points, double max_x, int i); - static int element_count(const QList& points, double max_x); - - mem s; - QMetaObject::Connection connection; - - std::vector data; - using interp_data_t = decltype(data); - - static constexpr int value_count = 4096; - - MyMutex _mutex; - QPointF last_input_value; - qreal max_x, max_y; - volatile bool activep; - bool validp; - -public: - using settings = spline_detail::settings; - - void reload(); - void save(QSettings& s); - void save(); - void set_bundle(bundle b); - - qreal max_input() const; - qreal max_output() const; - spline(); - spline(qreal maxx, qreal maxy, const QString& name); - ~spline(); - - spline& operator=(const spline&) = default; - spline(const spline&) = default; - - float get_value(double x); - float get_value_no_save(double x) const; - DEFUN_WARN_UNUSED bool get_last_value(QPointF& point); - void remove_point(int i); - void clear(); - - void add_point(QPointF pt); - void add_point(double x, double y); - void move_point(int idx, QPointF pt); - QList get_points() const; - void set_max_input(qreal MaxInput); - void set_max_output(qreal MaxOutput); - - void set_tracking_active(bool value); - bundle get_bundle(); - void recompute(); - - mem get_settings(); - mem get_settings() const; - - using points_t = decltype(s->points()); - int get_point_count() const; -}; diff --git a/spline/CMakeLists.txt b/spline/CMakeLists.txt new file mode 100644 index 00000000..d252004b --- /dev/null +++ b/spline/CMakeLists.txt @@ -0,0 +1,2 @@ +otr_module(spline NO-COMPAT BIN) +target_link_libraries(opentrack-spline opentrack-options opentrack-compat) diff --git a/spline/broken/qfunctionconfiguratorplugin.cpp b/spline/broken/qfunctionconfiguratorplugin.cpp new file mode 100644 index 00000000..1a9da10a --- /dev/null +++ b/spline/broken/qfunctionconfiguratorplugin.cpp @@ -0,0 +1,111 @@ +/* Copyright (c) 2011-2012 Stanislaw Halik + * Adapted to FaceTrackNoIR by Wim Vriend. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#include "qfunctionconfigurator.h" + +#include +#include "qfunctionconfiguratorplugin.h" + + +QFunctionConfiguratorPlugin::QFunctionConfiguratorPlugin(QObject *parent) + : QObject(parent) +{ + initialized = false; +} + +void QFunctionConfiguratorPlugin::initialize(QDesignerFormEditorInterface *) +{ + if (initialized) + return; + + initialized = true; +} + +bool QFunctionConfiguratorPlugin::isInitialized() const +{ + return initialized; +} + +QWidget *QFunctionConfiguratorPlugin::createWidget(QWidget *parent) +{ + return new QFunctionConfigurator(parent); +} + +QString QFunctionConfiguratorPlugin::name() const +{ + return "QFunctionConfigurator"; +} + +QString QFunctionConfiguratorPlugin::group() const +{ + return "My Plugins"; +} + +QIcon QFunctionConfiguratorPlugin::icon() const +{ + return QIcon(); +} + +QString QFunctionConfiguratorPlugin::toolTip() const +{ + return QString(); +} + +QString QFunctionConfiguratorPlugin::whatsThis() const +{ + return QString(); +} + +bool QFunctionConfiguratorPlugin::isContainer() const +{ + return false; +} + +QString QFunctionConfiguratorPlugin::domXml() const +{ + return "\n" + " \n" + " \n" + " 0\n" + " 0\n" + " 161\n" + " 220\n" + " \n" + " \n" + " \n" + " \n" + " 255\n" + " 170\n" + " 0\n" + " \n" + " \n" + " \n" + " Input Yaw (degr.)\n" + " \n" + " \n" + " Output Yaw (degr.)\n" + " \n" + " \n" + " 50\n" + " \n" + " \n" + " 180\n" + " \n" + " \n" + " 2\n" + " \n" + " \n" + " 1\n" + " \n" + "\n"; +} + +QString QFunctionConfiguratorPlugin::includeFile() const +{ + return "qfunctionconfigurator.h"; +} + +Q_EXPORT_PLUGIN2(qfunctionconfigurator, QFunctionConfiguratorPlugin) diff --git a/spline/broken/qfunctionconfiguratorplugin.h b/spline/broken/qfunctionconfiguratorplugin.h new file mode 100644 index 00000000..ca68e0e2 --- /dev/null +++ b/spline/broken/qfunctionconfiguratorplugin.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2011-2012 Stanislaw Halik + * Adapted to FaceTrackNoIR by Wim Vriend. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#ifndef QFUNCTIONCONFIGURATORPLUGIN_H +#define QFUNCTIONCONFIGURATORPLUGIN_H + +#include + +class QFunctionConfiguratorPlugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface" FILE "analogclock.json") + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QFunctionConfiguratorPlugin(QObject *parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget *createWidget(QWidget *parent); + void initialize(QDesignerFormEditorInterface *core); +private: + bool initialized; +}; + +#endif // QFUNCTIONCONFIGURATORPLUGIN_H diff --git a/spline/export.hpp b/spline/export.hpp new file mode 100644 index 00000000..4875b4c1 --- /dev/null +++ b/spline/export.hpp @@ -0,0 +1,27 @@ +#pragma once + +#ifdef BUILD_spline_widget +# ifdef _WIN32 +# define OPENTRACK_SPLINE_LINKAGE __declspec(dllexport) +# else +# define OPENTRACK_SPLINE_LINKAGE +# endif + +# ifndef _MSC_VER +# define OPENTRACK_SPLINE_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_SPLINE_LINKAGE +# else +# define OPENTRACK_SPLINE_EXPORT OPENTRACK_SPLINE_LINKAGE +# endif +#else + #ifdef _WIN32 + # define OPENTRACK_SPLINE_LINKAGE __declspec(dllimport) + #else + # define OPENTRACK_SPLINE_LINKAGE + #endif + + #ifndef _MSC_VER + # define OPENTRACK_SPLINE_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_SPLINE_LINKAGE + #else + # define OPENTRACK_SPLINE_EXPORT OPENTRACK_SPLINE_LINKAGE + #endif +#endif diff --git a/spline/lang/nl_NL.ts b/spline/lang/nl_NL.ts new file mode 100644 index 00000000..9e739505 --- /dev/null +++ b/spline/lang/nl_NL.ts @@ -0,0 +1,4 @@ + + + + diff --git a/spline/lang/ru_RU.ts b/spline/lang/ru_RU.ts new file mode 100644 index 00000000..f62cf2e1 --- /dev/null +++ b/spline/lang/ru_RU.ts @@ -0,0 +1,4 @@ + + + + diff --git a/spline/lang/stub.ts b/spline/lang/stub.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/spline/lang/stub.ts @@ -0,0 +1,4 @@ + + + + diff --git a/spline/spline-widget.cpp b/spline/spline-widget.cpp new file mode 100644 index 00000000..c7dd5e5a --- /dev/null +++ b/spline/spline-widget.cpp @@ -0,0 +1,652 @@ +/* Copyright (c) 2012-2016 Stanislaw Halik + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#include "compat/util.hpp" +#include "spline-widget.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +spline_widget::spline_widget(QWidget *parent) : + QWidget(parent), + _config(nullptr), + snap_x(0), + snap_y(0), + _x_step(10), + _y_step(10), + moving_control_point_idx(-1), + _draw_function(true), + _preview_only(false) +{ + setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); + setCursor(Qt::ArrowCursor); +} + +spline_widget::~spline_widget() +{ + if (connection) + QObject::disconnect(connection); +} + +void spline_widget::setConfig(spline* spl) +{ + if (spl != _config) + { + if (connection) + { + QObject::disconnect(connection); + connection = QMetaObject::Connection(); + } + + if (spl) + { + mem s = spl->get_settings(); + connection = connect(s.get(), &spline::settings::recomputed, + this, [this]() { reload_spline(); }, + Qt::QueuedConnection); + } + + _config = spl; + _background = QPixmap(); + update_range(); + } +} + +QColor spline_widget::colorBezier() const +{ + return spline_color; +} + +void spline_widget::setColorBezier(QColor color) +{ + spline_color = color; + update(); +} + +void spline_widget::force_redraw() +{ + _background = QPixmap(); + update(); +} + +void spline_widget::set_preview_only(bool val) +{ + _preview_only = val; +} + +bool spline_widget::is_preview_only() const +{ + return _preview_only; +} + +void spline_widget::drawBackground() +{ + _background = QPixmap(width(), height()); + + QPainter painter(&_background); + + painter.fillRect(rect(), palette().color(QPalette::Light)); + + QColor bg_color(112, 154, 209); + if (!isEnabled() && !_preview_only) + bg_color = QColor(176,176,180); + painter.fillRect(pixel_bounds, bg_color); + + QFont font; + font.setPointSize(8); + painter.setFont(font); + QFontMetrics metrics(font); + + QColor color__(176, 190, 209, 127); + + if (!isEnabled()) + color__ = QColor(70, 90, 100, 96); + + const QPen pen(color__, 1, Qt::SolidLine, Qt::FlatCap); + + const int ystep = _y_step, xstep = _x_step; + const qreal maxx = _config->max_input(); + const qreal maxy = _config->max_output(); + + // horizontal grid + for (int i = 0; i <= maxy; i += ystep) + { + const int y = int(pixel_bounds.height() - i * c.y() + pixel_bounds.y()); + drawLine(painter, + QPoint(pixel_bounds.x(), y), + QPoint(pixel_bounds.x() + pixel_bounds.width(), y), + pen); + painter.drawText(QRectF(10, + y - metrics.height()/2., + pixel_bounds.left(), + metrics.height()), + QString::number(i)); + } + + // vertical grid + for (int i = 0; i <= maxx; i += xstep) + { + const int x = iround(pixel_bounds.x() + i * c.x()); + drawLine(painter, + QPoint(x, pixel_bounds.y()), + QPoint(x, pixel_bounds.y() + pixel_bounds.height()), + pen); + const QString text = QString::number(i); + painter.drawText(QRectF(x - metrics.width(text)/2., + pixel_bounds.height() + 10 + metrics.height(), + metrics.width(text), + metrics.height()), + text); + } +} + +void spline_widget::drawFunction() +{ + _function = _background; + QPainter painter(&_function); + painter.setRenderHint(QPainter::Antialiasing, true); + + const points_t points = _config->get_points(); + + if (moving_control_point_idx >= 0 && + moving_control_point_idx < points.size()) + { + const QPen pen(Qt::white, 1, Qt::SolidLine, Qt::FlatCap); + const QPointF prev_ = point_to_pixel(QPointF(0, 0)); + QPoint prev(iround(prev_.x()), iround(prev_.y())); + for (int i = 0; i < points.size(); i++) + { + const QPointF tmp_ = point_to_pixel(points[i]); + const QPoint tmp(iround(tmp_.x()), iround(tmp_.y())); + drawLine(painter, prev, tmp, pen); + prev = tmp; + } + } + + const QColor color = progn + ( + if (!isEnabled() && !_preview_only) + { + QColor color(spline_color); + const int avg = int(float(color.red() + color.green() + color.blue())/3); + return QColor(int(float(color.red() + avg) * .5f), + int(float(color.green() + avg) * .5f), + int(float(color.blue() + avg) * .5f), + 96); + } + else + { + QColor color(spline_color); + color.setAlphaF(color.alphaF() * .9); + return color; + } + ); + + painter.setPen(QPen(color, 1.75, Qt::SolidLine, Qt::FlatCap)); + +//#define DEBUG_SPLINE +#ifndef DEBUG_SPLINE + static constexpr double step_ = 3./3; + + const double maxx = _config->max_input(); + const double step = step_ / c.x(); + + QPainterPath path; + + path.moveTo(point_to_pixel(QPointF(0, 0))); + + for (double k = 0; k < maxx; k += step*3) + { + const float next_1(_config->get_value_no_save(k + step*1)); + const float next_2(_config->get_value_no_save(k + step*2)); + const float next_3(_config->get_value_no_save(k + step*3)); + + const QPointF b(point_to_pixel_(QPointF(k + step*1, qreal(next_1)))), + c(point_to_pixel_(QPointF(k + step*2, qreal(next_2)))), + d(point_to_pixel_(QPointF(k + step*3, qreal(next_3)))); + + path.cubicTo(b, c, d); + } + + painter.drawPath(path); +#else + static constexpr int line_length_pixels = 3; + const qreal max = _config->maxInput(); + const qreal step = clamp(line_length_pixels / c.x(), 5e-2, max); + QPointF prev = point_to_pixel(QPoint(0, 0)); + for (qreal i = 0; i < max; i += step) + { + const qreal val = qreal(_config->get_value_no_save(i)); + const QPointF cur = point_to_pixel(QPointF(i, val)); + painter.drawLine(prev, cur); + prev = cur; + } + { + const qreal maxx = _config->maxInput(); + const qreal maxy = qreal(_config->get_value_no_save(maxx)); + painter.drawLine(QPointF(prev), point_to_pixel_(QPointF(maxx, maxy))); + } +#endif + + const int alpha = !isEnabled() ? 64 : 120; + if (!_preview_only) + { + for (int i = 0; i < points.size(); i++) + { + drawPoint(painter, + point_to_pixel(points[i]), + QColor(200, 200, 210, alpha), + isEnabled() ? QColor(50, 100, 120, 200) : QColor(200, 200, 200, 96)); + } + } +} + +void spline_widget::paintEvent(QPaintEvent *e) +{ + if (!_config) + return; + + QPainter p(this); + + if (_background.isNull()) + { + _draw_function = true; + drawBackground(); + } + + if (_draw_function) + { + _draw_function = false; + drawFunction(); + } + + p.drawPixmap(e->rect(), _function); + + // If the Tracker is active, the 'Last Point' it requested is recorded. + // Show that point on the graph, with some lines to assist. + // This new feature is very handy for tweaking the curves! + QPointF last; + if (_config->get_last_value(last) && isEnabled()) + drawPoint(p, point_to_pixel(last), QColor(255, 0, 0, 120)); +} + +void spline_widget::drawPoint(QPainter& painter, const QPointF& pos, const QColor& colBG, const QColor& border) +{ + painter.save(); + painter.setPen(QPen(border, 1, Qt::SolidLine, Qt::PenCapStyle::FlatCap)); + painter.setBrush(colBG); + painter.drawEllipse(QRectF(pos.x() - point_size, + pos.y() - point_size, + point_size*2, point_size*2)); + painter.restore(); +} + +void spline_widget::drawLine(QPainter& painter, const QPoint& start, const QPoint& end, const QPen& pen) +{ + painter.save(); + painter.setPen(pen); + painter.setBrush(Qt::NoBrush); + painter.drawLine(start, end); + painter.restore(); +} + +void spline_widget::mousePressEvent(QMouseEvent *e) +{ + if (!_config || !isEnabled() || !is_in_bounds(e->pos())) + { + clearFocus(); + return; + } + + const int point_pixel_closeness_limit = get_closeness_limit(); + + points_t points = _config->get_points(); + if (e->button() == Qt::LeftButton) + { + bool bTouchingPoint = false; + moving_control_point_idx = -1; + for (int i = 0; i < points.size(); i++) + { + if (point_within_pixel(points[i], e->pos())) + { + bTouchingPoint = true; + moving_control_point_idx = int(i); + break; + } + } + if (!bTouchingPoint) + { + bool too_close = false; + const QPoint pos = e->pos(); + + for (int i = 0; i < points.size(); i++) + { + const QPointF pt = point_to_pixel(points[i]); + const qreal x = std::fabs(pt.x() - pos.x()); + if (point_pixel_closeness_limit >= x) + { + too_close = true; + break; + } + } + + if (!too_close) + { + _config->add_point(pixel_coord_to_point(e->pos())); + show_tooltip(e->pos()); + } + } + } + + if (e->button() == Qt::RightButton) + { + if (_config) + { + int found_pt = -1; + for (int i = 0; i < points.size(); i++) + { + if (point_within_pixel(points[i], e->pos())) + { + found_pt = i; + break; + } + } + + if (found_pt != -1) + { + _config->remove_point(found_pt); + } + moving_control_point_idx = -1; + } + } + _draw_function = true; + update(); +} + +void spline_widget::mouseMoveEvent(QMouseEvent *e) +{ + if (_preview_only && _config) + { + show_tooltip(e->pos()); + clearFocus(); + return; + } + + if (!_config || !isEnabled() || !isActiveWindow() || (moving_control_point_idx != -1 && !hasFocus())) + { + clearFocus(); + return; + } + + const int i = moving_control_point_idx; + const points_t points = _config->get_points(); + const int sz = points.size(); + + if (i >= 0 && i < sz) + { + const int point_pixel_closeness_limit = get_closeness_limit(); + bool overlap = false; + + const QPoint pix = progn( + // takes snap into account + const QPointF pix_ = point_to_pixel(pixel_coord_to_point(e->pos())); + return QPoint(int(pix_.x()), int(pix_.y())); + ); + + QPointF new_pt = pixel_coord_to_point(e->pos()); + + if (i + 1 < points.size()) + { + const QPointF other_pix = point_to_pixel(points[i+1]); + overlap |= other_pix.x() - pix.x() < point_pixel_closeness_limit; + } + if (i != 0) + { + const QPointF other_pix = point_to_pixel(points[i-1]); + overlap |= pix.x() - other_pix.x() < point_pixel_closeness_limit; + } + + if (overlap) + new_pt.setX(points[i].x()); + + _config->move_point(i, new_pt); + _draw_function = true; + + setCursor(Qt::ClosedHandCursor); + show_tooltip(pix, new_pt); + update(); + } + else if (sz) + { + int i; + bool is_on_point = is_on_pt(e->pos(), &i); + + if (is_on_point) + { + setCursor(Qt::CrossCursor); + show_tooltip(e->pos(), points[i]); + } + else + { + setCursor(Qt::ArrowCursor); + if (is_in_bounds(e->pos())) + show_tooltip(e->pos()); + else + QToolTip::hideText(); + } + } +} + +void spline_widget::mouseReleaseEvent(QMouseEvent *e) +{ + if (!_config || !isEnabled() || !isActiveWindow() || !hasFocus()) + { + clearFocus(); + return; + } + + if (e->button() == Qt::LeftButton) + { + { + if (is_on_pt(e->pos(), nullptr)) + setCursor(Qt::CrossCursor); + else + setCursor(Qt::ArrowCursor); + } + moving_control_point_idx = -1; + _draw_function = true; + + if (is_in_bounds(e->pos())) + show_tooltip(e->pos()); + else + QToolTip::hideText(); + + update(); + } +} + +void spline_widget::reload_spline() +{ + // don't recompute here as the value's about to be recomputed in the callee + update_range(); +} + +int spline_widget::get_closeness_limit() +{ + return std::max(iround(snap_x * c.x()), 1); +} + +void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_) +{ + const QPointF value = QPoint(0, 0) == value_ ? pixel_coord_to_point(pos) : value_; + + double x = value.x(), y = value.y(); + + if (_preview_only) + y = _config->get_value_no_save(x); + + const int x_ = iround(x), y_ = iround(y); + + using std::fabs; + + if (fabs(x_ - x) < 1e-3) + x = x_; + if (fabs(y_ - y) < 1e-3) + y = y_; + + const bool is_fusion = QStringLiteral("fusion") == QApplication::style()->objectName(); + const int add_x = (is_fusion ? 25 : 0), add_y = (is_fusion ? 15 : 0); + + const QPoint pix(int(pos.x()) + add_x, int(pos.y()) + add_y); + + QToolTip::showText(mapToGlobal(pix), + QStringLiteral("value: %1x%2").arg(x).arg(y), + this, + rect(), + 0); +} + +bool spline_widget::is_in_bounds(const QPoint& pos) const +{ + static constexpr int grace = point_size * 3; + static constexpr int bottom_grace = int(point_size * 1.5); + return (pos.x() + grace > pixel_bounds.left() && + pos.x() - grace < pixel_bounds.right() && + pos.y() + grace > pixel_bounds.top() && + pos.y() - bottom_grace < pixel_bounds.bottom()); +} + +void spline_widget::update_range() +{ + if (!_config) + return; + + const int w = width(), h = height(); + const int mwl = 40, mhl = 20; + const int mwr = 15, mhr = 35; + + pixel_bounds = QRect(mwl, mhl, (w - mwl - mwr), (h - mhl - mhr)); + c = QPointF(pixel_bounds.width() / _config->max_input(), pixel_bounds.height() / _config->max_output()); + _draw_function = true; + + _background = QPixmap(); + _function = QPixmap(); + + update(); +} + +bool spline_widget::point_within_pixel(const QPointF& pt, const QPoint &pixel) +{ + const QPointF tmp = pixel - point_to_pixel(pt); + return QPointF::dotProduct(tmp, tmp) < point_size * point_size; +} + +void spline_widget::focusOutEvent(QFocusEvent* e) +{ + if (moving_control_point_idx != -1) + QToolTip::hideText(); + moving_control_point_idx = -1; + _draw_function = true; + lower(); + setCursor(Qt::ArrowCursor); + e->accept(); +} + +QPointF spline_widget::pixel_coord_to_point(const QPoint& point) +{ + qreal x = (point.x() - pixel_bounds.x()) / c.x(); + qreal y = (pixel_bounds.height() - point.y() + pixel_bounds.y()) / c.y(); + + static constexpr int c = 1000; + + if (snap_x > 0) + { + x += snap_x * .5; + x -= std::fmod(x, snap_x); + // truncate after few decimal places to reduce rounding errors. + // round upward to nearest. + x = int(x * c + .5/c) / double(c); + } + if (snap_y > 0) + { + y += snap_y * .5; + y -= std::fmod(y, snap_y); + // idem + y = int(y * c + .5/c) / double(c); + } + + if (x < 0) + x = 0; + if (x > _config->max_input()) + x = _config->max_input(); + + if (y < 0) + y = 0; + if (y > _config->max_output()) + y = _config->max_output(); + + return QPointF(x, y); +} + +QPointF spline_widget::point_to_pixel_(const QPointF& point) +{ + return QPointF(pixel_bounds.x() + point.x() * c.x(), + pixel_bounds.y() + pixel_bounds.height() - point.y() * c.y()); +} + +QPoint spline_widget::point_to_pixel(const QPointF& point) +{ + QPointF pt(point_to_pixel_(point)); + + return QPoint(iround(pt.x()), iround(pt.y())); +} + +void spline_widget::resizeEvent(QResizeEvent *) +{ + update_range(); +} + +bool spline_widget::is_on_pt(const QPoint& pos, int* pt) +{ + if (!_config) + { + if (pt) + *pt = -1; + return false; + } + + const points_t points = _config->get_points(); + + for (int i = 0; i < points.size(); i++) + { + if (point_within_pixel(points[i], pos)) + { + if (pt) + *pt = i; + return true; + } + } + + if (pt) + *pt = -1; + return false; +} diff --git a/spline/spline-widget.hpp b/spline/spline-widget.hpp new file mode 100644 index 00000000..4d5a7680 --- /dev/null +++ b/spline/spline-widget.hpp @@ -0,0 +1,106 @@ +/* Copyright (c) 2012-2016 Stanislaw Halik + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +// Adapted to FaceTrackNoIR by Wim Vriend. + +#pragma once + +#include "spline.hpp" +#include "api/plugin-api.hpp" +#include "options/options.hpp" +using namespace options; + +#include "export.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class OPENTRACK_SPLINE_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) + Q_PROPERTY(int x_step READ x_step WRITE set_x_step) + Q_PROPERTY(int y_step READ y_step WRITE set_y_step) + + using points_t = spline::points_t; +public: + spline_widget(QWidget *parent = 0); + ~spline_widget(); + + spline* config(); + void setConfig(spline* spl); + + QColor colorBezier() const; + void setColorBezier(QColor color); + + void force_redraw(); + void set_preview_only(bool val); + bool is_preview_only() const; + + double x_step() { return _x_step; } + double y_step() { return _y_step; } + void set_x_step(double val) { _x_step = val; } + void set_y_step(double val) { _y_step = val; } + + void set_snap(double x, double y) { snap_x = x; snap_y = y; } + void get_snap(double& x, double& y) const { x = snap_x; y = snap_y; } +public slots: + void reload_spline(); +protected slots: + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; +private: + int get_closeness_limit(); + void show_tooltip(const QPoint& pos, const QPointF& value = QPointF(0, 0)); + bool is_in_bounds(const QPoint& pos) const; + + void drawBackground(); + void drawFunction(); + void drawPoint(QPainter& painter, const QPointF& pt, const QColor& colBG, const QColor& border = QColor(50, 100, 120, 200)); + void drawLine(QPainter& painter, const QPoint& start, const QPoint& end, const QPen& pen); + bool point_within_pixel(const QPointF& pt, const QPoint& pixel); + + void focusOutEvent(QFocusEvent*e) override; + void resizeEvent(QResizeEvent *) override; + + bool is_on_pt(const QPoint& pos, int* pt = nullptr); + void update_range(); + QPointF pixel_coord_to_point(const QPoint& point); + + QPointF point_to_pixel_(const QPointF& point); + QPoint point_to_pixel(const QPointF& point); + + QPointF c; + spline* _config; + + QPixmap _background; + QPixmap _function; + QColor spline_color; + + // bounds of the rectangle user can interact with + QRect pixel_bounds; + + QMetaObject::Connection connection; + + double snap_x, snap_y; + double _x_step, _y_step; + int moving_control_point_idx; + bool _draw_function, _preview_only; + + static constexpr int point_size = 4; +}; diff --git a/spline/spline.cpp b/spline/spline.cpp new file mode 100644 index 00000000..2db23c14 --- /dev/null +++ b/spline/spline.cpp @@ -0,0 +1,469 @@ +/* Copyright (c) 2012-2016, Stanislaw Halik + + * Permission to use, copy, modify, and/or distribute this + * software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission + * notice appear in all copies. + */ + +#include "spline.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +constexpr int spline::value_count; + +spline::spline(qreal maxx, qreal maxy, const QString& name) : + s(nullptr), + data(value_count, -1.f), + _mutex(QMutex::Recursive), + max_x(maxx), + max_y(maxy), + activep(false), + validp(false) +{ + set_bundle(options::make_bundle(name)); +} + +spline::~spline() +{ + QMutexLocker l(&_mutex); + + if (connection) + { + QObject::disconnect(connection); + connection = QMetaObject::Connection(); + } +} + +spline::spline() : spline(0, 0, "") {} + +void spline::set_tracking_active(bool value) +{ + activep = value; +} + +bundle spline::get_bundle() +{ + return s->b; +} + +void spline::clear() +{ + QMutexLocker l(&_mutex); + s->points = points_t(); + validp = false; +} + +void spline::set_max_input(qreal max_input) +{ + QMutexLocker l(&_mutex); + max_x = max_input; + recompute(); +} + +void spline::set_max_output(qreal max_output) +{ + QMutexLocker l(&_mutex); + max_y = max_output; + recompute(); +} + +qreal spline::max_input() const +{ + QMutexLocker l(&_mutex); + return max_x; +} + +qreal spline::max_output() const +{ + QMutexLocker l(&_mutex); + return max_y; +} + +float spline::get_value(double x) +{ + QMutexLocker foo(&_mutex); + + const float ret = get_value_no_save(x); + last_input_value.setX(std::fabs(x)); + last_input_value.setY(double(std::fabs(ret))); + return ret; +} + +float spline::get_value_no_save(double x) const +{ + return const_cast(*this).get_value_no_save_internal(x); +} + +float spline::get_value_no_save_internal(double x) +{ + QMutexLocker foo(&_mutex); + + if (max_x > 0) + x = std::min(max_x, x); + + float q = float(x * precision(s->points)); + int xi = (int)q; + float yi = get_value_internal(xi); + float yiplus1 = get_value_internal(xi+1); + float f = (q-xi); + float ret = yiplus1 * f + yi * (1.0f - f); // at least do a linear interpolation. + return ret; +} + +DEFUN_WARN_UNUSED bool spline::get_last_value(QPointF& point) +{ + QMutexLocker foo(&_mutex); + point = last_input_value; + return activep; +} + +float spline::get_value_internal(int x) +{ + if (!validp) + { + update_interp_data(); + validp = true; + } + + float sign = x < 0 ? -1 : 1; + x = std::abs(x); + float ret; + ret = data[std::min(unsigned(x), unsigned(value_count)-1u)]; + return ret * sign; +} + +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& points, double max_x, int i) +{ + const int sz = element_count(points, max_x); + + if (i < 0 || sz == 0) + return QPointF(0, 0); + + if (i < sz) + return points[i]; + + return points[sz - 1]; +} + +int spline::element_count(const QList& points, double max_x) +{ + if (!(max_x > 0)) + return points.size(); + else + { + const unsigned sz = points.size(); + for (unsigned i = 0; i < sz; i++) + { + if (!(points[i].x() <= max_x)) + return i; + } + return points.size(); + } +} + +bool spline::sort_fn(const QPointF& one, const QPointF& two) +{ + return one.x() <= two.x(); +} + +void spline::update_interp_data() +{ + points_t points = s->points; + + int sz = element_count(points, max_x); + + if (sz == 0) + points.prepend(QPointF(max_x, max_y)); + + std::stable_sort(points.begin(), points.begin() + sz, sort_fn); + + const double mult = precision(points); + const double mult_ = mult * 30; + + for (unsigned i = 0; i < value_count; i++) + data[i] = -1; + + if (sz < 2) + { + if (points[0].x() - 1e-2 <= max_x) + { + const double x = points[0].x(); + const double y = points[0].y(); + const int max = clamp(int(x * precision(points)), 1, value_count-1); + for (int k = 0; k <= max; k++) + { + if (k < value_count) + data[unsigned(k)] = float(y * k / max); + } + } + } + else + { + if (points[0].x() > 1e-2 && points[0].x() <= max_x) + points.push_front(QPointF(0, 0)); + + for (int i = 0; i < sz; i++) + { + const QPointF p0 = ensure_in_bounds(points, max_x, i - 1); + const QPointF p1 = ensure_in_bounds(points, max_x, i + 0); + const QPointF p2 = ensure_in_bounds(points, max_x, i + 1); + const QPointF p3 = ensure_in_bounds(points, max_x, i + 2); + 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(); + + const double cx[4] = { + 2 * p1_x, // 1 + -p0_x + p2_x, // t + 2 * p0_x - 5 * p1_x + 4 * p2_x - p3_x, // t^2 + -p0_x + 3 * p1_x - 3 * p2_x + p3_x, // t3 + }; + + const double cy[4] = + { + 2 * p1_y, // 1 + -p0_y + p2_y, // t + 2 * p0_y - 5 * p1_y + 4 * p2_y - p3_y, // t^2 + -p0_y + 3 * p1_y - 3 * p2_y + p3_y, // t3 + }; + + // multiplier helps fill in all the x's needed + 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 double t = (j - start) / (double) (end - start); + const double t2 = t*t; + const double t3 = t*t*t; + + const int x = iround(.5 * mult * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3)); + const float y = float(.5 * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3)); + + if (x >= 0 && x < value_count) + data[unsigned(x)] = y; + } + } + } + + float last = 0; + for (unsigned i = 0; i < unsigned(value_count); i++) + { + if (data[i] < 0) + data[i] = last; + last = data[i]; + } +} + +void spline::remove_point(int i) +{ + QMutexLocker foo(&_mutex); + + points_t points = s->points; + const int sz = element_count(points, max_x); + + if (i >= 0 && i < sz) + { + points.erase(points.begin() + i); + s->points = points; + validp = false; + } +} + +void spline::add_point(QPointF pt) +{ + QMutexLocker foo(&_mutex); + + points_t points = s->points; + points.push_back(pt); + std::stable_sort(points.begin(), points.end(), sort_fn); + s->points = points; + validp = false; +} + +void spline::add_point(double x, double y) +{ + add_point(QPointF(x, y)); +} + +void spline::move_point(int idx, QPointF pt) +{ + QMutexLocker foo(&_mutex); + + points_t points = s->points; + + const int sz = element_count(points, max_x); + + if (idx >= 0 && idx < sz) + { + points[idx] = pt; + // we don't allow points to be reordered, but sort due to possible caller logic error + std::stable_sort(points.begin(), points.end(), sort_fn); + s->points = points; + validp = false; + } +} + +QList spline::get_points() const +{ + QMutexLocker foo(&_mutex); + return s->points; +} + +int spline::get_point_count() const +{ + QMutexLocker foo(&_mutex); + return element_count(s->points, max_x);; +} + +void spline::reload() +{ + QMutexLocker foo(&_mutex); + s->b->reload(); +} + +void spline::save(QSettings& settings) +{ + QMutexLocker foo(&_mutex); + s->b->save_deferred(settings); +} + +void spline::save() +{ + save(*group::ini_file()); +} + +void spline::set_bundle(bundle b) +{ + QMutexLocker l(&_mutex); + + // gets called from ctor hence the need for nullptr checks + // the sentinel settings/bundle objects don't need any further branching once created + if (!s || s->b != b) + { + s = std::make_shared(b); + + if (connection) + QObject::disconnect(connection); + + if (b) + { + connection = QObject::connect(b.get(), &bundle_::changed, + s.get(), [&]() { + // we're holding the mutex to allow signal disconnection in spline dtor + // before this slot gets called for the next time + + // spline isn't a QObject and the connection context is incorrect + + QMutexLocker l(&_mutex); + recompute(); + emit s->recomputed(); + }, + Qt::QueuedConnection); + } + + recompute(); + + emit s->recomputed(); + } +} + +void spline::recompute() +{ + QMutexLocker foo(&_mutex); + + QList list = s->points; + + // storing to s->points fires bundle::changed and that leads to an infinite loop + // only store if we can't help it + std::stable_sort(list.begin(), list.end(), sort_fn); + + if (list != s->points) + s->points = list; + + const int sz = list.size(); + + QList ret_list; + ret_list.reserve(sz); + + for (int i = 0; i < sz; i++) + { + QPointF& pt(list[i]); + + const bool overlap = progn( + for (int j = 0; j < i; j++) + { + QPointF& pt2(list[j]); + const double dist_sq = (pt.x() - pt2.x())*(pt.x() - pt2.x()); + static constexpr double overlap = .6; + if (dist_sq < overlap * overlap) + return true; + } + return false; + ); + if (!overlap) + ret_list.push_back(pt); + } + + if (ret_list != s->points) + s->points = ret_list; + + last_input_value = QPointF(0, 0); + activep = false; + validp = false; +} + +// the return value is only safe to use with no spline::set_bundle calls +mem spline::get_settings() +{ + QMutexLocker foo(&_mutex); + return s; +} + +mem spline::get_settings() const +{ + QMutexLocker foo(&_mutex); + return s; +} + +double spline::precision(const QList& points) const +{ + // this adjusts the memoized range to the largest X value. empty space doesn't take value_count discrete points. + const int sz = element_count(points, max_x); + if (sz) + return clamp(value_count / clamp(points[sz - 1].x(), 1., max_x), 0., double(value_count)); + + return value_count / clamp(max_x, 1., double(value_count)); +} + +namespace spline_detail { + +settings::settings(bundle b): + b(b ? b : make_bundle("")), + points(b, "points", QList()) +{} + +settings::~settings() +{ +} + +} diff --git a/spline/spline.hpp b/spline/spline.hpp new file mode 100644 index 00000000..77b2bdf9 --- /dev/null +++ b/spline/spline.hpp @@ -0,0 +1,108 @@ +/* Copyright (c) 2012-2016, Stanislaw Halik + + * Permission to use, copy, modify, and/or distribute this + * software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission + * notice appear in all copies. + */ + +#pragma once + +#include "compat/qcopyable-mutex.hpp" +#include "options/options.hpp" +#include "compat/util.hpp" +using namespace options; + +#include "export.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace spline_detail { + +class OPENTRACK_SPLINE_EXPORT settings final : public QObject +{ + Q_OBJECT +public: + bundle b; + value> points; + settings(bundle b); + ~settings() override; +signals: + void recomputed() const; +}; + +} + +class OPENTRACK_SPLINE_EXPORT spline final +{ + double precision(const QList& points) const; + void update_interp_data(); + float get_value_internal(int x); + void add_lone_point(); + float get_value_no_save_internal(double x); + static bool sort_fn(const QPointF& one, const QPointF& two); + + static QPointF ensure_in_bounds(const QList& points, double max_x, int i); + static int element_count(const QList& points, double max_x); + + mem s; + QMetaObject::Connection connection; + + std::vector data; + using interp_data_t = decltype(data); + + static constexpr int value_count = 4096; + + MyMutex _mutex; + QPointF last_input_value; + qreal max_x, max_y; + volatile bool activep; + bool validp; + +public: + using settings = spline_detail::settings; + + void reload(); + void save(QSettings& s); + void save(); + void set_bundle(bundle b); + + qreal max_input() const; + qreal max_output() const; + spline(); + spline(qreal maxx, qreal maxy, const QString& name); + ~spline(); + + spline& operator=(const spline&) = default; + spline(const spline&) = default; + + float get_value(double x); + float get_value_no_save(double x) const; + DEFUN_WARN_UNUSED bool get_last_value(QPointF& point); + void remove_point(int i); + void clear(); + + void add_point(QPointF pt); + void add_point(double x, double y); + void move_point(int idx, QPointF pt); + QList get_points() const; + void set_max_input(qreal MaxInput); + void set_max_output(qreal MaxOutput); + + void set_tracking_active(bool value); + bundle get_bundle(); + void recompute(); + + mem get_settings(); + mem get_settings() const; + + using points_t = decltype(s->points()); + int get_point_count() const; +}; diff --git a/tracker-tobii-eyex/CMakeLists.txt b/tracker-tobii-eyex/CMakeLists.txt index 66fa90ae..31125e1a 100644 --- a/tracker-tobii-eyex/CMakeLists.txt +++ b/tracker-tobii-eyex/CMakeLists.txt @@ -3,13 +3,13 @@ if(WIN32) if(SDK_TOBII_EYEX) set(porked NO-INSTALL) otr_module(tracker-tobii-eyex ${porked}) - target_link_libraries(opentrack-tracker-tobii-eyex opentrack-spline-widget) + target_link_libraries(opentrack-tracker-tobii-eyex opentrack-spline) set(tobii-libdir ${SDK_TOBII_EYEX}/lib/x86/) set(tobii-dll ${tobii-libdir}/Tobii.EyeX.Client.dll) # we only care about the .lib for MSVC++ build anyway set(tobii-link ${tobii-libdir}/Tobii.EyeX.Client.lib) - target_include_directories(opentrack-tracker-tobii-eyex PRIVATE ${CMAKE_SOURCE_DIR}/spline-widget) - target_link_libraries(opentrack-tracker-tobii-eyex ${tobii-link} opentrack-spline-widget) + target_include_directories(opentrack-tracker-tobii-eyex PRIVATE ${CMAKE_SOURCE_DIR}/spline) + target_link_libraries(opentrack-tracker-tobii-eyex ${tobii-link} opentrack-spline) # we only ever use the C headers due to Microsoft CRT ABI incompatibility with GNU set(tobii-incdir ${SDK_TOBII_EYEX}/include/eyex) target_include_directories(opentrack-tracker-tobii-eyex SYSTEM PUBLIC ${tobii-incdir}) diff --git a/tracker-tobii-eyex/tobii-eyex-dialog.ui b/tracker-tobii-eyex/tobii-eyex-dialog.ui index b6138514..f32c91f4 100644 --- a/tracker-tobii-eyex/tobii-eyex-dialog.ui +++ b/tracker-tobii-eyex/tobii-eyex-dialog.ui @@ -537,7 +537,7 @@ spline_widget QWidget -
spline-widget/spline-widget.hpp
+
spline/spline-widget.hpp
diff --git a/tracker-tobii-eyex/tobii-settings.hpp b/tracker-tobii-eyex/tobii-settings.hpp index 5c09b201..08e05ecf 100644 --- a/tracker-tobii-eyex/tobii-settings.hpp +++ b/tracker-tobii-eyex/tobii-settings.hpp @@ -3,8 +3,8 @@ #include "api/plugin-api.hpp" #include "options/options.hpp" using namespace options; -#include "spline-widget/spline.hpp" -#include "spline-widget/spline-widget.hpp" +#include "spline/spline.hpp" +#include "spline/spline-widget.hpp" #include -- cgit v1.2.3