Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Visual1.doc
Скачиваний:
8
Добавлен:
07.03.2016
Размер:
4.35 Mб
Скачать

5.15. Користувацький режим роботи з графікою на прикладі малювання годинника Clock

Приклад знаходиться у папці DISK\GDI\GDI13.

До цих пір задавання координат відбувалося відносно лівого верхнього кута екрану (screen coordinates) або лівого верхнього кута клієнтської області вікна (client coordinates). В принципі, для всього, що стосується вікон в цілому та віконних повідомлень, наприклад WM_MOVE, WM_MOUSEMOVE, це правильно. Проте не завжди зручно малювати лінію “по точках”, коли, по-перше, все перевернуто зверху вниз – координати по вертикалі ростуть вниз, по-друге, доводитися постійно піклуватися про масштаб, і, по-третє, треба обчислювати координат точок при повороті і зсуві, враховуючи недоліки такої системи координат.

Система Windows забезпечує програміста можливістю створення власної користувацької системи координат. Для цього можна маніпулюючи різними параметрами, задавати напрям і масштаб осей, а також початок координат.

Налаштування режимів відображення

Функції цієї групи встановлюють і настроюють систему координат, яка використовується всіма функціями виведення, – координати виведення задаються саме в логічних одиницях. Окрім одиниць по осях OX та OY, функції цієї групи задають напрям осей і початок координат. Практично всі функції групи реалізовані парами. Функції з префіксом Get дозволяють набути поточного значення параметра, а з префіксом Set встановлюють нове значення і повертають попереднє. За умовчанням початок координат знаходиться в лівому верхньому куті області виведення, вісь OX направлена зліва направо, а вісь OY зверху вниз. Одиниці вимірювання – один піксель (режим MM_TEXT). Тому на різних пристроях, унаслідок відмінності в розмірах пікселя, фактичний (візуальний) розмір по осі OX може не відповідати розміру по осі OY.

Для налаштування системи координат Windows використовує два поняття – фізична область виведення (viewport), координати і розміри якої задаються у фізичних одиницях – пікселях, і логічна область виведення (window), координати і розміри якої задаються в логічних одиницях, які визначаються режимом малювання.

Тепер давайте познайомимося з режимами відображення, які підтримує Windows. Всього їх вісім, і основна функція, яка дозволяє вибрати режим, а значить і систему координат, – SetMapMode:

virtual int CDC::SetMapMode(int nMapMode).

Ця функція встановлює напрями осей і визначає логічні одиниці, тобто одиниці вимірювання для заданої системи координат.

Можливі режими задаються параметром nMapMode, який може набувати наступних значень (табл. 5.7.):

Таблиця 5.7

Режими відображення графіки

Режим

Опис

MM_TEXT

при цьому режимі одна логічна одиниця дорівнює одному пікселю, вісь x направлена праворуч, вісь у направлена вниз. Цей режим заданий за умовчанням

MM_HIENGLISH

при цьому режимі одна логічна одиниця рівна 0.001 дюйма, вісь x направлена праворуч, вісь у направлена вгору

MM_HIMETRIC

при цьому режимі одна логічна одиниця рівна 0.01 міліметра, вісь x направлена праворуч, вісь у направлена вгору

MM_LOENGLISH

при цьому режимі одна логічна одиниця рівна 0.01 дюйма, вісь x направлена праворуч, вісь у направлена вгору

MM_LOMETRIC

при цьому режимі одна логічна одиниця рівна 0.1 міліметра, вісь x направлена праворуч, вісь у направлена вгору

MM_TWIPS

при цьому режимі одна логічна одиниця - твіп (twips) – рівна 1/20 точки (point) або 1/1440 дюйма, вісь x направлена праворуч, вісь у направлена вгору

MM_ANISOTROPIC

режим дозволяє настроювати, за допомогою функцій SetWindowExt та SetViewportExt, розмірність (окремо для кожної з осей), їх напрями і початок відліку

MM_ISOTROPIC

режим дозволяє настроювати, за допомогою функцій SetWindowExt та SetViewportExt, розмірність осей, їх напрями і початок відліку, проте одна одиниця по осі x дорівнює одній одиниці по осі у, таким чином, при необхідності GDI налаштовує розмірність по осях, забезпечуючи співвідношення 1:1

Відмітимо, що тільки режими MM_HIENGLISH, MM_HIMETRIC, MM_LOENGLISH, MM_LOMETRIC та MM_TWIPS забезпечують додатки фізично зрозумілими одиницями вимірювання з одного боку, а з іншого – певну незалежність від пристроїв виведення.

