На главную

Библиотека Интернет Индустрии I2R.ru

Rambler's Top100

Малобюджетные сайты...

Продвижение веб-сайта...

Контент и авторское право...

Забобрить эту страницу! Забобрить! Блог Библиотека Сайтостроительства на toodoo
  Поиск:   
Рассылки для занятых...»
I2R » И2Р Программы » Программирование » Общее в программировании

Графика Windows и API. Создание векторного редактора. Часть 2

Определение фигуры по щелчку мышки

Первым, что приходит в голову, это по координатам щелчка проверить попадает ли точка в прямоугольник фигуры и тем самым определить текущую фигуру. Это правило истинно, только для одной фигуры - прямоугольника. Посмотрим, что же неверно для других.

Окружность рисуется по координатам прямоугольника. Так вот, если бы мы делали проверку, через координаты прямоугольника, то в точке щелчка мышкой произошло попадание точки, а это неправильно.

Поэтому необходимо использовать специальный объект Windows - регион (region). Регион - это часть области, которую можно протестировать на наличие попадания точки, залить определенным цветом и т.д. Вся сущность региона в том, что он может иметь любую конфигурацию, что сильно облегчает обработку данных.

Регион создается API функцией CreateEllipticRgn (для окружностей, существуют различные функции создания регионов), проверка попадания точки в регион функцией PtInRegion. Вообще вы можете создать несколько регионов различной конфигурации, а затем скомбинировать их в область, как показано на рисунке выше.

Перемещение фигуры

Определив, что точка находится в нужной области, выполняем её перемещение, путем изменения координат фигуры и перерисовке всей поверхности холста.

Изменение размеров фигуры

Для изменения размеров фигуры используем понятие маркеров знакомых вам по дизайнеру Visual Basic, Corel Draw и т.п. программ. После того как определено, что щелчок пришелся по нужной фигуре, необходимо по углам и серединам сторон отрисовать маркеры (размеры маркеров в программе выбраны произвольно, для более профессионального уровня необходимо определить разрешение экрана и в соответствии с ним выбрать размер маркера, т.к. при разрешении 1024X768 маркер размером в 2 точки будет слишком неудобным).

Затем при наведении указателя "мыши" меняем его вид на соответствующий (диагональный, верх-низ и т.д.). Если пользователь щелкнул по маркеру, определяем его номер, чтобы корректно произвести изменение размеров в нужном направлении.

При событии перемещения "мышки" изменяем размеры фигуры и перерисовываем поверхность холста.

Использованные API функции

В программе использованы новые API функции. Полную декларацию описания функций смотрите в исходном тексте. Где пропущено описание типа подразумевается тип Long.

PtInRegion(hRGN,X,Y) --- Проверят, попадает ли точка с координатами (X,Y) в регион с дескриптором hRGN.

EqualRect(lpRect1 As RECT, lpRect2 As RECT) --- Проверяет равны ли координаты прямоугольников lpRect1 и lpRect2.

CreateRectRgn(X1,Y1, X2, Y2) --- Создает прямоугольный регион по координатам (X1,Y1,X2,Y2).

CreateEllipticRgn(X1,Y1,X2,Y2) --- Создает регион окружности по координатам (X1,Y1,X2,Y2).


Описание программы

В процедуру загрузки формы добавим следующий код (Form_Load):

...
' NEW Установим координаты плоскости виртуального окна
RectVW = RectForm
RectVW.Left = 0
RectVW.Top = 0
RectVW.Bottom = RectVW.Bottom - 10
RectVW.Right = RectVW.Right - 10
...
' NEW Установить начальный индекс
IndexFigure = SHAPE_RECT
CurrentFigure = SHAPE_RECT
...


Пояснение. Переменная RectVW содержит реальные координаты окна отображения, т.е. виртуального окна. Без этой переменной происходило усечение фигур (неточность в первой статье). IndexFigure содержит индекс выбранной фигуры с помощью мышки или команды меню.
На уровне модуля формы добавляем несколько переменных, их смысл будет ясен из дальнейшего объяснения.

