aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Koivikko <jarkko.koivikko@code-q.fi>2017-04-30 18:32:52 +0300
committerJarkko Koivikko <jarkko.koivikko@code-q.fi>2017-05-15 16:00:39 +0000
commitf1258d58bbb72e193a2ba0bc4add3d7d94965d7b (patch)
tree53b73f9674dce3ad01f7372ab1366a06cc1c120d
parent6200f4c997e2dbeedd8b3f23b43f77b3ebe6c97c (diff)
Move gesture recognition to class, so other input methods can use it
LipiInputMethod supports simple swipe gesture recognition. This can also be used in other input methods, so move it to simple class. Change-Id: Ib5328e0570346d534c71787fcb79a6c7e4c3d0a7 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r--src/virtualkeyboard/gesturerecognizer.cpp39
-rw-r--r--src/virtualkeyboard/gesturerecognizer.h50
-rw-r--r--src/virtualkeyboard/handwritinggesturerecognizer.cpp203
-rw-r--r--src/virtualkeyboard/handwritinggesturerecognizer.h54
-rw-r--r--src/virtualkeyboard/lipiinputmethod.cpp159
-rw-r--r--src/virtualkeyboard/virtualkeyboard.pro8
6 files changed, 357 insertions, 156 deletions
diff --git a/src/virtualkeyboard/gesturerecognizer.cpp b/src/virtualkeyboard/gesturerecognizer.cpp
new file mode 100644
index 00000000..09db68a5
--- /dev/null
+++ b/src/virtualkeyboard/gesturerecognizer.cpp
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gesturerecognizer.h"
+
+namespace QtVirtualKeyboard {
+
+GestureRecognizer::GestureRecognizer(QObject *parent) :
+ QObject(parent)
+{
+}
+
+}
diff --git a/src/virtualkeyboard/gesturerecognizer.h b/src/virtualkeyboard/gesturerecognizer.h
new file mode 100644
index 00000000..f1636609
--- /dev/null
+++ b/src/virtualkeyboard/gesturerecognizer.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GESTURERECOGNIZER_H
+#define GESTURERECOGNIZER_H
+
+#include <QObject>
+#include <QVariantMap>
+#include "trace.h"
+
+namespace QtVirtualKeyboard {
+
+class GestureRecognizer : public QObject
+{
+ Q_OBJECT
+public:
+ explicit GestureRecognizer(QObject *parent = 0);
+
+ virtual QVariantMap recognize(const QList<Trace *> traceList) = 0;
+};
+
+} // namespace QtVirtualKeyboard
+
+#endif // GESTURERECOGNIZER_H
diff --git a/src/virtualkeyboard/handwritinggesturerecognizer.cpp b/src/virtualkeyboard/handwritinggesturerecognizer.cpp
new file mode 100644
index 00000000..fa3838aa
--- /dev/null
+++ b/src/virtualkeyboard/handwritinggesturerecognizer.cpp
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "handwritinggesturerecognizer.h"
+
+#include <QtCore/qmath.h>
+#include <QVector2D>
+
+namespace QtVirtualKeyboard {
+
+HandwritingGestureRecognizer::HandwritingGestureRecognizer(QObject *parent) :
+ GestureRecognizer(parent),
+ m_dpi(96)
+{
+}
+
+void HandwritingGestureRecognizer::setDpi(int value)
+{
+ m_dpi = value >= 0 ? value : 96;
+}
+
+int HandwritingGestureRecognizer::dpi() const
+{
+ return m_dpi;
+}
+
+QVariantMap HandwritingGestureRecognizer::recognize(const QList<Trace *> traceList)
+{
+ if (traceList.count() > 0 && traceList.count() < 3) {
+
+ // Swipe gesture detection
+ // =======================
+ //
+ // The following algorithm is based on the assumption that a
+ // vector composed of two arbitrary selected, but consecutive
+ // measuring points, and a vector composed of the first and last
+ // of the measuring points, are approximately in the same angle.
+ //
+ // If the measuring points are located very close to each other,
+ // the angle can fluctuate a lot. This has been taken into account
+ // by setting a minimum Euclidean distance between the measuring
+ // points.
+ //
+
+ // Minimum euclidean distance of a segment (in millimeters)
+ static const int MINIMUM_EUCLIDEAN_DISTANCE = 8;
+
+ // Maximum theta variance (in degrees)
+ static const qreal THETA_THRESHOLD = 25.0;
+
+ // Maximum width variance in multitouch swipe (+- in percent)
+ static const int MAXIMUM_WIDTH_VARIANCE = 20;
+
+ const qreal minimumEuclideanDistance = MINIMUM_EUCLIDEAN_DISTANCE / 25.4 * m_dpi;
+ static const qreal thetaThreshold = qDegreesToRadians(THETA_THRESHOLD);
+
+ QList<QVector2D> swipeVectors;
+
+ int traceIndex;
+ const int traceCount = traceList.size();
+ for (traceIndex = 0; traceIndex < traceCount; ++traceIndex) {
+
+ const Trace *trace = traceList.at(traceIndex);
+ const QVariantList &points = trace->points();
+ QVector2D swipeVector;
+ const int pointCount = points.count();
+ int pointIndex = 0;
+ if (pointCount >= 2) {
+
+ QPointF startPosition = points.first().toPointF();
+ swipeVector = QVector2D(points.last().toPointF() - startPosition);
+ const qreal swipeLength = swipeVector.length();
+
+ if (swipeLength >= minimumEuclideanDistance) {
+
+ QPointF previousPosition = startPosition;
+ qreal euclideanDistance = 0;
+ for (pointIndex = 1; pointIndex < pointCount; ++pointIndex) {
+
+ QPointF currentPosition(points.at(pointIndex).toPointF());
+
+ euclideanDistance += QVector2D(currentPosition - previousPosition).length();
+ if (euclideanDistance >= minimumEuclideanDistance) {
+
+ // Set the angle (theta) between the sample vector and the swipe vector
+ const QVector2D sampleVector(currentPosition - startPosition);
+ const qreal theta = qAcos(QVector2D::dotProduct(swipeVector, sampleVector) / (swipeLength * sampleVector.length()));
+
+ // Rejected when theta above threshold
+ if (theta >= thetaThreshold) {
+ swipeVector = QVector2D();
+ break;
+ }
+
+ startPosition = currentPosition;
+ euclideanDistance = 0;
+ }
+
+ previousPosition = currentPosition;
+ }
+
+ if (pointIndex < pointCount) {
+ swipeVector = QVector2D();
+ break;
+ }
+
+ // Check to see if angle and length matches to existing touch points
+ if (!swipeVectors.isEmpty()) {
+ bool matchesToExisting = true;
+ const qreal minimumSwipeLength = (swipeLength * (100.0 - MAXIMUM_WIDTH_VARIANCE) / 100.0);
+ const qreal maximumSwipeLength = (swipeLength * (100.0 + MAXIMUM_WIDTH_VARIANCE) / 100.0);
+ for (const QVector2D &otherSwipeVector : qAsConst(swipeVectors)) {
+ const qreal otherSwipeLength = otherSwipeVector.length();
+ const qreal theta = qAcos(QVector2D::dotProduct(swipeVector, otherSwipeVector) / (swipeLength * otherSwipeLength));
+
+ if (theta >= thetaThreshold) {
+ matchesToExisting = false;
+ break;
+ }
+
+ if (otherSwipeLength < minimumSwipeLength || otherSwipeLength > maximumSwipeLength) {
+ matchesToExisting = false;
+ break;
+ }
+ }
+
+ if (!matchesToExisting) {
+ swipeVector = QVector2D();
+ break;
+ }
+ }
+ } else {
+ swipeVector = QVector2D();
+ }
+ }
+
+ if (swipeVector.isNull())
+ break;
+
+ swipeVectors.append(swipeVector);
+ }
+
+ if (swipeVectors.size() == traceCount) {
+
+ QVariantMap swipeGesture;
+
+ // Get swipe angle from the first vector:
+ // 0 degrees == right
+ // 90 degrees == down
+ // 180 degrees == left
+ // 270 degrees == up
+ QList<QVector2D>::ConstIterator swipeVector = swipeVectors.constBegin();
+ qreal swipeLength = swipeVector->length();
+ qreal swipeAngle = qAcos(swipeVector->x() / swipeLength);
+ if (swipeVector->y() < 0)
+ swipeAngle = 2 * M_PI - swipeAngle;
+
+ // Calculate an average length of the vector
+ for (++swipeVector; swipeVector != swipeVectors.end(); ++swipeVector)
+ swipeLength += swipeVector->length();
+ swipeLength /= traceCount;
+
+ swipeGesture[QLatin1String("type")] = QLatin1String("swipe");
+ swipeGesture[QLatin1String("angle")] = swipeAngle;
+ swipeGesture[QLatin1String("angle_degrees")] = qRadiansToDegrees(swipeAngle);
+ swipeGesture[QLatin1String("length")] = swipeLength;
+ swipeGesture[QLatin1String("length_mm")] = swipeLength / m_dpi * 25.4;
+ swipeGesture[QLatin1String("touch_count")] = traceCount;
+
+ return swipeGesture;
+ }
+ }
+
+ return QVariantMap();
+}
+
+}
diff --git a/src/virtualkeyboard/handwritinggesturerecognizer.h b/src/virtualkeyboard/handwritinggesturerecognizer.h
new file mode 100644
index 00000000..5ef2ba14
--- /dev/null
+++ b/src/virtualkeyboard/handwritinggesturerecognizer.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HANDWRITINGGESTURERECOGNIZER_H
+#define HANDWRITINGGESTURERECOGNIZER_H
+
+#include "gesturerecognizer.h"
+
+namespace QtVirtualKeyboard {
+
+class HandwritingGestureRecognizer : public GestureRecognizer
+{
+ Q_OBJECT
+public:
+ explicit HandwritingGestureRecognizer(QObject *parent = 0);
+
+ void setDpi(int dpi);
+ int dpi() const;
+
+ QVariantMap recognize(const QList<Trace *> traceList);
+
+private:
+ int m_dpi;
+};
+
+} // namespace QtVirtualKeyboard
+
+#endif // HANDWRITINGGESTURERECOGNIZER_H
diff --git a/src/virtualkeyboard/lipiinputmethod.cpp b/src/virtualkeyboard/lipiinputmethod.cpp
index 30ee9ed4..5a6d7886 100644
--- a/src/virtualkeyboard/lipiinputmethod.cpp
+++ b/src/virtualkeyboard/lipiinputmethod.cpp
@@ -34,6 +34,7 @@
#include "shifthandler.h"
#include "virtualkeyboarddebug.h"
#include "trace.h"
+#include "handwritinggesturerecognizer.h"
#ifdef HAVE_HUNSPELL
#include "hunspellinputmethod_p.h"
@@ -55,8 +56,6 @@
#include <QDir>
#endif
-#include <QtCore/qmath.h>
-
#ifdef HAVE_HUNSPELL
#define LipiInputMethodPrivateBase HunspellInputMethodPrivate
#else
@@ -147,6 +146,8 @@ public:
}
}
+ gestureRecognizer.setDpi(deviceInfo->getXDPI());
+
currentContext = context;
}
@@ -201,7 +202,7 @@ public:
if (countActiveTraces() > 0)
return;
- QVariantMap gesture = detectGesture();
+ QVariantMap gesture = gestureRecognizer.recognize(traceList);
if (gesture.isEmpty())
return;
@@ -296,157 +297,6 @@ public:
}
}
- QVariantMap detectGesture()
- {
- if (traceList.count() > 0 && traceList.count() < 3) {
-
- // Swipe gesture detection
- // =======================
- //
- // The following algorithm is based on the assumption that a
- // vector composed of two arbitrary selected, but consecutive
- // measuring points, and a vector composed of the first and last
- // of the measuring points, are approximately in the same angle.
- //
- // If the measuring points are located very close to each other,
- // the angle can fluctuate a lot. This has been taken into account
- // by setting a minimum Euclidean distance between the measuring
- // points.
- //
-
- // Minimum euclidean distance of a segment (in millimeters)
- static const int MINIMUM_EUCLIDEAN_DISTANCE = 8;
-
- // Maximum theta variance (in degrees)
- static const qreal THETA_THRESHOLD = 25.0;
-
- // Maximum width variance in multitouch swipe (+- in percent)
- static const int MAXIMUM_WIDTH_VARIANCE = 20;
-
- const int dpi = deviceInfo->getXDPI() >= 0 ? deviceInfo->getXDPI() : 96;
- const qreal minimumEuclideanDistance = MINIMUM_EUCLIDEAN_DISTANCE / 25.4 * dpi;
- static const qreal thetaThreshold = qDegreesToRadians(THETA_THRESHOLD);
-
- QList<QVector2D> swipeVectors;
-
- int traceIndex;
- const int traceCount = traceList.size();
- for (traceIndex = 0; traceIndex < traceCount; ++traceIndex) {
-
- const Trace *trace = traceList.at(traceIndex);
- const QVariantList &points = trace->points();
- QVector2D swipeVector;
- const int pointCount = points.count();
- int pointIndex = 0;
- if (pointCount >= 2) {
-
- QPointF startPosition = points.first().toPointF();
- swipeVector = QVector2D(points.last().toPointF() - startPosition);
- const qreal swipeLength = swipeVector.length();
-
- if (swipeLength >= minimumEuclideanDistance) {
-
- QPointF previousPosition = startPosition;
- qreal euclideanDistance = 0;
- for (pointIndex = 1; pointIndex < pointCount; ++pointIndex) {
-
- QPointF currentPosition(points.at(pointIndex).toPointF());
-
- euclideanDistance += QVector2D(currentPosition - previousPosition).length();
- if (euclideanDistance >= minimumEuclideanDistance) {
-
- // Set the angle (theta) between the sample vector and the swipe vector
- const QVector2D sampleVector(currentPosition - startPosition);
- const qreal theta = qAcos(QVector2D::dotProduct(swipeVector, sampleVector) / (swipeLength * sampleVector.length()));
-
- // Rejected when theta above threshold
- if (theta >= thetaThreshold) {
- swipeVector = QVector2D();
- break;
- }
-
- startPosition = currentPosition;
- euclideanDistance = 0;
- }
-
- previousPosition = currentPosition;
- }
-
- if (pointIndex < pointCount) {
- swipeVector = QVector2D();
- break;
- }
-
- // Check to see if angle and length matches to existing touch points
- if (!swipeVectors.isEmpty()) {
- bool matchesToExisting = true;
- const qreal minimumSwipeLength = (swipeLength * (100.0 - MAXIMUM_WIDTH_VARIANCE) / 100.0);
- const qreal maximumSwipeLength = (swipeLength * (100.0 + MAXIMUM_WIDTH_VARIANCE) / 100.0);
- for (const QVector2D &otherSwipeVector : qAsConst(swipeVectors)) {
- const qreal otherSwipeLength = otherSwipeVector.length();
- const qreal theta = qAcos(QVector2D::dotProduct(swipeVector, otherSwipeVector) / (swipeLength * otherSwipeLength));
-
- if (theta >= thetaThreshold) {
- matchesToExisting = false;
- break;
- }
-
- if (otherSwipeLength < minimumSwipeLength || otherSwipeLength > maximumSwipeLength) {
- matchesToExisting = false;
- break;
- }
- }
-
- if (!matchesToExisting) {
- swipeVector = QVector2D();
- break;
- }
- }
- } else {
- swipeVector = QVector2D();
- }
- }
-
- if (swipeVector.isNull())
- break;
-
- swipeVectors.append(swipeVector);
- }
-
- if (swipeVectors.size() == traceCount) {
-
- QVariantMap swipeGesture;
-
- // Get swipe angle from the first vector:
- // 0 degrees == right
- // 90 degrees == down
- // 180 degrees == left
- // 270 degrees == up
- QList<QVector2D>::ConstIterator swipeVector = swipeVectors.constBegin();
- qreal swipeLength = swipeVector->length();
- qreal swipeAngle = qAcos(swipeVector->x() / swipeLength);
- if (swipeVector->y() < 0)
- swipeAngle = 2 * M_PI - swipeAngle;
-
- // Calculate an average length of the vector
- for (++swipeVector; swipeVector != swipeVectors.end(); ++swipeVector)
- swipeLength += swipeVector->length();
- swipeLength /= traceCount;
-
- swipeGesture[QLatin1String("type")] = QLatin1String("swipe");
- swipeGesture[QLatin1String("angle")] = swipeAngle;
- swipeGesture[QLatin1String("angle_degrees")] = qRadiansToDegrees(swipeAngle);
- swipeGesture[QLatin1String("length")] = swipeLength;
- swipeGesture[QLatin1String("length_mm")] = swipeLength / dpi * 25.4;
- swipeGesture[QLatin1String("touch_count")] = traceCount;
-
- return swipeGesture;
- }
- }
-
- return QVariantMap();
- }
-
void clearTraces()
{
qDeleteAll(traceList);
@@ -647,6 +497,7 @@ public:
InputEngine::TextCase textCase;
vector<int> subsetOfClasses;
QVariantMap delayedResult;
+ HandwritingGestureRecognizer gestureRecognizer;
};
/*!
diff --git a/src/virtualkeyboard/virtualkeyboard.pro b/src/virtualkeyboard/virtualkeyboard.pro
index 74ceffe8..018c5b98 100644
--- a/src/virtualkeyboard/virtualkeyboard.pro
+++ b/src/virtualkeyboard/virtualkeyboard.pro
@@ -34,7 +34,9 @@ SOURCES += platforminputcontext.cpp \
virtualkeyboardsettings.cpp \
trace.cpp \
desktopinputselectioncontrol.cpp \
- shadowinputcontext.cpp
+ shadowinputcontext.cpp \
+ gesturerecognizer.cpp \
+ handwritinggesturerecognizer.cpp
HEADERS += platforminputcontext.h \
inputcontext.h \
@@ -55,7 +57,9 @@ HEADERS += platforminputcontext.h \
plugin.h \
trace.h \
desktopinputselectioncontrol.h \
- shadowinputcontext.h
+ shadowinputcontext.h \
+ gesturerecognizer.h \
+ handwritinggesturerecognizer.h
RESOURCES += \
content/styles/default/default_style.qrc \