Для перших шести режимів можна змінювати тільки початок координат, який за умовчанням відповідає лівому верхньому куту області виведення. Для режимів MM_ANISOTROPIC та MM_ISOTROPIC додатково можна настроювати напрям осей і масштаб. Для цього клас CDC має в своєму складі наступні функції.

Для налаштування системи координат Windows використовує два поняття – фізична область виведення (viewport), координати і розміри якої задаються у фізичних одиницях – пікселях, і логічна область виведення (window), координати і розміри якої задаються в логічних одиницях, які визначаються режимом малювання.

І фізична і логічна область виведення характеризуються точкою, що визначає початок координат. Для налаштування початку координат фізичної області виведення використовуються наступні функції.

virtual CPoint CDC::SetViewportOrg (int x, int y)

або

virtual CPoint CDC::SetViewportOrg (POINT point).

Для налаштування початку координат логічної області виведення використовуються наступні функції.

CPoint CDC::SetWindowOrg (int x, int y)

або

CPoint CDC::SetWindowOrg (POINT point).

Всі чотири функції як параметри набувають нових значень для установки початку координат, а повертають попередні значення. Причому, якщо для фізичної області виведення указуються координати, які надалі будуть початком координат, то для логічної області виведення указуються логічні координати, які надалі відповідатимуть лівому верхньому куту логічної області виведення.

Окрім початку координат кожна з областей виведення характеризується ще розмірами (extents), які використовуються тільки для визначення коефіцієнта масштабування. Коефіцієнт для кожної з осей знаходитися як відношення відповідних розмірів областей виведення. Проте для більшості режимів коефіцієнт масштабування змінити неможливо.

Для налаштування розмірів фізичної області виведення використовуються функції:

virtual CSize CDC::SetViewportExt (int cx, int cy)

або

virtual CSize CDC::SetViewportExt (SIZE size).

Для налаштування розмірів логічної області виведення використовуються функції:

virtual CSize CDC::SetWindowExt (int cx, int cy)

або

virtual CSize CDC::SetWindowExt(SIZE size).

Всі чотири функції як параметри отримують нові значення-розміри по осях. Ці значення використовуються для перетворення логічних координат у фізичні. Крім того, налаштування розмірів для фізичного вікна виведення дозволяє управляти напрямом осей. Оскільки після вибору одного з режимів вісь OX направлена зліва направо, а вісь OY – зверху вниз, то для зміни напряму, наприклад, осі OY, розмір має бути заданий з від’ємним значенням. Всі функції повертають попередні значення, причому вони можуть відрізнятися від тих, що були фактично задані відповідними функціями, оскільки Windows налаштовує їх значення залежно від конкретних можливостей або типу пристрою.

Наведемо приклад малювання годинника у програмі Clock з використанням користувацької системи координат.

Спочатку до класу CClockView додамо власні змінні та функції. Опишемо призначення кожної з них.

class CClockView : public CView

{

………………………………………………………..

public:

COleDateTime Time;

void SetMode(CDC *pDC);

void DrawDots(CDC *pDC);

void DrawHourHand(CDC *pDC);

void DrawMinuteHand(CDC *pDC);

void DrawSecondHand(CDC *pDC);

void RotatePoint(POINT pt[], int nNum, double nAngle);

………………………………………………………..

}

Об’єкт Time класу COleDateTime необхідний для отримання поточного часу. Призначення функцій:

  1. void SetMode(CDC *pDC) – функція установки режиму користувацьких координат в діапазоні від -1000 до 1000.

  2. void DrawDots(CDC *pDC) – функція виведення циферблату годинника на екран.

  3. void DrawHourHand(CDC *pDC) – функція виведення годинникової стрілки.

  4. void DrawMinuteHand(CDC *pDC) – функція виведення хвилинної стрілки.

  5. void DrawSecondHand(CDC *pDC) – функція виведення секундної стрілки.

  6. void CClockView::RotatePoint(POINT pt[], int nNum, double nAngle) – функція повертання масиву точок pt на заданий кут nAngle.

Крім власних функцій використовуються конструктор класу CClockView, функції PreCreateWindow та OnCreate, функція малювання OnDraw та функція відгуку на таймер OnTimer.

Розпишемо кожну з цих функцій більш детально.

На початку спрацьовує конструктор вікна у якому за допомогою функції GetCurrentTime повертається поточний час.

