Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ИГС / labor_03a.doc
Скачиваний:
26
Добавлен:
17.04.2018
Размер:
1.34 Mб
Скачать

Интерактивное изменение положения опорных точек сплайновых кривых с помощью мышки.

Современные графические системы предполагают наличие интерактивного взаимодействия с пользователем. Другими словами пользователь в диалоговом режиме может изменять создаваемое им графическое изображение.

Покажем на примере как можно в диалоговом режиме менять число и положение опорных точек для сплайновой кривой Безье порядка N. Кривую Безье будем строить по формулам (27). Кроме того, для разнообразия, будем строить плоскую кривую Безье.

Пусть код программы находится в файле line9.cpp. Покажем, как можно создать эту программу. Возьмем за основу программу из первой лабораторной работыmenuwin.cpp. Будем использовать меню, созданное в этой программе.

Создаем пустой проект line9 в каталогеline9. Перебрасываем в этот каталог файлыmenuwin.cpp,menuwin.rc,resource.rc. Переименовываем файл исходного кода и файл ресурсов в файлыline9.cpp,line9.rc. Подключаем эти два файла к проекту. Компилируем файл кода, компилируем файл ресурсов, создаем исполняемый файл. Запускаем программу и убеждаемся в правильной работе.

Теперь начнем вносить изменения в новую программу. Изменим имя класса окна и заголовок окна «Управление в интерактивном режиме сплайновыми кривыми Безье».

char cname[] = "Spline";

char title[] = " Interactive Control to Bezie Spline Curves";

Внесем изменения в названия пунктов меню. Откроем редактор меню. Выберем пункт Ellipse, названиеEllipseзаменим на General Bezier Spline, идентификаторID_ELLIPSEзаменим наID_GENERAL. Выберем пунктPolygon, названиеPolygonзаменим на Cubic Bezier Spline, идентификаторID_POLYGONзаменим наID_CUBIC.

В функции окна WndProcзаменим идентификаторыID_ELLIPSEиID_POLYGONна идентификаторыID_GENERALиID_CUBIC.

В файле resource.hнадо убрать строчки определения идентификаторовID_ELLIPSEиID_POLYGON.

При выборе пункта меню Cubic Bezier Splineдолжна быть нарисована кубическая кривая Безье. Поэтому в начале переименуем функциюPolygon_OnDCв функциюSplineDC. В новой функцииSplineDCдолжны рисоваться опорные точки, ломаная линия, связывающая эти точки и сплайновая кривая Безье произвольного порядкаN.

Порядок Nбудет глобальной переменной, которую обозначимNorder. Сделаем глобальной переменнойNpointsчисло опорных точек. Поэтому в начале программы появятся строчки кода.

//порядок кривой Безье

int Norder;

//число опорных точек

int Npoints;

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

case ID_CUBIC:

Norder = 3;

SplineDC(hWnd);

break;

Введем массив опорных точек, сделав его глобальной переменной.

//массив опорных точек

POINT2 V[21];

Для вычисления функций Безье bN,i(t) , определенных формулами (27) создадим следующие три функции.

//вычисляется факториал

double Fact(int N)

{

double p=1;

if(N != 0)

for(int n=1; n<=N; n++)

p *= n;

return p;

}

//вычисляется коэффициенты для функций Безье

inline double Cb(int N, int i)

{

return Fact(N)/Fact(i)/Fact(N-i);

}

// функции Безье

double Bezie(int N, int i, double t)

{

double b;

double p1 = 1, p2 = 1;

if(i != 0)

for(int in=0; in<i; in++)

p1 *= t;

if((N-i) != 0)

for(int in=0; in<(N-i); in++)

p2 *= 1-t;

b = Cb(N,i)*p1*p2;

return b;

}

Теперь используя функции Безье и опорные векторы, создадим функцию для вычисления точек на кривой Безье в соответствии с формулой (31).

//координаты точки сплайновой кривой

POINT2 splineBezie(int N, double t)

{

POINT2 S;

S.x = 0; S.y = 0;

for(int i=0; i<=N; i++)

{

double Bez = Bezie(N,i,t);

S.x += V[i].x*Bez;

S.y += V[i].y*Bez;

}

return S;

}

Обратимся к функции SplineDCи уберем в ней строчки кода связанные с рисованием полигона. Оставим только рисование координатных осей. Уберем из файла о функции Ellipse_OnDC. В функции окнаWndProcв строчки кода, связанные с выбором пункта меню рисования общей кривой Безье добавим вызов функцииSplineDC.

case ID_GENERAL:

SplineDC(hWnd);

break;

Теперь если откомпилировать программу и запустить на выполнение, то при выборе пунктов меню или будут рисоваться только координатные оси.

Заполним функцию SplineDCстрочками кода для рисования опорных точек, ломаной линии, связывающая эти точки и сплайновой кривой Безье произвольного порядкаN.

Во-первых, задавать опорные точки будем с помощью генератора случайных чисел, как показано в строчках следующего кода.

Npoints = Norder + 1;

//задаем опорные точки с помощью генератора случайных чисел

