В первой части этой серии я представил класс для обработки распознавания одиночного удара мыши: MGesture
. Этот урок делает шаг вперед, обнаруживая последовательность штрихов.
Для этого мы будем использовать класс GManager
в тандеме с MGesture
. После ознакомления с функциональностью GManager
будет разработано приложение, демонстрирующее его использование.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться. Чтобы использовать презентацию Flash ниже, используйте мышь для выполнения жеста, как показано стрелкой в верхнем левом углу. Жест, нажав левую кнопку мыши, перемещая мышь, удерживая кнопку, а затем отпустив ее.
Шаг 1: Повторная нумерация направлений
Первый шаг, пересмотр целых чисел, используемых для представления различных направлений из части 1 . Надеемся, что вы можете сформировать мысленную картину на диаграмме выше, так как мы будем ссылаться на нее в течение всего этого урока.
Шаг 2: Обнаружены удары
Когда пользователь делает жест любым указательным устройством (мышью, пером планшета и т. Д.), Векторы, успешно обнаруженные с течением времени, фактически образуют последовательность целых чисел. Обратитесь к диаграмме ниже.
Обратите внимание, что уникальные целые числа в последовательности равны 0, 4, 1. Однако я скажу, что уникальные целые числа равны 0, 1. Посмотрим правде в глаза: трудно сделать точный штрих на моем планшете Bamboo, и тем более с помощью мыши. Ошибки неизбежны. Наш алгоритм должен тогда основываться на большом повторении целых чисел в непрерывной строке. Их здесь 0, 1 — 4, вероятно, из-за неточного удара. Следовательно, этот жест может быть однозначно идентифицирован как последовательность 0, а затем 1.
Я включил еще несколько жестов с сопровождающими их уникальными последовательностями ниже.
Шаг 3: Представляем GManager
Цель этого класса проста:
- Чтобы зарегистрировать уникальные последовательности штрихов для сравнения.
- Для захвата последовательности, обнаруженной текущего жеста, сделанного пользователем.
- Сравнить последовательность в (2) со всеми последовательностями штрихов в (1).
- Чтобы вернуть результат поиска.
Разработчик должен определить набор последовательностей штрихов для первоначального обнаружения. После этого можно запрограммировать прослушиватели событий для захвата пользовательских жестов во время выполнения. Если последовательность текущего жеста успешно совпадает с какой-либо одной последовательностью в предварительно определенном наборе, тогда могут быть выполнены различные операции.
Шаг 4: Переменные
GManager
имеет следующие переменные:
переменная | Тип данных | Цель |
gestSeq |
Vector.<Vector.<int>> |
2D массив для записи различных последовательностей штрихов. |
gestName |
Vector.<String> |
1D массив для записи имен последовательностей штрихов. |
strokes |
Vector.<int> |
1D массив для записи ограничения на gestCurrent по сравнению с последовательностью штрихов. |
gestCurrent |
Vector.<int> |
1D массив для записи текущей последовательности штрихов. |
_order |
Boolean |
Определяет, должны ли наборы жестов в gestSeq обнаруживаться по порядку. |
orderCurrent |
int |
Текущая последовательность, чтобы определить, _order ли _order ; начинается с 0. |
Шаг 5: Методы
Методы в GManager
следующие:
метод | вход | Выход | Описание |
GManager |
недействительным | недействительным | Инициирование класса, gestSeq и gestName, инициализированы |
register |
Vector.<int>, String, int |
недействительным | Зарегистрируйте последовательность хода, имя и ограничение хода (необязательно). |
remove |
int |
недействительным | Удалить выбранную последовательность штрихов |
removeAll |
недействительным | недействительным | Удалить все зарегистрированные последовательности штрихов |
start |
недействительным | недействительным | Подготовьте переменную для записи текущей последовательности gestCurrent , gestCurrent |
populate |
int |
недействительным | gestCurrent с обнаруженными штрихами (единственное число) |
tracer |
недействительным | Vector.<int> |
gestCurrent и выводит gestCurrent для целей отладки. |
dropStrokes |
Vector.<int>, int |
Vector.<int> |
Устраните ненужные удары в gestCurrent . Держите сеть и диагонали (0), оставляйте только сеть (1), оставляйте только диагонали (2). (Часть 3 дополнительно объяснит его использование.) |
dropDuplicates |
Vector.<int>, int |
Vector.<int> |
Определите действительное уникальное целое число в текущей последовательности и отбросьте дубликаты. Введите минимальные дубликаты (кроме себя), которые будут считаться допустимым штрихом |
checkMatch |
int |
int |
Проверяет, gestSeq ли элемент gestSeq с gestCurrent |
end |
недействительным | Array |
Возвращает результат поиска. Результатом является Array с индексом соответствия последовательность в gestSeq и ее имя в gestName |
Свойства GManager
следующие:
Свойство | Типы доступа | Цель |
useOrder |
Геттер / Сеттер | Получает и устанавливает, gestSeq ли gestSeq быть обнаружен в порядке |
length |
Только добытчик | Получает длину gestSeq |
Шаг 6: Зарегистрируйте предопределенные последовательности штрихов
Как уже упоминалось, первым шагом является регистрация уникальных последовательностей штрихов, с которыми можно сравнить введенный жест. Конструктор инициирует необходимые переменные (подсвеченные) и методы, которые следуют за регистрами и удаляет из предопределенных последовательностей штрихов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
/**
* Constructor to initiate gestSeq, stokes, gestName
*/
public function GManager(){
gestSeq = new Vector.<Vector.<int>>;
gestName = new Vector.<String>;
strokes = new Vector.<int>;
}
/**
* Method to register a set/ sequence of gesture
* @param sequence Numerical representation of gesture sequence
* @param identifier Name representation of gesture
* @param allowed Stokes allowed: Both (0), main four (1), diagonals (2)
*/
public function register(sequence:Vector.<int>, name:String, onlyStrokes:int = 0):void {
gestSeq.push(sequence);
gestName.push(name);
strokes.push(onlyStrokes);
}
/**
* Method to remove a specific set of gesture
* @param selectSet Selected sequence to remove
*/
public function remove(selectSet:int):void {
gestSeq.splice(selectSet, 1);
gestName.splice(selectSet, 1);
strokes.splice(selectSet, 1);
}
/**
* Method to remove all predefined gestures.
*/
public function removeAll():void {
gestSeq = new Vector.<Vector.<int>>;
gestName = new Vector.<String>;
strokes = new Vector.<int>;
}
|
Шаг 7: Свойства
Ниже приведены свойства, специально предназначенные для последовательностей удержания штриха, gestSeq
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
/**
* Property to get total sets of predefined gestures
*/
public function get length ():int {
return gestSeq.length;
}
/**
* Property to get current setting on sequential gesture detection
*/
public function get useOrder():Boolean {
return _order;
}
/**
* Property to determine whether sets of predefined gestures should be detected sequentially
*/
public function set useOrder(value:Boolean):void {
_order = value;
}
|
Шаг 8: запишите текущую последовательность хода
После регистрации последовательностей штрихов для обнаружения в них мы можем подготовить gestCurrent
и записать в него последовательные действительные единичные штрихи. Методы ниже обеспечат это. Tracer
— это метод для отслеживания последовательности текущего жеста.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/**
* Method to start recording current set of gesture made
*/
public function start():void {
gestCurrent = new Vector.<int>;
}
/**
* Method to populate gesture into current set of gesture
* @param gestureCode Gesture detected from MGesture
*/
public function populate (gestureCode:int):void {
//Accept only valid singular gesture (0~8) into current sequence
if(gestureCode > -1) gestCurrent.push(gestureCode);
}
/**
* Method used for debugging.
*/
public function tracer():Vector.<int> {
return gestCurrent;
trace(gestCurrent);
}
|
Шаг 9: gestCurrent
К настоящему времени мы уже можем написать программу для проверки последовательности текущего жеста, как указано в шаге 2. Вы можете скачать исходный код и посмотреть на класс CheckOut2
. Включите панель «Вывод» в FlashDevelop, чтобы просмотреть последовательность целых чисел, зафиксированных вашим текущим жестом.
Я также включил демонстрацию этого ниже и поместил TextField
для отображения последовательности вместо панели «Вывод» в FlashDevelop.
Шаг 10: Реализация CheckOut2
Я включил исходный код checkOut2
как checkOut2
ниже. Нетрудно понять логику, если вы поняли, как назначаются обработчики событий. ( Прочтите это руководство, чтобы узнать больше об обработчиках событий .) Я также выделил важные моменты, в которых GManager
методы GManager
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
public class CheckOut2 extends Sprite
{
private var ges:MGesture;
private var gesMan:GManager;
private var t:TextField;
public function CheckOut2()
{
//initiating MGesture to allow singular gesture detection
ges = new MGesture(stage);
//initiating GManager to allow sequential gesture detection
gesMan = new GManager();
t = new TextField()
tx = 50;
ty = 50;
t.autoSize = TextFieldAutoSize.LEFT;
addChild(t);
stage.addEventListener(MouseEvent.MOUSE_DOWN, start);
stage.addEventListener(MouseEvent.MOUSE_UP, end);
}
private function start(e:MouseEvent):void
{
//Start detecting singular and sequential gesture
ges.start();
gesMan.start();
stage.addEventListener(MouseEvent.MOUSE_MOVE, check);
//Start drawing gesture
graphics.clear();
graphics.lineStyle(3);
graphics.moveTo(mouseX, mouseY);
}
private function check(e:MouseEvent):void
{
gesMan.populate(ges.evalDirections());
graphics.lineTo(mouseX, mouseY) //Drawing gesture
}
private function end(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, check);
t.text = gesMan.tracer().toString()
}
}
|
Шаг 11: Очистить gestCurrent
от gestCurrent
ненужных gestCurrent
Очистка штрихов в текущем жесте важна, так как есть:
- Удары, затрудняющие распознавание жестов.
- Удары, которые дублируются
- Штрихи, которые являются недействительными
Я разделил работу по очистке на две фазы, а именно dropStrokes
и dropDuplicates
. dropStrokes
устраняет штрихи, которые затрудняют обнаружение жестов. (Эта функция будет подробно объяснена в части 3, когда мы обнаружим буквы алфавита.) dropDuplicates
удаляет дубликаты и недействительные штрихи.
dropStrokes
будет хранить только обводки в gestCurrent
соответствии с третьим параметром, onlyStrokes
, в register
. Это определяется, когда мы регистрируем последовательности жестов изначально. Для дальнейшего понимания, опять же остерегайтесь части 3. Но сейчас просто держите ее в затылке. На самом деле, этот урок пока не использует эту функцию.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/**
* Method to rid irrelevant strokes based upon condition
* @param strokesCondition
* @return Vector of
*/
private function dropStrokes(inputArray:Vector.<int>, strokesCondition:int):Vector.<int> {
var xDiagonals:Vector.<int> = inputArray.slice(0,inputArray.length);
//Keep mains only
if (strokesCondition == 1) {
for (var i:int = 0; i < xDiagonals.length; i++) {
if (xDiagonals[i] > 3) {
xDiagonals.splice(i, 1);
i—
}
}
}
else if (strokesCondition == 2) {
for (var j:int = 0; j < xDiagonals.length; j++) {
if (xDiagonals[j] <4) {
xDiagonals.splice(j, 1);
j—
}
}
}
return xDiagonals
}
|
Шаг 12: Очистить gestCurrent
Проведите пальцем слева направо, чтобы перейти к следующему кадру.
Второй этап очистки — это удаление дублирующих и недействительных штрихов, как указано в шаге 2. Реализация описана ниже.
Я разместил презентацию Flash выше, чтобы облегчить ваше понимание алгоритма. Сделайте правый жест, используя мышь, чтобы перейти к следующему кадру, сделайте левый, чтобы вернуться к предыдущему кадру. Жест вверх переводит вас в последний кадр, жест вниз — в первый кадр.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
/**
* Method to rid duplicates and possible mistaken gestures
* @param minDuplicates Repetition to be considered high repetition
* @return Vector array w/o duplicates and possible mistaken gestures
*/
private function dropDuplicates (inputArray:Vector.<int>, minDuplicates:int = 1):Vector.<int> {
//Append end of line indicator to integer array
var xDuplicates:Vector.<int> = inputArray.slice(0,inputArray.length);
xDuplicates.push( -1);
//Initiate variables
var count:int = 1;
var keepIndex:int = 0;
//Ripping duplicates and invalid moves
for (var i:int = 0; i < xDuplicates.length — 1; i++) {
if (xDuplicates[i] == xDuplicates[i + 1]) count ++
else{
if (count > minDuplicates) {
xDuplicates.splice(keepIndex, count — 1)
keepIndex++
i = keepIndex
count = 1
}
else xDuplicates.splice(keepIndex, minDuplicates)
i—
}
}
xDuplicates.splice(xDuplicates.length — 1, 1);
return xDuplicates;
}
|
Шаг 13: Проверьте на совпадение
После обрезки gesCurrent
наша следующая задача — найти правильное соответствие в наборе предопределенных последовательностей жестов. Мы можем проверить соответствие между текущим жестом и членом предопределенных жестов, используя checkMatch
. Введите индекс gestSeq
вы хотите проверить соответствие с gestCurrent
. Возвращаемое значение -1
указывает на отсутствие совпадения; в противном случае индекс gestSeq
совпадает.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/**
* Method to check if gestCurrent matches with gestSeq
* @param index Member of gestSeq
* @return Match (1) or fail (-1)
*/
private function checkMatch(index:int):int {
//Pessimistic search, assumes not found initially
var matched:int = -1;
//clean input first
var cleaned:Vector.<int> = dropStrokes(gestCurrent, strokes[index]);
cleaned = dropDuplicates(cleaned);trace(cleaned,» xduplicates»)
//Check only those of same length
if (cleaned.length == gestSeq[index].length) {
var counter:int = cleaned.length;
//Scroll through each integer of selected predefined gesture
for (var member:int = 0; member < cleaned.length; member++)
{
if (cleaned[member] == gestSeq[index][member]) counter—;
else break;
}
//if all integers matched, current index of gestSeq matches gestCurrent
if (counter == 0 ) matched = index;
}
return matched;
}
|
Шаг 14: Выведите результат
Наконец, мы можем вывести результат. Результат будет зависеть от того, _order
или нет. Мы проверяем совпадение между gestCurrent
и текущим gestSeq
только если _order
включен, в противном случае нам нужно будет найти совпадение с любым членом в gestSeq
.
Независимо от результата поиска, нам нужно вывести результат. Результат поиска будет -1
если не найдено ни одного совпадения; если есть совпадение, будет возвращен массив индекса штриховки в сочетании с его именем. Результат равен -2
если gestSeq
пуст.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
/**
* Method to evaluate gesture according to preferred approach (in sequential order or not)
* @return No predefined gestures (-2), No match of gesture (-1), array with index of match and its description
*/
public function end():Array {
//Pessimistic search, -2 indicates that there are no predefined gesture
var result:Array = [ -2, «No predefined gestures»];
//If there are predefined gestures
if (gestSeq.length > 0) {
//Find the match in order
if (_order) {
if (checkMatch(orderCurrent) > -1) {
result = [orderCurrent, gestName[orderCurrent]];
orderCurrent++;
}
}
//Find a match in no specific order
else if (!_order) {
//Scroll through all gestures
for (var i:int = 0; i < gestSeq.length; i++)
{
if (checkMatch(i) > -1) {
result = [i, gestName[i]];
}
}
}
//Handle when cant find
else {
result = [ -1, «Gesture is invalid.»]
}
}
return result
}
|
Шаг 15. Подготовка графических ресурсов во Flash
С шагом 14 мы завершили GManager
. Нам нужно создать графические активы дальше. Я создал графические ресурсы во Flash Pro и экспортирую их в формате .swc. Нажмите Ctrl + Shift + F12, чтобы открыть окно «Параметры публикации» во Flash, и установите флажок «Экспорт SWC» на вкладке «Flash». Я сделал свои ресурсы доступными для скачивания, но не стесняйтесь разрабатывать свои собственные.
Шаг 16. Импорт активов в FlashDevelop
Поместите ваш SWC в папку lib
FlashDevelop, как показано выше. Создайте класс с именем «Main2» во FlashDevelop и создайте экземпляр мувиклипа, который содержит графику. Я выделил соответствующий ActionScript ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
private var ges:MGesture;
private var gesMan:GManager;
private var a:Arrows;
private var seq:Boolean = false;
public function Main2()
{
//Importing graphics in form of movieclip
a = new Arrows();
a.stop();
ax = 50;
ay = 50;
addChild(a);
|
Шаг 17: зарегистрируйте соответствующие жесты
Мы зарегистрируем наши последовательности жестов в порядке расположения графики в опубликованном мувиклипе. Мы делаем это, чтобы упростить прокрутку кадров после достижения результата. Обратите внимание, что я выделил строку 33 реализации; Вы можете раскомментировать это в моем исходном файле, чтобы увидеть, что сопоставление сделано по порядку.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
public function Main2()
{
//Importing graphics in form of movieclip
a = new Arrows();
a.stop();
ax = 50;
ay = 50;
addChild(a);
//initiating MGesture to allow singular gesture detection
ges = new MGesture(stage);
//initiating GManager to allow sequential gesture detection
gesMan = new GManager();
//this.seq = true;
//Detecting gestures in sequence
if (seq) {
a.gotoAndStop(2);
gesMan.useSeq = true;
}
//Register gesture sequences in line with frames
//Register gesture sequences in line with frames
gesMan.register(new < int > [2, 1], «2 Stroke Down, CCW»);
gesMan.register(new < int > [1, 0], «2 Stroke Right, CCW»);
gesMan.register(new < int > [0, 3], «2 Stroke Up, CCW»);
gesMan.register(new < int > [3, 2], «2 Stroke Left, CCW»);
gesMan.register(new < int > [3, 2, 1], «3 Stroke Down, CCW»);
gesMan.register(new < int > [2, 1, 0], «3 Stroke Right, CCW»);
gesMan.register(new < int > [1, 0, 3], «3 Stroke Up, CCW»);
gesMan.register(new < int > [0, 3, 2], «3 Stroke Left, CCW»);
gesMan.register(new < int > [3, 0], «2 Stroke Right, CW»);
gesMan.register(new < int > [0, 1], «2 Stroke Down, CW»);
gesMan.register(new < int > [1, 2], «2 Stroke Left, CW»);
gesMan.register(new < int > [2, 3], «2 Stroke Up, CW»);
gesMan.register(new < int > [2, 3, 0], «3 Stroke Right, CW»);
gesMan.register(new < int > [3, 0, 1], «3 Stroke Down, CW»);
gesMan.register(new < int > [0, 1, 2], «3 Stroke Left, CW»);
gesMan.register(new < int > [1, 2, 3], «3 Stroke Up, CW»);
gesMan.register(new < int > [0, 3, 0, 3, 0],»Step Up»);
gesMan.register(new < int > [0, 1, 0, 1, 0], «Step Down»);
gesMan.register(new < int > [4], «South East»);
gesMan.register(new < int > [5], «South West»);
gesMan.register(new < int > [6], «North West»);
gesMan.register(new < int > [7], «North East»);
gesMan.register(new < int > [4, 5, 4, 5], «Zigzag»);
stage.addEventListener(MouseEvent.MOUSE_DOWN, start);
stage.addEventListener(MouseEvent.MOUSE_UP, end);
}
|
Шаг 18: Обработка результатов
Запись последовательности жестов осуществляется по тому же CheckOut2
что и CheckOut2
. Я предполагаю, что читатели прошли Шаг 10. Итак, наконец, мы выведем результат. После успешного совпадения (> -1) мы перейдем к соответствующему кадру. Опять же, это зависит от того, включили ли вы использование последовательности путем включения seq
или нет.
01
02
03
04
05
06
07
08
09
10
11
|
private function end(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, check);
//evaluate gesture and output result
var output:Array = gesMan.end()
if (output[0] > -1) {
if (seq)a.nextFrame();
else a.gotoAndStop(output[0] + 2);
}
}
|
Шаг 19: опубликуйте свой проект
Нажмите Ctrl + Enter, чтобы опубликовать свой проект. Жест с помощью мыши и что было обнаружено. Я включил оба проекта (один запрашивает у вас соответствие данному жесту; другой отображает нарисованный вами жест, если он соответствует тому, который он знает). Веселиться.
Шаг 20: Разработка приложений
Неточности в пользовательских жестах распространены, и любое приложение, которое включает жесты, должно учитывать это. В следующей части этого руководства я MGesture
использовать MGesture
и GManager
для разработки приложения распознавания алфавита. Я укажу несколько деталей для соответствующей настройки, чтобы улучшить распознавание жестов.
Вывод
Теперь вам должно быть легко разрабатывать приложения, которые обнаруживают последовательности жестов любой комбинации. Надеюсь, этот урок помог вам в некоторых отношениях. Дайте мне знать о ваших комментариях, запросах и найденных ошибках. Спасибо.