CClockView::CClockView()

{

//Отримання поточного часу

Time = COleDateTime::GetCurrentTime();

}

Потім спрацьовує функція PreCreateWindow у якій встановимо необхідні атрибути вікна та чорний колір фону вікна.

BOOL CClockView::PreCreateWindow(CREATESTRUCT& cs)

{

if (!CView::PreCreateWindow(cs))

return FALSE;

cs.dwExStyle |= WS_EX_CLIENTEDGE;

cs.style &= ~WS_BORDER;

cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|

CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL,

IDC_ARROW), HBRUSH(COLOR_WINDOW+2), NULL);

return TRUE;

}

Далі викликається функція створення вікна OnCreate, де ми встановлюємо таймер, використовуючи функцію SetTimer.

int CClockView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CView::OnCreate(lpCreateStruct) == -1)

return -1;

SetTimer(1,1000,0);

return 0;

}

Таймер встановлено на 1 секунду – 1000 мілісекунд.

Після створення вікна спрацьовує функція OnDraw де безпосередньо відбувається виведення годинника.

void CClockView::OnDraw(CDC* pDC)

{

CClockDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

SetMode(pDC);

DrawDots(pDC);

DrawHourHand(pDC);

DrawMinuteHand(pDC);

DrawSecondHand(pDC);

}

У функції OnDraw відбувається виклик 5-ти функцій, призначення яких описувалося вище: SetMode, DrawDots, DrawHourHand, DrawMinuteHand, DrawSecondHand. Опишемо кожну з цих функцій.

У функції SetMode встановлюється користувацька система координат з початковою точкою у центрі екрану та осями OX з координатами від -1000 до 1000; OY з координатами від -1000 до 1000.

//Функція установки режиму користувацьких координат

//(-1000,1000)

void CClockView::SetMode(CDC *pDC)

{

CRect rect;

GetClientRect(&rect);

//Завдання режиму з однаковим масштабом

pDC->SetMapMode(MM_ISOTROPIC);

//Завдання логічних координат

pDC->SetWindowExt(1000, 1000);

//та відповідність їм фізичних координат

pDC->SetViewportExt(rect.Width()/2, -rect.Height()/2);

//Завдання початку відліку

pDC->SetViewportOrg(rect.Width()/2, rect.Height()/2);

}

Таким чином початок відліку буде посередині клієнтської області вікна, вісь OX буде спрямована зліва направо, OY – знизу вверх.

Далі за допомогою функції DrawDots виведемо циферблат годинника.

//Виведення циферблату годинника

void CClockView::DrawDots(CDC *pDC)

{

int i,iSize,nkx,nky;

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

{

iSize = i % 5 == 0 ? 100 : 30;

nkx = (int)(900*cos(6*i*M_PI/180) - iSize / 2);

nky = (int)(900*sin(6*i*M_PI/180) - iSize / 2);

pDC->Ellipse(nkx, nky, nkx + iSize, nky + iSize);

}

}

У даному фрагменті малюємо 60 кругів циферблату, які будуть характеризувати часи, хвилини та секунди. Причому 12 кругів будуть мати радіус 100, інші 48 – радіус 30. Малювання циферблату відбувається по колу, радіус якого 900 одиниць через кожні 6 градусів.

На наступному етапі треба намалювати три стрілки – годинникову, хвилинну та секундну, які залежать від поточного часу.

Приведемо функцію малювання годинникової стрілки DrawHourHand.

//Виведення годинникової стрілки

void CClockView::DrawHourHand(CDC *pDC)

{

int Hour;

Hour = (Time.GetHour() > 12) ? Time.GetHour()-12:

Time.GetHour();

double ugol = 360./12*(Hour + Time.GetMinute() / 60.0);

CPen pen(PS_SOLID,1,RGB(255,255,255));

CPen *pen_st;

pen_st = pDC->SelectObject(&pen);

CPoint point[5]=

{

CPoint(0, -150),

CPoint(100,0),

CPoint(0,600),

CPoint(-100,0),

CPoint(0, -150)

};

RotatePoint(point,5,ugol);

pDC->Polyline(point,5);

pDC->SelectObject(pen_st);

}