' NEW Координаты точки в которой нажали мышку
' в событии MouseDown
Private PointDown As POINTAPI
' NEW Координаты плоскости виртуального окна
Private RectVW As RECT
' NEW Индекс найденной фигуры
Private IndexFigure As Integer
' NEW Координаты фигуры при перемещении
Private RectDown As RECT
' NEW Номер маркера с помощью которого
' производится изменение размеров
Private MarkerDown As Integer
' NEW Флаг изменения размеров фигуры
Private SizeFigure As Boolean
' NEW Флаг перемещения фигуры
Private MoveFigure As Boolean


Дополнительно применяются собственные функции:

ApiPoint преобразует координаты из переменных в структуру POINTAPI.
RectInRect проверяет вхождение одного прямоугольника во второй.
PtInRect проверяет, попадает ли точка в заданный прямоугольник.
PointConvert преобразует точку из координат формы в координаты виртуального окна.
NormalizeRect если верхние координаты больше нижних координат, обменять местами.

В форму добавлена новая процедура обработки события MouseDown (Form_MouseDown):

' NEW При нажатии мышки определить координаты
' для нахождения фигуры
Private Sub Form_MouseDown(:)
Dim i As Byte
Dim FindFigure As Integer
' Дескриптор региона
Dim hRGN As Long
' Проверить какая кнопка нажата
If Button <> 1 Then Exit Sub
' Инициализация
SizeFigure = False
MoveFigure = False
FindFigure = -1
' Занесем координаты в структуру
PointDown = PointConvert(CLng(X), CLng(Y))
' Проверяем, что точка в плоскости холста
If PtInRect(RectVW, PointDown) Then
' Проверяем попадание точки в одну из фигур
For i = LBound(Figures) To UBound(Figures)
' Создадим регион для проверки попадания точки
With Figures(i).Coord
If Figures(i).Shape = SHAPE_RECT Then
hRGN = CreateRectRgn(.Left, .Top, .Right, .Bottom)
Else
hRGN = CreateEllipticRgn(.Left, .Top, .Right, .Bottom)
End If
End With
' Проверим попадание в регион
If PtInRegion(hRGN, PointDown.X, PointDown.Y) Then
' Точка в нужной фигуре. Выставляем индекс.
IndexFigure = i
FindFigure = i
' Сохранить координаты фигуры
RectDown = Figures(i).Coord
End If
' Удалим дескриптор региона
DeleteObject hRGN
Next
' Если фигура найдена установим маркеры
Call mnuObjectItem_Click(IndexFigure)
' Проверить, что щелчок пришелся на маркер
MarkerDown = HitTest(IndexFigure, PointDown)
' Проверяем, что происходит изменение размера
' или перемещение
If MarkerDown <> -1 Then
' Режим изменения размеров
SizeFigure = True
Else
' Режим перемещения фигуры
If FindFigure <> -1 Then MoveFigure = True
End If
' Конец определения фигур
End If
End Sub


Пояснение. После проверки того, что точка в плоскости холста в цикле перебираем фигуры (цикл для тех сделает более, чем две фигуры), на основе типа фигуры вызываем соответствующие функции создания регионов (CreateRectRgn, CreateEllipticRgn) по координатам фигуры. С помощью функции PtInRegion тестируем точку, если она в регионе, то сохраняем индекс фигуры (IndexFigure) и самое главное сохраняем координаты фигуры (RectDown), в которой щелкнули (это понадобится при перемещении фигуры). В конце цикла удаляем дескриптор региона. По завершении цикла с помощью функции HitTest проверяем, где произошел щелчок в фигуре на маркере или нет. Если в маркере - выставляем флаг изменения размера SizeFigure, иначе если есть выделенная фигура (FindFigure<>-1) выставляем флаг перемещения фигуры.

После события нажатия кнопки мышки обрабатываем событие перемещения MouseMove (Form_MouseMove):