double kr = 0.75;

for(int i=0; i<Npoints; i++)

{

V[i].x = kr*(xLeft + rand()*(xRight - xLeft)/RAND_MAX);

V[i].y = kr*(yBottom + rand()*(yTop - yBottom)/RAND_MAX);

}

Во-вторых, рисуем ломаную линию по опорным точкам. Имеем следующий код.

//рисуем ломаную линию по опорным точкам

MoveToEx(hdc,xn(V[0].x),ym(V[0].y),0);

for( i=1; i<Npoints; i++)

LineTo(hdc,xn(V[i].x),ym(V[i].y));

В-третьих, рисуем кривую Безье. Имеем следующий код.

//рисуем кривую Безье

int Nt = 10*Norder;

double t, dt = 1.0/(Nt-1);

MoveToEx(hdc,xn(splineBezie(Norder,0).x),ym(splineBezie(Norder,0).y),0);

for(int p=1; p<Nt; p++)

{

t = dt*p;

LineTo(hdc,xn(splineBezie(Norder,t).x),ym(splineBezie(Norder,t).y));

}

В-четвертых, рисуем опорные точки. Имеем следующий код.

//рисуем опорные точки

for(i=0; i<Npoints; i++)

Ellipse(hdc, xn(V[i].x)-4, ym(V[i].y)-4, xn(V[i].x)+4, ym(V[i].y)+4);

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

В функции окна WndProcдобавим строчки кода, связанные с обработкой сообщений: левая клавиша нажата -WM_LBUTTONDOWN, левая клавиша опущена -WM_LBUTTONUP.

case WM_LBUTTONDOWN:

SplineLButtonDown();

break;

case WM_LBUTTONUP:

SplineLButtonUp();

break;

Сообщения при нажатии и отпускании левой клавиши мышки обрабатываются функциями SplineLButtonDown()иSplineLButtonUp(). Поэтому в начале программы надо поставить прототипы этих функций.

void SplineLButtonDown();

void SplineLButtonUp();

Затем создадим эти функции.

//обрабатывает сообщение WM_LBUTTONDOWN

void SplineLButtonDown()

{

flagUp = 0;

flagDown = 1;

}

//обрабатывает сообщение WM_LBUTTONUP

void SplineLButtonUp()

{

flagUp = 1;

flagDown = 0;

flagLock = 0;

}

В этих простых функциях устанавливаются соответствующие флаги. Флаг flagDown=0,1обозначает состояние - нажата или нет левая клавиша. Флаг flagUp=0,1обозначает состояние - отпущена или нет левая клавиша. ФлагflagLock=0,1обозначает состояние - захвачена или нет опорная точка.

Флаги сделаем глобальными переменными, и в начале программы напишем строчку.

//флаги

int flagDown, flagUp, flagLock;

Начальные значения флагов удобно установить в ответ на сообщение WM_CREATE, которое приходит в начале создания окна приложения. Поэтому в функции окнаWndProcдобавим строчки кода:

//сообщение при открытии окна

case WM_CREATE:

SplineCreate (hWnd); //выход из цикла сообщений

break;

Создадим также функцию SplineCreate(), где зададим начальные значения флагов.

//обрабатывает сообщение WM_CREATE

void SplineCreate(HWND hWnd)

{

flagDown = 0; flagUp = 0; flagLock = 0;

}

Теперь надо обработать сообщение о движении мышки и обработать координаты указателя мышки (x,y). Для этого в функции окнаWndProcдобавим строчки кода, связанные с обработкой сообщения о движении мышки -WM_MOUSEMOVE.

case WM_MOUSEMOVE:

x = LOWORD(lParam);

y = HIWORD(lParam);

SplineMouseMove(hWnd, x, y);

break;

Здесь функция SplineMouseMove()обрабатывает сообщение о движении мыши, и в качестве параметров принимает координаты указателя мыши. Алгоритм должен быть следующим. Если мышка двигается и нажата левая клавиша, если не зацеплена ни какая опорная точка, и если координаты указателя мышки (x,y) оказались в окрестности опорной точкиV[i], то эта точка должна быть зацепленной. Этой точке присваиваются координаты указателя мышки. Затем картина должна быть перерисована с новым расположением опорной точкиV[i]. Перерисовывание будет выполняться функциейLineDC().

Ниже приводим код функции SplineMouseMove().

void SplineMouseMove(HWND hwnd, int x, int y)

{

if(flagDown)

{

if(!flagLock)

for(int i=0; i<Npoints; i++)

if(abs(x-xn(Pt[i].x))+abs(y-ym(Pt[i].y))<10)

{

Nlock = i;

flagLock = 1;

}

if(flagLock)

{

V[Nlock].x = nx(x); V[Nlock].y = my(y);

LineDC(hwnd);

}

}

}

В этой функции встречается глобальная переменная Nlock, которая равна номеру зацепленной опорной точки. Поэтому добавим в начале программы строчку кода.

//номер зацепленной опорной точки

int Nlock;

