From 4758f1216bd16763c72500bc3c2f0fb43c08d613 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Mon, 14 Dec 2009 15:41:07 -0800 Subject: [PATCH] Added the Protractor algorithm for calculating the minimum cosine distance between gestures --- core/java/android/gesture/GestureStore.java | 7 +++- .../android/gesture/GestureUtilities.java | 32 +++++++++++++++++++ core/java/android/gesture/Instance.java | 2 +- .../java/android/gesture/InstanceLearner.java | 4 +-- core/java/android/gesture/Learner.java | 2 +- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java index 5f1a44503604e..11a94d1552948 100644 --- a/core/java/android/gesture/GestureStore.java +++ b/core/java/android/gesture/GestureStore.java @@ -65,7 +65,12 @@ public class GestureStore { // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures public static final int ORIENTATION_INVARIANT = 1; + // at most 2 directions can be recognized public static final int ORIENTATION_SENSITIVE = 2; + // at most 4 directions can be recognized + static final int ORIENTATION_SENSITIVE_4 = 4; + // at most 8 directions can be recognized + static final int ORIENTATION_SENSITIVE_8 = 8; private static final short FILE_FORMAT_VERSION = 1; @@ -131,7 +136,7 @@ public class GestureStore { public ArrayList recognize(Gesture gesture) { Instance instance = Instance.createInstance(mSequenceType, mOrientationStyle, gesture, null); - return mClassifier.classify(mSequenceType, instance.vector); + return mClassifier.classify(mSequenceType, mOrientationStyle, instance.vector); } /** diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java index 40d70295fc9b2..f1dcd89d3babe 100755 --- a/core/java/android/gesture/GestureUtilities.java +++ b/core/java/android/gesture/GestureUtilities.java @@ -366,6 +366,38 @@ final class GestureUtilities { } return Math.acos(sum); } + + /** + * Calculate the "minimum" cosine distance between two instances + * + * @param vector1 + * @param vector2 + * @param numOrientations the maximum number of orientation allowed + * @return the distance between the two instances (between 0 and Math.PI) + */ + static double minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) { + final int len = vector1.length; + double a = 0; + double b = 0; + for (int i = 0; i < len; i += 2) { + a += vector1[i] * vector2[i] + vector1[i + 1] * vector2[i + 1]; + b += vector1[i] * vector2[i + 1] - vector1[i + 1] * vector2[i]; + } + if (a != 0) { + final double tan = b/a; + final double angle = Math.atan(tan); + if (numOrientations > 2 && Math.abs(angle) >= Math.PI / numOrientations) { + return Math.acos(a); + } else { + final double cosine = Math.cos(angle); + final double sine = cosine * tan; + return Math.acos(a * cosine + b * sine); + } + } else { + return Math.PI / 2; + } + } + static OrientedBoundingBox computeOrientedBoundingBox(ArrayList pts) { GestureStroke stroke = new GestureStroke(pts); diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java index ef208acf4ec79..68a298529d787 100755 --- a/core/java/android/gesture/Instance.java +++ b/core/java/android/gesture/Instance.java @@ -94,7 +94,7 @@ class Instance { float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]); float adjustment = -orientation; - if (orientationType == GestureStore.ORIENTATION_SENSITIVE) { + if (orientationType != GestureStore.ORIENTATION_INVARIANT) { int count = ORIENTATIONS.length; for (int i = 0; i < count; i++) { float delta = ORIENTATIONS[i] - orientation; diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java index b93b76fa9a876..9987e6915eaf2 100644 --- a/core/java/android/gesture/InstanceLearner.java +++ b/core/java/android/gesture/InstanceLearner.java @@ -41,7 +41,7 @@ class InstanceLearner extends Learner { }; @Override - ArrayList classify(int sequenceType, float[] vector) { + ArrayList classify(int sequenceType, int orientationType, float[] vector) { ArrayList predictions = new ArrayList(); ArrayList instances = getInstances(); int count = instances.size(); @@ -53,7 +53,7 @@ class InstanceLearner extends Learner { } double distance; if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) { - distance = GestureUtilities.cosineDistance(sample.vector, vector); + distance = GestureUtilities.minimumCosineDistance(sample.vector, vector, orientationType); } else { distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector); } diff --git a/core/java/android/gesture/Learner.java b/core/java/android/gesture/Learner.java index feacde5f97365..60997e0bb8a73 100755 --- a/core/java/android/gesture/Learner.java +++ b/core/java/android/gesture/Learner.java @@ -79,5 +79,5 @@ abstract class Learner { instances.removeAll(toDelete); } - abstract ArrayList classify(int gestureType, float[] vector); + abstract ArrayList classify(int sequenceType, int orientationType, float[] vector); }