Головним при малюванні годинникової, хвилинної, а також секундної стрілок є визначення кута під яким вона повинна бути повернута. Для розрахунку кута треба визначити кількість годин поточного дня за допомогою методу GetHour() класу COleDateTime. Якщо кількість годин більше 12 змінній Hour привласнюється значення годин мінус 12. Так як на циферблаті 12 годин – одній годині відповідає кут 360/12 або 30 градусів. Кут для годинникової стрілки розраховується з врахуванням хвилин.

Після того, як кут повороту годинникової стрілки відомий, використаємо функцію RotatePoint для повертання координат на заданий кут відносно початку координат. Приведемо текст функції RotatePoint.

//Функція повертання масиву точок на заданий кут

void CClockView::RotatePoint(POINT pt[], int nNum,

double nAngle)

{

POINT ptTemp;

// поворот відбувається відносно початку координат

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

{

ptTemp.x = (int)(pt[i].x * cos (M_PI * nAngle / 180) +

pt[i].y * sin (M_PI * nAngle / 180));

ptTemp.y = (int)(pt[i].y * cos (M_PI * nAngle / 180) -

pt[i].x * sin (M_PI * nAngle / 180));

pt[i] = ptTemp;

}

}

Таким чином годинникова стрілка стане на задане місце. Аналогічним чином відбувається виведення хвилинної та секундної стрілок. Код відповідних функцій наведено нижче.

//Виведення хвилинної стрілки

void CClockView::DrawMinuteHand(CDC *pDC)

{

double ugol = 360.0 / 60 * (Time.GetMinute() +

Time.GetSecond() / 60.0);

CPen pen(PS_SOLID,1,RGB(255,255,255));

CPen *pen_st;

pen_st = pDC->SelectObject(&pen);

CPoint point[5]=

{

CPoint(0, -200),

CPoint(50,0),

CPoint(0,800),

CPoint(-50,0),

CPoint(0, -200)

};

RotatePoint(point,5,ugol);

pDC->Polyline(point,5);

pDC->SelectObject(pen_st);

}

//Виведення секундної стрілки

void CClockView::DrawSecondHand(CDC *pDC)

{

SYSTEMTIME ms;

Time.GetAsSystemTime(ms);

double ugol = 360.0 / 60 * (Time.GetSecond() +

ms.wMilliseconds / 1000.0);

CPen pen(PS_SOLID,1,RGB(255,255,255));

CPen *pen_st;

pen_st = pDC->SelectObject(&pen);

pDC->MoveTo(0,0);

pDC->LineTo((int)(800*sin(ugol*M_PI/180)),

(int)(800*cos(ugol*M_PI/180)));

pen_st = pDC->SelectObject(&pen);

}

Хвилинна стрілка виводиться з врахуванням секунд, секундна – з врахуванням мілісекунд. Для обчислення кількості мілісекунд скористаємося функцією GetAsSystemTime класу COleDateTime.

Результат виконання програми Clock приведено на рис. 5.33.

Рис. 5.33. Результат виведення годинника у програмі Clock

Крім виведення годинника у користувацьких координатах інтерес являє функція OnTimer, у якій будемо перемальовувати тільки цікавлячи нас область вікна. У попередніх прикладах розглядався режим роботи з пікселями – SetROP2. У цьому прикладі продемонструємо альтернативний варіант – використання функції InvalidateRgn – для перемалювання заданої області. Приведемо текст функції OnTimer.

void CClockView::OnTimer(UINT nIDEvent)

{

//Отримання поточного часу

Time = COleDateTime::GetCurrentTime();

//Перемалювання круглої області усередині годинника

CRect rect;

CRgn rgn;

GetClientRect(&rect);

int r,r1,r2;

r1=rect.Width()/2;

r2=rect.Height()/2;

r = r1 < r2 ? r1 : r2;

rect.InflateRect((int)(r-r1),(int)(r-r2));

rect.InflateRect((int)(-0.09*rect.Width()),

(int)(-0.09*rect.Height()));

rgn.CreateEllipticRgn(rect.left,rect.top,rect.right,

rect.bottom);

InvalidateRgn(&rgn);

CView::OnTimer(nIDEvent);

}

У даній функції після визначення поточного часу за допомогою функції GetCurrentTime() відбувається формування кругової області. Для формування цієї області скористаємося функцією CreateEllipticRgn класу CRgn. З цією метою обчислимо мінімальний радіус вписаного кола та перетворимо прямокутник до квадрату. Потім стиснемо його таким чином, щоб задане коло було всередині циферблату.

Після того як кругова область для перемалювання сформована, викличемо функцію InvalidateRgn, яка перемалює лише область усередині циферблату.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]