Кроме того, в функции SplineMouseMove()вызываются новые функцииnx()иmy(), которые являются обратными к функциямxn()иym(). Функцииxn()иym()переводя мировые координаты в экранные координаты, а функцииnx()иmy()экранные координаты в мировые.

Ниже приводим код этих функций.

//переход от пикселя n к x

inline double nx(int n)

{

return (n - nLeft)*(xRight - xLeft)/(nRight - nLeft) + xLeft;

}

//переход от пикселя m к y

inline double my(int m)

{

return (m - mBottom)*(yTop - yBottom)/(mTop - mBottom) + yBottom;

}

В тот момент, когда зацепляется опорная точка V[i], и ей передаются координаты указателя мышки, вызывается функцияLineDC()для перерисовывания всей картины с новым положением опорной точкиV[i]. Рисовать новую картину будем в контексте памяти, и затем мгновенно выбрасывать на экран с помощью функции BitBlt().

Функция LineDC()и функцияSplineDC()имеют одинаковую структуру, отличие в том, что функцияSplineDC()рисует в контексте экран, а функцияLineDC()рисует в контексте памяти. Ниже приводим код функцииLineDC().

void LineDC(HWND hwnd)

{

//получаем контекст устройства <hdc> для окна <hwnd>

HDC hdc = GetDC(hwnd);

//создаем контекст памяти hdcMem, совместимый с контекстом экрана hdc

HDC hdcMem = CreateCompatibleDC(hdc);

//создаем (пустую) битовую карту совместимую с контекстом экрана

HBITMAP hBmp = CreateCompatibleBitmap(hdc, nRight, mBottom);

//выбираем битовую карту hBmp в контекст памяти hdcMem

HBITMAP hBmpOld = (HBITMAP)SelectObject(hdcMem, hBmp);

HPEN hpen0 = CreatePen(PS_SOLID,2,RGB(0xC0,0xC0,0xC0));

HPEN hpenOld = (HPEN)SelectObject(hdcMem,hpen0);

HBRUSH hbrush0 = CreateSolidBrush(RGB(0xC0,0xC0,0xC0));

HBRUSH hbrushOld = (HBRUSH)SelectObject(hdcMem,hbrush0);

Rectangle(hdcMem,0,0,nRight,mBottom);

HPEN hpen1 = CreatePen(PS_SOLID,2,RGB(0,255,255));

SelectObject(hdcMem,hpen1);

int nb, mb, ne, me;

//рисуем ось OX

nb = xn(xLeft); mb = ym(0);

MoveToEx(hdcMem, nb, mb, NULL);

ne = xn(xRight); me = ym(0);

LineTo(hdcMem,ne,me);

//рисуем ось OY

nb = xn(0); mb = ym(yBottom);

MoveToEx(hdcMem, nb, mb, NULL);

ne = xn(0); me = ym(yTop);

LineTo(hdcMem,ne,me);

HPEN hpen2 = CreatePen(PS_SOLID,2,RGB(255,255,255));

SelectObject(hdcMem,hpen2);

//рисуем ломаную линию по опорным точкам

MoveToEx(hdcMem,xn(V[0].x),ym(V[0].y),0);

for( int i=1; i<Npoints; i++)

LineTo(hdcMem,xn(V[i].x),ym(V[i].y));

HPEN hpen3 = CreatePen(PS_SOLID,2,RGB(0,255,0));

SelectObject(hdcMem,hpen3);

//рисуем кривую Безье

int Nt = 10*Norder;

double t, dt = 1.0/(Nt-1);

MoveToEx(hdcMem,xn(splineBezie(Norder,0).x),ym(splineBezie(Norder,0).y),0);

for(int p=1; p<Nt; p++)

{

t = dt*p;

LineTo(hdcMem,xn(splineBezie(Norder,t).x),ym(splineBezie(Norder,t).y));

}

HPEN hpen4 = CreatePen(PS_SOLID,1,RGB(0,255,255));

SelectObject(hdcMem,hpen4);

HBRUSH hbrush = CreateSolidBrush(RGB(255,0,0));

SelectObject(hdcMem,hbrush);

//рисуем опорные точки в виде кружков

for(i=0; i<Npoints; i++)

Ellipse(hdcMem, xn(V[i].x)-4, ym(V[i].y)-4, xn(V[i].x)+4, ym(V[i].y)+4);

//копируем контекст памяти в контекст экрана

BitBlt(hdc, 0, 0, nRight, mBottom, hdcMem, 0, 0, SRCCOPY);

SelectObject(hdcMem,hbrushOld);

DeleteObject(hbrush0);

DeleteObject(hbrush);

SelectObject(hdcMem,hpenOld);

DeleteObject(hpen0);

DeleteObject(hpen1);

DeleteObject(hpen2);

DeleteObject(hpen3);

DeleteObject(hpen4);

DeleteObject(hBmp);

DeleteDC(hdcMem);

ReleaseDC(hwnd, hdc);

}

Если теперь откомпилировать программу и запустить на выполнение файл line9.exe, то с помощью мышки можно перемещать опорные точки кубической сплайновой кривой Безье.

Соседние файлы в папке ИГС