summaryrefslogtreecommitdiffhomepage
path: root/qfunctionconfigurator
diff options
context:
space:
mode:
Diffstat (limited to 'qfunctionconfigurator')
-rw-r--r--qfunctionconfigurator/broken/qfunctionconfiguratorplugin.cpp111
-rw-r--r--qfunctionconfigurator/broken/qfunctionconfiguratorplugin.h37
-rw-r--r--qfunctionconfigurator/functionconfig.cpp281
-rw-r--r--qfunctionconfigurator/functionconfig.h75
-rw-r--r--qfunctionconfigurator/qfunctionconfigurator.cpp413
-rw-r--r--qfunctionconfigurator/qfunctionconfigurator.h92
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