' NEW Перемещение мышки
Private Sub Form_MouseMove(:)
' Координаты точки, которая перемещается
Dim PointMove As POINTAPI
' Координаты курсора мыши
Dim p As POINTAPI
' Временный прямоугольник фигуры
Dim r As RECT
' Преобразуем перемещаемую точку в координаты холста
PointMove = PointConvert(CLng(X), CLng(Y))
If PtInRect(RectVW, PointMove) Then
' Проверка, на какую кнопку нажали
If (Button = 1) Then
If SizeFigure Then
Call MoveMarkerTo(IndexFigure, MarkerDown, PointMove)
Else
If MoveFigure Then
' Изменяем координаты фигуры и проверяем, чтобы
' не вышли за пределы холста
r = RectDown
r.Left = r.Left + (PointMove.X - PointDown.X)
r.Top = r.Top + (PointMove.Y - PointDown.Y)
r.Right = r.Right + (PointMove.X - PointDown.X)
r.Bottom = r.Bottom + (PointMove.Y - PointDown.Y)
If RectInRect(RectVW, NormalizeRect(r)) Then
Figures(IndexFigure).Coord = r
End If
End If
' Обновление окна формы
Call InvalidateCanvas
End If
' Установить курсор на маркере
MousePointer = GetMarkerCursor(IndexFigure, _
HitTest(IndexFigure, PointMove))
End If
End Sub


Пояснение. Проверяем флаги выставленные в MouseDown. Если изменение размеров - вызываем процедуру MoveMarkerTo, если перемещение, тогда сохраняем координаты из RectDown во временной переменной и изменяем размеры на разницу между координатами точки, где нажали кнопку (PointDown) и координатами точки, куда переместили указатель мышки (PointMove). Если измененные координаты фигуры находятся в области поверхности холста (RectInRect(RectVW, NormalizeRect(r))) тогда сохраняем их в структуре фигуры и перерисовываем область. В конце устанавливаем курсор маркера с помощью GetMarkerCursor. И в конце, когда пользователь отпустил кнопку обрабатываем событие MouseUp (From_MouseUp): ' NEW При отпускании мышки сбрасывание параметров Private Sub Form_MouseUp(:) ' Сбросить параметры SizeFigure = False MoveFigure = False Figures(IndexFigure).Coord = NormalizeRect(Figures(IndexFigure).Coord) End Sub Пояснение. Сбрасываем все флаги и производим нормализацию координат, если пользователь их изменял (можно оптимизировать ?). Измените процедуру CreateVirtualWindow, где была неточность:

...
MBM = CreateCompatibleBitmap(CDC, RectVW.Right, RectVW.Bottom)
...
PatBlt MDC, 0, 0, RectVW.Right, RectVW.Bottom, WHITENESS
...


В процедуре InvalidateCanvas добавьте строчку, выводящую маркеры для выделенной фигуры:

' NEW Вывести маркеры для выделенной фигуры
Call DrawTracker(IndexFigure, MDC, True)


А теперь перейдем к описанию процедур и функций отвечающих за работу с маркерами.

[Название подпрограммы] --- [Назначение]
GetMarker --- По индексу маркера получает координаты маркера POINTAPI
GetMarkerCount --- Количество маркеров у фигуры (у нас всегда 8)
GetMarkerCursor --- Получает курсор для определенного маркера
GetMarkerRect --- Получает по номеру маркера его координаты в виде RECT
MoveMarkerTo --- Перемещает маркер в нужную позицию
DrawTracker --- Отрисовывает маркеры фигуры
HitTest --- Проверяет где находится точка в пределах фигуры

В принципе смысл подпрограмм вы поймете из исходных текстов. Отдельного внимания заслуживают только DrawTracker и HitTest.

С помощью DrawTracker отрисовываются маркеры:

' NEW Рисует маркеры на фигуре
' HDC - дескриптор контекста на котором происходит рисование
' State - статус фигуры, при истине у фигуры отрисовываются маркеры
Sub DrawTracker(IndexFigure As Integer, _
HDC As Long, State As Boolean)
Dim i As Integer
Dim p As POINTAPI
Dim sm As Integer
sm = SIZE_MARKER + SIZE_MARKER
If State Then
' В цикле пройтись по координатам всех маркеров
For i = 1 To GetMarkerCount
' Получить координаты маркера
p = GetMarker(IndexFigure, i)
' С помощью функции API шаблонная заливка PatBlt
' заполняем маркер по размеру
' инверсным цветом на котором рисуется маркер
PatBlt HDC, p.X - SIZE_MARKER, _
p.Y - SIZE_MARKER, sm, sm, DSTINVERT
Next
End If
End Sub


Пояснение. В переменную (sm) устанавливаем полный размер маркера, в цикле проходимся по всем углам и серединам сторон фигуры, получаем по номеру маркера (переменная i цикла) координаты точки маркера и с помощью функции PatBlt заполняем области маркеров инверсным цветом. Вся прелесть функции PatBlt с параметром DSTINVERT, что она инвертирует цвета и поэтому при черном маркере на черном фоне, вы получите белый маркер. Таким образом мы никогда не потеряем маркер на фоне поверхности.

Функция HitTest определяет положение курсора мышки в пределах фигуры:

' NEW Определение, что курсор находится в маркере
' В случае успеха вернет номер маркера, иначе (-1)
Function HitTest(IndexFigure As Integer, _
p As POINTAPI) As Integer
Dim i As Integer
Dim r As RECT
' Установим начальное значение
HitTest = -1
' Цикл по всем маркерам
For i = 1 To GetMarkerCount
' Получить координаты маркера в структуре
r = GetMarkerRect(IndexFigure, i)
' Проверить попадает ли точка в
' координаты маркера
If PtInRect(r, p) Then
' Если да, то установить номер маркера
' и прервать цикл
HitTest = i
Exit For
End If
Next
End Function


Пояснение. Передвигаясь по всем маркерам фигуры получаем координаты маркера в переменную ( r ) используя функцию GetMarkerRect. Если указатель мышки находится в маркере (PtInRect(r,p)), тогда выставляем индекс маркера, иначе (-1), т.е. указатель в фигуре.

Последние замечания

Вот мы и закончили разбираться с API и графикой. Рекомендую вам изучить раздел в MSDN Graphics SDK, также изучать исходные тексты и книги на Си и Паскале.

В программе специально не производилась оптимизация, чтобы читателю было яснее разобраться и не запутаться в изменениях строк кода и переменных. Для тех, кто решит создавать собственный векторный редактор хочу посоветовать следующее:
  • используйте контекстные меню
  • восстанавливайте свойства фигур (например выбрав фигуру установить маркер в списке шаблонов и толщине линий)
  • выключайте курсор на плоскости холста и рисуйте свой
  • использовать перенос фигур на задний и передний план и т.д.

Все эти возможности реализуются довольно легко, главное хорошо продумать алгоритм вашей программы.

Заключение

Более элегантным способом построение похожих программ является использование классов, но версия VB 6.0 обладает усеченными возможностями объектно-ориентированного программирования (например, отсутствует наследование).

Этой статьей мы показали, что серьезные вещи можно писать не только на VC++ (Borland C) или Pascal (Delphi), но и на "простом" Basic (VB). Последняя статья цикла, посвященная разработке справочных систем, в принципе не связана с графикой, но также использует API функции справочной системы.

К статье прилагается пример приложения.

Андрей Зубарев
НОКСТР

Другие разделы
C, C++
Java
PHP
VBasic, VBS
Delphi и Pascal
Новое в разделе
Базы данных
Общие вопросы
Теория программирования и алгоритмы
JavaScript и DHTML
Perl
Python
Active Server Pages
Программирование под Windows
I2R-Журналы
I2R Business
I2R Web Creation
I2R Computer
рассылки библиотеки +
И2Р Программы
Всё о Windows
Программирование
Софт
Мир Linux
Галерея Попова
Каталог I2R
Партнеры
Amicus Studio
NunDesign
Горящие путевки, идеи путешествийMegaTIS.Ru

2000-2008 г.   
Все авторские права соблюдены.
Rambler's Top100