diff options
Diffstat (limited to 'qfunctionconfigurator')
| -rw-r--r-- | qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp | 111 | ||||
| -rw-r--r-- | qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h | 37 | ||||
| -rw-r--r-- | qfunctionconfigurator/functionconfig.cpp | 281 | ||||
| -rw-r--r-- | qfunctionconfigurator/functionconfig.h | 75 | ||||
| -rw-r--r-- | qfunctionconfigurator/qfunctionconfigurator.cpp | 413 | ||||
| -rw-r--r-- | qfunctionconfigurator/qfunctionconfigurator.h | 92 |
6 files changed, 1009 insertions, 0 deletions
diff --git a/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp new file mode 100644 index 00000000..1a9da10a --- /dev/null +++ b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp @@ -0,0 +1,111 @@ +/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl> + * 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 <QtCore/QtPlugin> +#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 "<widget class=\"QFunctionConfigurator\" name=\"qFunctionA\">\n" + " <property name=\"geometry\">\n" + " <rect>\n" + " <x>0</x>\n" + " <y>0</y>\n" + " <width>161</width>\n" + " <height>220</height>\n" + " </rect>\n" + " </property>\n" + " <property name=\"colorBezier\">\n" + " <color>\n" + " <red>255</red>\n" + " <green>170</green>\n" + " <blue>0</blue>\n" + " </color>\n" + " </property>\n" + " <property name=\"stringInputEGU\" stdset=\"0\">\n" + " <string>Input Yaw (degr.)</string>\n" + " </property>\n" + " <property name=\"stringOutputEGU\" stdset=\"0\">\n" + " <string>Output Yaw (degr.)</string>\n" + " </property>\n" + " <property name=\"maxInputEGU\" stdset=\"0\">\n" + " <number>50</number>\n" + " </property>\n" + " <property name=\"maxOutputEGU\" stdset=\"0\">\n" + " <number>180</number>\n" + " </property>\n" + " <property name=\"pixPerEGU_Input\" stdset=\"0\">\n" + " <number>2</number>\n" + " </property>\n" + " <property name=\"pixPerEGU_Output\" stdset=\"0\">\n" + " <number>1</number>\n" + " </property>\n" + "</widget>\n"; +} + +QString QFunctionConfiguratorPlugin::includeFile() const +{ + return "qfunctionconfigurator.h"; +} + +Q_EXPORT_PLUGIN2(qfunctionconfigurator, QFunctionConfiguratorPlugin) diff --git a/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h new file mode 100644 index 00000000..bc637338 --- /dev/null +++ b/qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl> + * 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 <QDesignerCustomWidgetInterface> + +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/qfunctionconfigurator/functionconfig.cpp b/qfunctionconfigurator/functionconfig.cpp new file mode 100644 index 00000000..97a6db24 --- /dev/null +++ b/qfunctionconfigurator/functionconfig.cpp @@ -0,0 +1,281 @@ +/* Copyright (c) 2012, 2013 Stanisław Halik <sthalik@misaki.pl> + + * 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 <QMutexLocker> +#include <QCoreApplication> +#include <QPointF> +#include <QList> +#include "functionconfig.h" +#include <QtAlgorithms> +#include <QtAlgorithms> +#include <QSettings> +#include <math.h> +#include <QPixmap> + +// +// Constructor with List of Points in argument. +// +FunctionConfig::FunctionConfig(QString title, int intMaxInput, int intMaxOutput) : + _mutex(QMutex::Recursive) +{ + _title = title; + _points = QList<QPointF>(); + _data = 0; + _size = 0; + lastValueTracked = QPointF(0,0); + _tracking_active = false; + _max_Input = intMaxInput; // Added WVR 20120805 + _max_Output = intMaxOutput; + QSettings settings("opentrack"); // Registry settings (in HK_USER) + QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString(); + QSettings iniFile( currentFile, QSettings::IniFormat ); + loadSettings(iniFile); + reload(); +} + +void FunctionConfig::setTrackingActive(bool blnActive) +{ + _tracking_active = blnActive; +} + +FunctionConfig::FunctionConfig() : + _mutex(QMutex::Recursive), + _data(0), + _size(0), + _tracking_active(false), + _max_Input(0), + _max_Output(0) +{ +} + +// +// Calculate the value of the function, given the input 'x'. +// Used to draw the curve and, most importantly, to translate input to output. +// The return-value is also stored internally, so the Widget can show the current value, when the Tracker is running. +// +float FunctionConfig::getValue(float x) { + QMutexLocker foo(&_mutex); + int x2 = (int) (std::min<float>(std::max<float>(x, -360), 360) * MEMOIZE_PRECISION); + float ret = getValueInternal(x2); + lastValueTracked.setX(x); + lastValueTracked.setY(ret); + return ret; +} + +// +// The return-value is also stored internally, so the Widget can show the current value, when the Tracker is running. +// +bool FunctionConfig::getLastPoint(QPointF& point ) { + QMutexLocker foo(&_mutex); + point = lastValueTracked; + return _tracking_active; +} + +float FunctionConfig::getValueInternal(int x) { + float sign = x < 0 ? -1 : 1; + x = x < 0 ? -x : x; + float ret; + if (!_data) + ret = 0; + else if (_size == 0) + ret = 0; + else if (x < 0) + ret = 0; + else if (x < _size && x >= 0) + ret = _data[x]; + else + ret = _data[_size - 1]; + return ret * sign; +} + +static __inline QPointF ensureInBounds(QList<QPointF> points, int i) { + int siz = points.size(); + if (siz == 0 || i < 0) + return QPointF(0, 0); + if (siz > i) + return points[i]; + return points[siz - 1]; +} + +static bool sortFn(const QPointF& one, const QPointF& two) { + return one.x() < two.x(); +} + +void FunctionConfig::reload() { + _size = 0; + + if (_points.size()) + qStableSort(_points.begin(), _points.end(), sortFn); + + if (_data) + delete[] _data; + _data = NULL; + if (_points.size()) { + _data = new float[_size = MEMOIZE_PRECISION * _points[_points.size() - 1].x()]; + + for (int i = 0; i < _size; i++) + _data[i] = -1e6; + + for (int k = 0; k < _points[0].x() * MEMOIZE_PRECISION; k++) { + if (k < _size) + _data[k] = _points[0].y() * k / (_points[0].x() * MEMOIZE_PRECISION); + } + + for (int i = 0; i < _points.size(); i++) { + QPointF p0 = ensureInBounds(_points, i - 1); + QPointF p1 = ensureInBounds(_points, i); + QPointF p2 = ensureInBounds(_points, i + 1); + QPointF p3 = ensureInBounds(_points, i + 2); + + int end = p2.x() * MEMOIZE_PRECISION; + int start = p1.x() * MEMOIZE_PRECISION; + + for (int j = start; j < end && j < _size; j++) { + double t = (j - start) / (double) (end - start); + double t2 = t*t; + double t3 = t*t*t; + + int x = .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) + * MEMOIZE_PRECISION; + + float y = .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 < _size) + _data[x] = y; + } + } + + float last = 0; + + for (int i = 0; i < _size; i++) + { + if (_data[i] == -1e6) + _data[i] = last; + last = _data[i]; + } + } +} + +FunctionConfig::~FunctionConfig() { + if (_data) + delete[] _data; +} + +// +// Remove a Point from the Function. +// Used by the Widget. +// +void FunctionConfig::removePoint(int i) { + QMutexLocker foo(&_mutex); + if (i >= 0 && i < _points.size()) + { + _points.removeAt(i); + reload(); + } +} + +// +// Add a Point to the Function. +// Used by the Widget and by loadSettings. +// +void FunctionConfig::addPoint(QPointF pt) { + QMutexLocker foo(&_mutex); + _points.append(pt); + reload(); +} + +// +// Move a Function Point. +// Used by the Widget. +// +void FunctionConfig::movePoint(int idx, QPointF pt) { + QMutexLocker foo(&_mutex); + if (idx >= 0 && idx < _points.size()) + { + _points[idx] = pt; + reload(); + } +} + +// +// Return the List of Points. +// Used by the Widget. +// +QList<QPointF> FunctionConfig::getPoints() { + QList<QPointF> ret; + QMutexLocker foo(&_mutex); + for (int i = 0; i < _points.size(); i++) { + ret.append(_points[i]); + } + return ret; +} + +// +// Load the Points for the Function from the INI-file designated by settings. +// Settings for a specific Curve are loaded from their own Group in the INI-file. +// +void FunctionConfig::loadSettings(QSettings& settings) { + 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).toFloat(), + settings.value(QString("point-%1-y").arg(i), 0).toFloat()); + // + // Make sure the new Point fits in the Function Range. + // Maybe this can be improved? + // + if (newPoint.x() > _max_Input) { + newPoint.setX(_max_Input); + } + if (newPoint.y() > _max_Output) { + newPoint.setY(_max_Output); + } + points.append(newPoint); + } + settings.endGroup(); + _points = points; + reload(); +} + +// +// Save the Points for the Function to the INI-file designated by settings. +// Settings for a specific Curve are saved in their own Group in the INI-file. +// The number of Points is also saved, to make loading more convenient. +// +void FunctionConfig::saveSettings(QSettings& settings) { + QMutexLocker foo(&_mutex); + settings.beginGroup(QString("Curves-%1").arg(_title)); + int max = _points.size(); + settings.setValue("point-count", max); + for (int i = 0; i < max; i++) { + settings.setValue(QString("point-%1-x").arg(i), _points[i].x()); + settings.setValue(QString("point-%1-y").arg(i), _points[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)); + } + settings.endGroup(); +} diff --git a/qfunctionconfigurator/functionconfig.h b/qfunctionconfigurator/functionconfig.h new file mode 100644 index 00000000..4d771dfd --- /dev/null +++ b/qfunctionconfigurator/functionconfig.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2011-2012, Stanislaw Halik <sthalik@misaki.pl> + + * 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 <QList> +#include <QPointF> +#include <QString> +#include <QSettings> +#include <QMutex> +#include "ftnoir_tracker_base/ftnoir_tracker_base.h" + +#define MEMOIZE_PRECISION 100 + +class FTNOIR_TRACKER_BASE_EXPORT FunctionConfig { +private: + QMutex _mutex; + QList<QPointF> _points; + void reload(); + float* _data; + int _size; + QString _title; + float getValueInternal(int x); + QPointF lastValueTracked; // The last input value requested by the Tracker, with it's output-value. + volatile bool _tracking_active; + int _max_Input; + int _max_Output; + FunctionConfig(const FunctionConfig&) = delete; +public: + int maxInput() const { return _max_Input; } + int maxOutput() const { return _max_Output; } + // + // Contructor(s) and destructor + // + FunctionConfig(); + FunctionConfig(QString title, int intMaxInput, int intMaxOutput); + ~FunctionConfig(); + + float getValue(float x); + bool getLastPoint(QPointF& point); // Get the last Point that was requested. + + // + // Functions to manipulate the Function + // + void removePoint(int i); + void removeAllPoints() { + QMutexLocker foo(&_mutex); + _points.clear(); + reload(); + } + + void addPoint(QPointF pt); + void movePoint(int idx, QPointF pt); + QList<QPointF> getPoints(); + void setMaxInput(int MaxInput) { + _max_Input = MaxInput; + } + void setMaxOutput(int MaxOutput) { + _max_Output = MaxOutput; + } + + // + // Functions to load/save the Function-Points to an INI-file + // + void saveSettings(QSettings& settings); + void loadSettings(QSettings& settings); + + void setTrackingActive(bool blnActive); + QString getTitle() { return _title; } +}; diff --git a/qfunctionconfigurator/qfunctionconfigurator.cpp b/qfunctionconfigurator/qfunctionconfigurator.cpp new file mode 100644 index 00000000..55d1e1bc --- /dev/null +++ b/qfunctionconfigurator/qfunctionconfigurator.cpp @@ -0,0 +1,413 @@ +/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl> + * 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/qfunctionconfigurator.h" +#include <QPainter> +#include <QPaintEvent> +#include <QPainterPathStroker> +#include <QPainterPath> +#include <QBrush> +#include <QFileDialog> +#include <QPen> +#include <QMessageBox> +#include <QImage> +#include <QPixmap> +#include <QTimer> +#include <QtDebug> +#include <cmath> +#include <QTabWidget> +#include <QTabBar> +#include <QFontMetrics> + +static const int pointSize = 5; + +QFunctionConfigurator::QFunctionConfigurator(QWidget *parent) + : QWidget(parent) +{ + movingPoint = -1; // Index of that same point + _config = 0; + _draw_background = true; + _draw_function = true; + update_range(); + setMouseTracking(true); +} + +void QFunctionConfigurator::setConfig(FunctionConfig* config) { + QSettings settings("opentrack"); // Registry settings (in HK_USER) + QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString(); + QSettings iniFile( currentFile, QSettings::IniFormat ); // Application settings (in INI-file) + + config->loadSettings(iniFile); + _config = config; + _draw_function = _draw_background = true; + update_range(); + update(); +} + +void QFunctionConfigurator::saveSettings(QString settingsFile) { + QSettings iniFile( settingsFile, QSettings::IniFormat ); // Application settings (in INI-file) + + if (_config) { + _config->saveSettings(iniFile); + } +} + +void QFunctionConfigurator::drawBackground() +{ + if (!_config) + return; + _background = QPixmap(width(), height()); + QPainter painter(&_background); + painter.fillRect(rect(), QColor::fromRgb(204, 204, 204)); + painter.setRenderHint(QPainter::Antialiasing); + QColor bg_color(112, 154, 209); + painter.fillRect(range, bg_color); + + QFont font; + font.setPointSize(8); + painter.setFont(font); + QFontMetrics metrics(font); + + QPen pen(QColor(55, 104, 170, 127), 1, Qt::SolidLine); + + const int xstep = 10, ystep = 10; + const int maxx = _config->maxInput(); + const int maxy = _config->maxOutput(); + + // horizontal grid + + for (int i = 0; i < maxy; i += xstep) + { + double y = range.height() - i * c.y() + range.y(); + drawLine(&painter, + QPointF(range.x(), y), + QPointF(range.x() + range.width(), y), + pen); + painter.drawText(QRectF(10, + y - metrics.height()/2, + range.left(), + metrics.height()), + QString::number(i)); + } + + { + const int i = maxy; + double y = range.height() - i * c.y() + range.y(); + drawLine(&painter, + QPointF(range.x(), y), + QPointF(range.x() + range.width(), y), + pen); + painter.drawText(QRectF(10, + y - metrics.height()/2, + range.x() - 10, + metrics.height()), + QString::number(i)); + } + + // vertical grid + + for (int i = 0; i < maxx; i += ystep) + { + double x = range.x() + i * c.x(); + drawLine(&painter, + QPointF(x, range.y()), + QPointF(x, range.y() + range.height()), + pen); + const QString text = QString::number(i); + painter.drawText(QRectF(x - metrics.width(text)/2, + range.height() + 10 + metrics.height(), + metrics.width(text), + metrics.height()), + text); + } + { + const int i = maxx; + double x = range.x() + i * c.x(); + drawLine(&painter, + QPointF(x, range.y()), + QPointF(x, range.y() + range.height()), + pen); + const QString text = QString::number(i); + painter.drawText(QRectF(x - metrics.width(text)/2, + range.height() + 10 + metrics.height(), + metrics.width(text), + metrics.height()), + text); + } +} + +void QFunctionConfigurator::drawFunction() +{ + if (!_config) + return; + int i; + QPointF prevPoint; + QPointF currentPoint; + + _function = QPixmap(_background); + QPainter painter(&_function); + + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + + QList<QPointF> points = _config->getPoints(); + + for (i = 0; i < points.size(); i++) { + currentPoint = point_to_pixel( points[i] ); // Get the next point and convert it to Widget measures + drawPoint(&painter, currentPoint, QColor(200, 200, 210, 120)); + lastPoint = currentPoint; // Remember which point is the rightmost in the graph + } + + + QPen pen(colBezier, 1.2, Qt::SolidLine); + + prevPoint = point_to_pixel( QPointF(0,0) ); // Start at the Axis + double max = _config->maxInput(); + QPointF prev = point_to_pixel(QPointF(0, 0)); + const double step = 1.01; + for (double i = 0; i < max; i += step) { + double val = _config->getValue(i); + QPointF cur = point_to_pixel(QPointF(i, val)); + drawLine(&painter, prev, cur, pen); + prev = cur; + } + painter.restore(); +} + +void QFunctionConfigurator::paintEvent(QPaintEvent *e) +{ + QPointF prevPoint; + QPointF currentPoint; + QPointF actualPos; + int i; + + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + if (_draw_background) { + drawBackground(); + _draw_background = false; + } + p.drawPixmap(e->rect(), _background); + + if (_draw_function) { + drawFunction(); // Draw the Function on a Pixmap + _draw_function = false; + } + p.drawPixmap(e->rect(), _function); // Always draw the background and the function + + if (_config) { + QPen pen(Qt::white, 1, Qt::SolidLine); + QList<QPointF> points = _config->getPoints(); + if (movingPoint >= 0 && movingPoint < points.size()) { + prevPoint = point_to_pixel( QPointF(0,0) ); // Start at the Axis + for (i = 0; i < points.size(); i++) { + currentPoint = point_to_pixel( points[i] ); // Get the next point and convert it to Widget measures + drawLine(&p, prevPoint, currentPoint, pen); + prevPoint = currentPoint; + } + pen.setWidth(1); + pen.setColor( Qt::white ); + pen.setStyle( Qt::DashLine ); + actualPos = point_to_pixel(points[movingPoint]); + drawLine(&p, QPoint(range.left(), actualPos.y()), QPoint(actualPos.x(), actualPos.y()), pen); + drawLine(&p, QPoint(actualPos.x(), actualPos.y()), QPoint(actualPos.x(), range.height() + range.top()), pen); + } + + // + // 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! + // + if (_config->getLastPoint( currentPoint )) { + actualPos = point_to_pixel( QPointF(fabs(currentPoint.x()), fabs(currentPoint.y())) ); + drawPoint(&p, actualPos, QColor(255, 0, 0, 120)); + + pen.setWidth(1); + pen.setColor( Qt::black ); + pen.setStyle( Qt::SolidLine ); + drawLine(&p, QPoint(range.left(), actualPos.y()), QPoint(actualPos.x(), actualPos.y()), pen); + drawLine(&p, QPoint(actualPos.x(), actualPos.y()), QPoint(actualPos.x(), range.width()), pen); + } + + } +} + +// +// Draw the handle, to move the Bezier-curve. +// +void QFunctionConfigurator::drawPoint(QPainter *painter, const QPointF &pos, QColor colBG ) +{ + painter->save(); + painter->setPen(QColor(50, 100, 120, 200)); + painter->setBrush( colBG ); + painter->drawEllipse(QRectF(pos.x() - pointSize, + pos.y() - pointSize, + pointSize*2, pointSize*2)); + painter->restore(); +} + +void QFunctionConfigurator::drawLine(QPainter *painter, const QPointF &start, const QPointF &end, QPen pen) +{ + painter->save(); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawLine(start, end); + painter->restore(); +} + +void QFunctionConfigurator::mousePressEvent(QMouseEvent *e) +{ + if (!_config) + return; + QList<QPointF> points = _config->getPoints(); + if (e->button() == Qt::LeftButton) { + bool bTouchingPoint = false; + movingPoint = -1; + if (_config) { + for (int i = 0; i < points.size(); i++) { + if ( point_within_pixel(points[i], e->pos() ) ) { + bTouchingPoint = true; + movingPoint = i; + timer.restart(); + break; + } + } + if (!bTouchingPoint) { + _config->addPoint(pixel_coord_to_point(e->pos())); + emit CurveChanged( true ); + } + } + } + + 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->removePoint(found_pt); + emit CurveChanged( true ); + } + movingPoint = -1; + } + } + _draw_function = true; + update(); +} + +void QFunctionConfigurator::mouseMoveEvent(QMouseEvent *e) +{ + if (!_config) + return; + QList<QPointF> points = _config->getPoints(); + const int refresh_delay = 50; + + if (movingPoint >= 0 && movingPoint < points.size()) { + setCursor(Qt::ClosedHandCursor); + + if (timer.isValid() && timer.elapsed() > refresh_delay) + { + timer.restart(); + QPointF new_pt = pixel_coord_to_point(e->pos()); + points[movingPoint] = new_pt; + _config->movePoint(movingPoint, new_pt); + _draw_function = true; + update(); + } + } + else { + bool bTouchingPoint = false; + if (_config) { + for (int i = 0; i < points.size(); i++) { + if ( point_within_pixel(points[i], e->pos() ) ) { + bTouchingPoint = true; + } + } + } + + if ( bTouchingPoint ) { + setCursor(Qt::OpenHandCursor); + } + else { + setCursor(Qt::ArrowCursor); + } + } +} + +void QFunctionConfigurator::mouseReleaseEvent(QMouseEvent *e) +{ + if (!_config) + return; + QList<QPointF> points = _config->getPoints(); + + if (e->button() == Qt::LeftButton) { + timer.invalidate(); + if (movingPoint >= 0 && movingPoint < points.size()) { + emit CurveChanged( true ); + if (_config) { + _config->movePoint(movingPoint, pixel_coord_to_point(e->pos())); + } + } + setCursor(Qt::ArrowCursor); + movingPoint = -1; + } + + _draw_function = true; + update(); +} + +bool QFunctionConfigurator::point_within_pixel(QPointF pt, QPointF pixel) const +{ + QPointF pixel2(range.x() + pt.x() * c.x(), (range.y() + range.height() - pt.y() * c.y())); + return pixel2.x() >= pixel.x() - pointSize && pixel2.x() < pixel.x() + pointSize && + pixel2.y() >= pixel.y() - pointSize && pixel2.y() < pixel.y() + pointSize; +} + +QPointF QFunctionConfigurator::pixel_coord_to_point(QPointF point) const +{ + if (!_config) + return QPointF(-1, -1); + + double x = (point.x() - range.x()) / c.x(); + double y = (range.height() - point.y() + range.y()) / c.y(); + + if (x < 0) + x = 0; + if (x > _config->maxInput()) + x = _config->maxInput(); + + if (y < 0) + y = 0; + if (y > _config->maxOutput()) + y = _config->maxOutput(); + + return QPointF(x, y); +} + +QPointF QFunctionConfigurator::point_to_pixel(QPointF point) const +{ + return QPointF(range.x() + point.x() * c.x(), + range.y() + range.height() - point.y() * c.y()); +} + +void QFunctionConfigurator::setColorBezier(QColor color) +{ + colBezier = color; + update(); +} + +void QFunctionConfigurator::resizeEvent(QResizeEvent *) +{ + update_range(); + repaint(); +} diff --git a/qfunctionconfigurator/qfunctionconfigurator.h b/qfunctionconfigurator/qfunctionconfigurator.h new file mode 100644 index 00000000..1f6b4f78 --- /dev/null +++ b/qfunctionconfigurator/qfunctionconfigurator.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2011-2012 Stanislaw Halik <sthalik@misaki.pl> + * 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 QFUNCTIONCONFIGURATOR_H +#define QFUNCTIONCONFIGURATOR_H + +#include <QWidget> +#include <QtGui> +#include <QPointF> +#include <QElapsedTimer> +#include "qfunctionconfigurator/functionconfig.h" +#include "ftnoir_tracker_base/ftnoir_tracker_base.h" + +// +// The FunctionConfigurator Widget is used to display and configure a function (curve). +// The Function is used by FaceTrackNoIR to 'translate' the actual head-pose to the virtual headpose. Every axis is configured by a separate Function. +// +// The Function is coded in a separate Class and can exists, without the Widget. When the widget is displayed (therefore 'created'), the Function can be attached to the +// Widget and the Widget used to change the Function. +// + +class FTNOIR_TRACKER_BASE_EXPORT QFunctionConfigurator : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QColor colorBezier READ colorBezier WRITE setColorBezier) + QColor colorBezier() const + { + return colBezier; + } +public: + QFunctionConfigurator(QWidget *parent = 0); + FunctionConfig* config(); + + void setConfig(FunctionConfig* config); + void saveSettings(QString settingsFile); + +signals: + void CurveChanged(bool); + +public slots: + void setColorBezier(QColor); +protected slots: + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + +protected: + void drawBackground(); + void drawFunction(); + void drawPoint(QPainter *painter, const QPointF &pt, QColor colBG ); + void drawLine(QPainter *painter, const QPointF &start, const QPointF &end, QPen pen); + bool point_within_pixel(QPointF pt, QPointF pixel) const; + +protected: + virtual void resizeEvent(QResizeEvent *); + +private: + void update_range() { + if (!_config) + return; + double w = width(), h = height(); + const double mwl = 40, mhl = 20; + const double mwr = 15, mhr = 35; + range = QRectF(mwl, mhl, (w - mwl - mwr), (h - mhl - mhr)); + c = QPointF(range.width() / _config->maxInput(), range.height() / _config->maxOutput()); + _draw_function = _draw_background = true; + } + + QRectF range; // The actual rectangle for the Bezier-curve + QPointF lastPoint; // The right-most point of the Function + QPointF pixel_coord_to_point (QPointF point) const; // Convert the graphical Point to a real-life Point + QPointF point_to_pixel (QPointF point) const; // Convert the Point to a graphical Point + + int movingPoint; + QElapsedTimer timer; + QPointF c; + + QColor colBezier; // Color of Bezier curve + + bool _draw_background; // Flag to determine if the background should be (re-)drawn on the QPixmap + QPixmap _background; // Image of the static parts (axis, lines, etc.) + bool _draw_function; // Flag to determine if the function should be (re-)drawn on the QPixmap + QPixmap _function; // Image of the function (static unless edited by the user) + + FunctionConfig* _config; +}; + +#endif // QFUNCTIONCONFIGURATOR_H |
