|
Программирование >> Рекурсивные объекты и фрактальные узоры
Модный способ ввода команд Эта методика ввода команд стала довольно популярной в последнее время. Она используется, в частности, в таких разных программах, как игра Darwinia и браузер Maxthon. Суть методики заключается в следующем: вы зажимаете правую кнопку мыши и рисуете на экране какую-либо фигуру. Компьютер сравнивает её со списком имеющихся у него фигур и, б случае успеха, выполняет сопоставленную фигуре операцию. Например, жест вверх-вниз в Maxthon соответствует обновлению текущей страницы. В жесте важна не только фигура, но и процесс её рисования. Так, треугольник, заданный движением мыши по часовой стрелке, отличается от треугольника, начерченного в обратном направлении. Задача заключается в программировании ядра механизма, обрабатывающего жесты мыши. Система должна работать в двух режимах: запись и распознавание. В режиме записи пользователю предлагается начертить мышью фигуру и сохранить её в памяти под каким-либо именем. В режиме распоз- навания фигура, изображённая пользователем, сравнивается с содержимым памяти. Если рисунок определён, выводится соответствующее сообщение. Конечно, изобретать полноценный метод распознавания образов не требуется. Для анализа типичной фигуры будет вполне достаточно представить экран в виде сетки размером 3x3 и определить, по каким из девяти образовавшихся клеток проходят линии рисунка. Решение Решение этой задачи одинаково связано как с алгоритмическими вопросами, так и с разработкой приемлемого интерфейса пользователя. Создаваемое приложение будет состоять из главной формы, на которой располагаются две кнопки TButton, список записей TListBox и область рисования TPaintBox (рис. 9.7). Свойства элементов интерфейса перечислены в табл. 9.1. MainForm Пусто Пусто Пусто Пусто Пусто Пусто Пусто Пусто Пусто Пусто Запись Распознавание Рис. 9.7. Главная форма приложения Жесты мыши Для практического применения RLE потребуется также ограничить максимально допустимую длину последовательности точек одного цвета. Можно порекомендовать какое-нибудь число, предшествующее круглому , например, 15 (значения в промежутке 0-15 помещаются в четыре бита). Если в картинке встретится, скажем, двадцать подряд идущих белых пикселей, ситуацию придётся закодировать последовательностью 15 о 5 Полученная последовательность чисел записывается непосредственно в выходной файл. Конечно, описанная методика очень проста, и достичь высокой степени сжатия с её помоиЦ)Ю не удастся. Каждый пиксель исходной (несжатой) картинки кодируется одним битом, а каждый элемент сгенерированной алгоритмом RLE последовательности - четырьмя. Поэтому файл уменьшится в размере, лишь если в нём будут преобладать длинные строки из пикселей одного цвета. В качестве дополнительного задания можно запрофаммировать какой-нибудь алгоритм оптимального хранения чисел последовательности (например, метод Хаффмана или арифметическое кодирование). 9.2.3. Жесты мыши С++ мастер-класс. 85 нетривиальных проектов, решений и задач Таблица 9.1. Элементы управления приложения Жестымыши
Жестам мыши сопоставлены их текстовые описания: треугольник , квадрат , буква Z и т.п. Максимально возможное количество хранимых фигур равно десяти. В списке IsRecords содержится десять элементов, начальное значение каждго из которых равно строке пусто -. Щелчком по любой из кнопок ( Запись или Распознавание ) пользователь переходит в режим рисования. Зажав левую кнопку мыши, человек вычерчивает фигуру. Далее действия программы зависят от того, какой режим выбран. Если мы находимся в режиме записи, на экране появляется диалоговое окно с предложением ввести название только что вычерченного жеста, которое заносится в текущий элемент списка IsRecords. Если активирован режим распознавания, компьютер сравнит новую фигуру со всеми рисунками в памяти и выведет название распознанного жеста. Если новый жест не похож ни на один из записанных, компьютер выносит вердикт: не распознано . Начнем с алгоритмической части. В процессе записи нам ничего не остаётся делать, кроме как сохранять все вычерченные мышью точки без изменений. Результатом будет длинный список пар (X, Y) координат посещённых точек. Затем фигура покрывается сеткой размером 3x3 клетки (рис. 9.8). Заметьте, не вся область рисования, а только реально использованная её часть. Это небесспорное решение я принял исходя из своих представлений о том, как должна работать система распознавания жестов мыши. Мне кажется, что размер и расположение фигуры не имеют никакого значения. Маленькая буква Z, находящаяся в левом нижнем углу экрана должна распознаваться Рис 9.8. Наложение сетки на вычерченную фигуру так же, как и буква Z, занимающая всю область рисования. Вполне вероятно, что в иной задаче дела будут обстоять совсем по-другому. Каждая клетка задается парой координат в пределах от (О, 0) до (2, 2). Теперь запись движений мыши можно сжать , оставив в ней лишь информацию о посещенных клетках сетки. Здесь, однако, тоже есть своё затруднение. Посмотрите на клетки (О, 1) (первый столбец, вторая строка) и (2, 1) (третий столбец, вторая строка). Линии рисунка по этим клеткам проходят, но с тем же успехом они могли бы пройти чуть выше или чуть ниже. Понятно, что клетка, лишь немного зацепленная линией, не может считаться полноценной составляющей фигуры. Но что значит немного ? Я предлагаю следующий простой критерий. Если проведённая линия захватывает горизонтальный участок больше половины ширины клетки или вертикальный участок больше половины её высоты, клетка считается входящей в состав фигуры. Перейдем к разработке программы. Для начала потребуется описать несколько глобальных объектов: вектор записей (десять записей, каждая из которых представляет собой список координат (X, Y) отмеченных клеток сетки) vector<vector<pair<int, int> > > Records (10) ,- запись распознаваемой фигуры vector<pair<int, int> > Testlmage; указатель на текущую фигуру vector<pair<int, int> > *CurrentDrawing; нажата ли левая кнопка мыши bool IsMouseDown = false; сохранить текущее положение мыши CurrentDrawing->push back(make pair(X, Y)); Обработка события OnMouseMove сводится к добавлению очередной пары координат (X, Y) в состав записи рисунка: void fastcall TMainForm::DrawingAreaMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) если режим рисования активен i f(IsMous eDown) изобразить на экране отрезок от предыдущей точки записи до текущей (вывод фигуры в виде набора отдельных точек, а не отрезков смотрится менее симпатично) int OldX = CurrentDrawing->back().first; int OldY = CurrentDrawing->back().second; DrawingArea->Canvas->MoveTo(01dX, OldY); DrawingArea->Canvas->LineTo(X, Y) ; добавить (X, Y) в запись рисунка CurrentDrawing->push back(make pair(X, Y)); Основная часть алгоритма выполняется при отпускании левой кнопки мыши, то есть в обработчике события области рисования OnMouseUp. Рассмотрим сначала три небольпгах служебных функции. Функция GetBlockO возвращает координаты клетки сетки, соответствующей переданным координатам точки и набору координат углов рисунка: рисунок находится в прямоугольнике (MinX, MinY) - (МахХ, MaxY) Element - точка рисунка pair<int, int> GetBlock(pair<int, int> Element, int Minx, int MaxX, int MinY, int MaxY) return make pair(3*(Element.first - MinX) / (MaxX - MinX + 1), 3*(Element.second - MinY) / (MaxY - MinY + 1)); Предикаты LessX и LessY служат для сортировки пар координат по значению X и Y: возвращает true, если Х-составляющая Ihs меньше Х-составляющей rhs bool LessX(pair<int, int> Ihs, pair<int, int> rhs) { return Ihs.first < rhs.first; } возвращает true, если Y-составляющая lbs меньше Y-составляющей rhs bool LessY(pair<int, int> Ihs, pair<int, int> rhs) { return Ihs.second < rhs.second; } Код обработчика события OnMouseUp приведен ниже. режим работы прогрс1ммы enum { RECORD, RECOGNITION } Mode; В конструкторе формы текущим элементом списка устанавливается первый, а также увеличивается толщина линий рисунка, чтобы чертёж выглядел приятнее: fastcall TMainForm::TMainForm(TComponent* Owner) : TForm(Owner) lsRecords->ItemIndex = 0; DrawingArea->Canvas->Pen->Width = 5; Щелчок no кнопке Запись переводит программу в режим записи: void fastcall TMainForm:ibtnRecordClick(TObject *Sender) очистка экрана DrawingArea->Canvas->FillRect(Rect(О, 0, DrawingArea->Width, DrawingArea->Height)); запись будет производиться в элемент lsRecords->ItemIndex вектора Records CurrentDrawing = &Records[lsRecords->ItemIndex]; Mode = RECORD; Аналогично обработчик нажатия на кнопку Распознавание включает режим распознавания: void fastcall TMainForm::btnRecognitionClick(TObject *Sender) очистка экрана DrawingArea->Canvas->FillRect(Rect(О, О, DrawingArea->width, DrawingArea->Height)); запись будет производиться в объект TestImage CurrentDrawing = &TestImage; Mode = RECOGNITION; При нажатии на левую кнопку мыши выполняется функция-обработчик события OnMouseDown области рисования: void fastcall TMainForm::DrawingAreaMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) IsMouseDown = true; левая кнопка мыши нажата CurrentDrawing->clear () ; очистить содержимое записываемого рисунка
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |