Мета роботи - набути практичних навиків в складанні програм для побудови графіків функцій за допомогою засобів мов програмування Delphi, С++ або С#. 1. КОРОТКІ ТЕОРЕТИЧНІ ДАНІ Графіки, гістограми, діаграми тощо призначені для візуалізації (полегшення сприйняття людиною) числових результатів обробки інформації. При використанні комп’ютера в інженерних та наукових дослідженнях появі зображення на екрані монітора передують складні математичні розрахунки, які виконуються, як правило, в системі координат об’єкту дослідження. Така система координат називається реальною. Зображення, яке візуалізує на екрані результати розрахунків, формується в машинних координатах, характер яких визначається режимом роботи відеоадаптера. Тому для адекватного відображення числових результатів на екрані необхідно для кожної прикладної задачі розробити чіткий алгоритм приведення реальних координат об’єкту дослідження до машинних координат. Зокрема, визначити відображення центру реальних координат на екрані в машинних координатах та функціональні залежності для розрахунку машинних координат. / Рис. 1. Побудова графіка на екрані монітора При побудові графіків для приведення реальних координат до машинних в загальному випадку (рис. 1) використовують наступні вирази: ; (1) , (2) де Xмаш , Yмаш - поточні машинні координати точки в пікселях ; , - відображення центру реальних координат на екрані в машинних координатах ; Xреальн ,Yреальн - поточні реальні координати точки ; kX, kY - коефіцієнти перетворення . Коефіцієнти перетворення в загальному випадку розраховують за наступними співвідношеннями : ; (3) . (4) Отже, для побудови графіка довільної функції можна скористатися наступним алгоритмом: Протабулювати задану функцію на проміжку зміни аргументу з метою визначення максимального та мінімального значень функції. Визначити значення коефіцієнтів перетворення для стискання (у випадку, якщо максимальні значення функції або аргументу перевищують допустиму роздільну здатність монітора) або розширення діапазонів значень функції і аргументу для нормального візуального сприйняття заданої функції на екрані монітора. Наприклад, діапазон зміни значень функції синус від -1 до 1. Якщо не ввести коефіцієнт розтягу то на екрані монітора при побудові графіка функції одержимо пряму лінію. Домноживши значення Y на ky = 100, одержимо функцію синуса в звичному для нас вигляді. Побудувати графік функції, з’єднуючи за допомогою ліній (процедури LINE() або LINETO()) попередньо обраховані значення координат точки функції з наступними. Здійснити розмітку осей через певні проміжки табулювання для X та Y. Для розмітки осей користуються процедурами LINE() і LINETO() для зображення відміток на осях, а для підписування числових значень поблизу відміток - процедурами OUTTEXT() і OUTTEXTXY(). Процедура OUTTEXT() виводить текст за поточним положенням вказівника. Звертання OUTTEXT(<текст>), де <текст> - вираз типу string, що вказує на текст, який виводиться на екран монітора. Процедура OUTTEXTXY() виводить текст за заданими координатами. Формат звертання OUTTEXTXY(X,Y, <текст>), де X,Y - змінні типу integer - координати точки, в яку виводитиметься текст, <текст> - вираз типу string, що вказує на текст, який виводитиметься на екран монітора. Процедура SETTEXTSTYLE() встановлює тип шрифта, орієнтацію та розмір тексту, що виводиться на екран монітора. Синтаксис: SETTEXTSTYLE(Font, Direction, SizeChar), де Font - змінна типу word, що вказує на тип шрифта, Direction - змінна типу word, що визначає напрям виведення тексту (0 - горизонтально, 1 - вертикально), SizeChar - змінна типу word для встановлення розміру шрифта. 2. ЗАВДАННЯ 2.1. Домашня пiдготовка до роботи Ознайомлення з методами візуалізації графіків функцій на екрані монітора комп’ютера з використанням бібліотеки OPENGL (C#). Розглянемо процес створення програми, завданням якої буде візуалізація графіка заданої функції в середовищі Visual C# (www.esate.ru). Особливістю програми буде те, що в ній буде анімована демонстрація того, як міняється значення функції на графіку. Це буде реалізовано таким чином: по графіку рухається червона крапка, що приймає значення у для заданого x. Крім цього, біля курсора будуть виводитись його координати. Створіть або використайте основу додатку, як це було зроблено в попередніх лабораторних роботах. Тільки не додавайте кнопки «Візуалізувати» і «Закрити», обмежтеся елементом Simpleopenglcontrol. Додайте в проект 1 таймер - назвіть його (параметр name у властивостях таймера) Pointingrap, і встановіть в його властивостях інтервал 30 мілісекунд. Посля цього клацніть по ньому двічі, щоб створилася функція Pointingrap_tick, яка відповідає за обробку події ontimer. Тепер, коли основа додатку створена, ми перейдемо до початкового коду. Він буде базуватись на 7 функціях, які ми зараз розглянемо, але спочатку, перед кодом функції-конструктора класу додайте ініціалізацію наступних змінних: // разміри вікна double ScreenW, ScreenH; // відношення сторін вікна візуалізації// для коректного переводу координат миші в координати, // що прийняті в програмі private float devX; private float devY; // масив, котрий буде зберігати значення x,y точок графіка private float[,] GrapValuesArray; // кількість елементів в масиві private int elements_count = 0; // прапорець, що означає, що масив із значеннями координат графіка поки що не заповнений private bool not_calculate = true; // номер комірки масиву, з якого будуть узяті координати для червоної крапки // для візуалізації текучого кадра private int pointPosition = 0; // допоміжні змінні для побудови ліній від курсора миші до координатних осей float lineX, lineY; // текучі координати курсора миші float Mcoord_X = 0, Mcoord_Y = 0; Тепер розглянемо нашступні 7 функцій. Перша функція - це обробник події завантаження форми - Form1_load. Тут, при завантаженні додатку буде проведена ініціалізація OPENGL для подальшої візуалізації. Ініціалізацію OPENGL з двомірною проекцією було розглянуто в попередній лабораторній роботі. Єдиною відмінністю є те, що код став трохи більш докладним. Код цієї функції: private void Form1_Load(object sender, EventArgs e) { // ініціалізація бібліотеки glut Glut.glutInit(); // ініціалізація режиму екрану Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE); // установка кольору очищення екрану (RGBA) Gl.glClearColor(255, 255, 255, 1); // установка порта виводу Gl.glViewport(0, 0, OnGl.Width, OnGl.Height); // активація проекційної матриці Gl.glMatrixMode(Gl.GL_PROJECTION); // очистка матриці Gl.glLoadIdentity(); // визначення параметрів настройки проекції, залежно від розмірів сторін елемента OnGl. if ((float)OnGl.Width <= (float)OnGl.Height) { ScreenW = 30.0; ScreenH = 30.0 * (float)OnGl.Height / (float)OnGl.Width; Glu.gluOrtho2D(0.0, ScreenW, 0.0, ScreenH); } else { ScreenW = 30.0 * (float)OnGl.Width / (float)OnGl.Height; ScreenH = 30.0; Glu.gluOrtho2D(0.0, 30.0 * (float)OnGl.Width / (float)OnGl.Height, 0.0, 30.0); } // збереження коефіцентов, які нам необхідні для перекладу координат покажчика у віконній системі, в координати // прийняті в нашій OpenGL сцені devX = (float)ScreenW / (float)OnGl.Width; devY = (float)ScreenH / (float)OnGl.Height; // установка об'єктно-видової матриці Gl.glMatrixMode(Gl.GL_MODELVIEW); // старт лічильника, що відповідає за старт функції візуалізації сцени PointInGrap.Start(); } Тепер звернемося до функції Pointingrap_tick. Ця функція викликається із затримкою в 30 мілісекунд. У ній ми ведемо відлік того, з якого елементу масиву з координатами графіка, ми зараз возмем координати, які використовуємо для малювання червоної крапки. Звідси так само викликається функція Draw, що відповідає за візуалізацію. Код цієї функції: // функція обробник події таймера private void PointInGrap_Tick(object sender, EventArgs e) { // якщо ми дійшли до останнього елементу масиву if (pointPosition == elements_count-1) pointPosition = 0; // переходимо до початкового елементу // функція візуалізації Draw(); // перехід до наступного елементу масиву pointPosition++; } Тепер, перш ніж перейти до функцій, що відповідають за візуалізацію, ми розглянемо несолько невеликих допоміжних функий. Начитаємо з функції OnGl_mousemove. Эта функція додається створенням події Mousemove для елементу Simpleopnglcontrol (OnGl). Подія створюється аналогічно тому, як ми його створювали в розділі 2.2. Тільки в даному випадку ми переходимо до властивостей елементу OnGl, і вже в них перезходім у вкладку Event і додаємо подію Mousemove. У даній функції, ми проводимо збереження поточних координат миші, щоб в будующем використовувати їх при візуалізації графіка, а так само проводимо обчислення розмірів ліній, які по нормалях сполучатимуть координати покажчика миші з координатними осями. Код цієї функції виглядає наступним чином // обробка руху миші над елементом OnGl private void OnGl_MouseMove(object sender, MouseEventArgs e) { // зберігаємо координат миші Mcoord_X = e.X; Mcoord_Y = e.Y; // обчислюємо параметри для майбутнього домальовування ліній від покажчика миші до координатних осей. lineX = devX * e.X;lineY = (float)(ScreenH - devY * e.Y); } Тепер розглянемо функцію, яка здійснюватиме візуалізацію текстових рядків. Дана функція встановлює координати виведення растрових символів, відповідно до координат, переданих в параметрах x і у, а потім в циклі перебирає всі символи з вказаного в параметрі рядка тексту. Кожен з символів відображається за допомогою функції . У цій функції указується шрифт для виводу і змінна типу char для відображення. Код функції виглядає таким чином // функція відображення тексту private void PrintText2D(float x, float y, string text) { // встановлюємо позицію виведення растрових символів // у переданих координатах x і у. Gl.glRasterPos2f(x, y);// // у циклі foreach перебираємо значення з масиву text, // який містить значення рядка для візуалізації foreach (char char_for_draw in text) { // візуалізуємо символ з, за допомогою функції glutbitmapcharacter, використовуючи //шрифт GLUT_BITMAP_9_BY_15. Glut.glutBitmapCharacter(Glut.GLUT_BITMAP_9_BY_15, char_for_draw); } } Наступна функція, якої ми торкнемося, - це функція, що обчислює координати для побудови графіка. У ній ініціалізується масив координат і проводиться обчислення всіх координат графіка, залежно від вказаного діапазону значень x і кроку приросту цих значень. Звернете увагу на те, що при ініціалізації масиву для зберігання координат, повинно бути вказаний така кількість елементів масиву, щоб надалі їх вистачило для розміщення всіх координат, інакше відбудеться виключення, оскільки програма в процесі роботи спробує звернутися до області пам'яті, яка їй не належить. Код цієї функції з докладними коментарями // функція, що проводить обчислення координат графіка // і що заносить їх в масив Grapvaluesarray private void functionCalculation() { // визначення локальних змінних X і Y float x = 0, y = 0; // ініціалізація масиву, який зберігатиме значення 300 точок // з яких складатиметься графік GrapValuesArray = new float[300, 2]; // щетчик елементів масиву elements_count = 0; // обчислення всіх значень у, для x пренадлежащего проміжку від -15 до 15, з кроком в //0.01f for (x = -15; x < 15; x += 0.1f) { // обчислення у для поточного x// по формулі y = (float)Math.Sin(x)*3 + 1; // цей рядок задає формулу, що описує графік функції для нашого рівняння y = f(x). y = (float)Math.Sin(x)*3 + 1; // запис координати x GrapValuesArray[elements_count, 0] = x; // запис координати y GrapValuesArray[elements_count, 1] = y; // підрахунок елементів elements_count++; } // змінюємо прапор, що сигналізує про те, що координати графіка не розрахованіnot_calculate = false; } Функція, що виконує візуалізацію графіка. Оскільки візуалізацію графіка можна віднести до конкретної підзадачі функції візуалізації сцени (і щоб не захаращувати функцію візуалізації сцени), ми винесемо візуалізацію графіка в окрему функцію. У цій функції спочатку буде перевірений прапор, що сигналізує про те, що координати графіка обчислені і занесені в масив (змінна not_calculate). В тому випадку, якщо прапор указує, що прорахунку значень ще не було - викликається функція, яка порахує значення координат точок графіка і заповнить ними масив. Далі реалізується прохід циклом for по масиву значень координат точок графіка і їх візуалізація, причому ми візуалізуємо не крапки, а об'єднуємо ці крапки в лінію, грунтуючись на значенні координат крапок, як на вершинах. По завершенню отрісовки графіка, проводитися малювання червоної крапки, в тих координатах, до яких ми дійшли, послідовно перебираючи значення елементів в масиві координат. Вихідний код даної функції // візуалізація графіка private void DrawDiagram() { // перевірка прапора, що сигналізує про те, що координати графіка вичеслени if (not_calculate) { // якщо немає - те викликаємо функцію обчислення координат графікаfunctionCalculation(); } // стартуємо побудову в режимі візуалізації крапок // об'єднуваних в лінії (GL_LINE_STRIP) Gl.glBegin(Gl.GL_LINE_STRIP);// малюємо початкову точкуGl.glVertex2d(GrapValuesArray[0, 0], GrapValuesArray[0, 1]);// проходимо по масиву з координатами обчислених точокfor (int ax = 1; ax < elements_count; ax+=2){ // передаємо в OPENGL інформацію про вершину, що бере участь в побудові лінійGl.glVertex2d(GrapValuesArray[ax, 0], GrapValuesArray[ax, 1]); } // завершуємо режим малюванняGl.glEnd();// встановлюємо розмір крапок, рівний 5 пікселямGl.glPointSize(5);// встановлюємо поточним кольором - червоний колірGl.glColor3f(255, 0, 0);// активуємо режим виведення точок (GL_POINTS) Gl.glBegin(Gl.GL_POINTS);// виводимо червону крапку, використовуючи ту ячеку масиву, до якої ми дійшли // (обчислюється у функиі обробнику подій таймера)Gl.glVertex2d(GrapValuesArray[pointPosition, 0], GrapValuesArray[pointPosition, 1]);// завершуємо режим малюванняGl.glEnd();// встановлюємо розмір крапок рівний одиниціGl.glPointSize(1); } І тепер нам залишилося проглянути лише останню функцію - функцію Draw. У нім візуалізується координатна сітка під графіком, координатні осі і букви для їх позначень, а так само викликаємося функція малювання графіка, і виводяться координати миші з лініями, що сполучають покажчик миші і осі координат. Код цієї функції виглядає таким чином // функція, що управляє візуалізацією сцениprivate void Draw(){ // очищення буфера кольору і буфера глибиниGl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);// очищення поточної матриціGl.glLoadIdentity();// утснаовка чорного кольоруGl.glColor3f(0, 0, 0);// поміщаємо стан матриці в стек матриць Gl.glPushMatrix();// виконуємо переміщення в прострастве по осях X і Y Gl.glTranslated(15, 15, 0);// активуємо режим малювання (Вказані далі точки виводитимуться як точки GL_POINTS) Gl.glBegin(Gl.GL_POINTS);// за допомогою проходу вдумя циклами, створюємо сітку з крапок for (int ax = -15; ax < 15; ax++) { for (int bx = -15; bx < 15; bx++) { // вивід точки Gl.glVertex2d(ax, bx); } } // завершення режиму малювання примітивів Gl.glEnd();// активуємо режим малювання, кожні 2 послідовно викликані комманди glVertex // об'єднуються в лініїGl.glBegin(Gl.GL_LINES);// далі ми малюємо координатні осі і стреклі на їх кінцях Gl.glVertex2d(0, -15);Gl.glVertex2d(0, 15);Gl.glVertex2d(-15, 0);Gl.glVertex2d(15, 0);// вертикальна стрілка Gl.glVertex2d(0, 15);Gl.glVertex2d(0.1, 14.5);Gl.glVertex2d(0, 15);Gl.glVertex2d(-0.1, 14.5);// горизонтальна стрілка Gl.glVertex2d(15, 0);Gl.glVertex2d(14.5, 0.1);Gl.glVertex2d(15, 0);Gl.glVertex2d(14.5, -0.1);// завершуємо режим малюванняGl.glEnd();// виводимо підписи осей "x" і "y"PrintText2D(15.5f, 0, "x"); PrintText2D(0.5f, 14.5f, "y"); // викликаємо функцію малювання графіка DrawDiagram(); // повертаємо матрицю із стека Gl.glPopMatrix();// виводимо текст із значенням координат біля курсора PrintText2D(devX * Mcoord_X + 0.2f, (float)ScreenH - devY * Mcoord_Y + 0.4f, "[ x: " + (devX * Mcoord_X - 15).ToString() + " ; y: " + ((float)ScreenH - devY * Mcoord_Y - 15).ToString() + "]"); // встановлюємо червоний колір Gl.glColor3f(255, 0, 0);// вмикаємо режим малювання ліній, для того, щоб намалювати// лінії від курсора миші до координатних осейGl.glBegin(Gl.GL_LINES);Gl.glVertex2d(lineX, 15);Gl.glVertex2d(lineX, lineY);Gl.glVertex2d(15, lineY);Gl.glVertex2d(lineX, lineY);Gl.glEnd();// чекаємо завершення візуалізації кадруGl.glFlush();// сигнал для оновлення елементу того, що реалізовує візуалізацію.OnGl.Invalidate(); } 2. Написати програму, яка будує в середині екрану систему координат XY і на ній графік функції Y=F(X), використовуючи графічні бібліотеку OpenGl, якщо аргумент або параметр змінюється на проміжку [a;b] з кроком h. Крім C# можна використовувати С+ або Delphi. Варіанти завдань беруть з таблиці 1 за вказівкою викладача. Таблиця 1. N% п/п Функція Інтервал Крок
1
r=[1;50] k=[0;2*pi] h=5 h=pi/(4*r)
2
x=[0;16] h=0.1
3
x=[0;180] h=0.15
4
x=[1;] h=0.5
5
t=[1;200] h=0.5
6
t=[1;2] h=0.01
7
t=[-100;100] h=0.5
8
t=[1;20] h=0.1
Продовження таблиці 2. N% п/п Функція Інтервал Крок
9
t=[1;18] h=0.1
10
t=[-100;100] h=0.5
11
t=[1;500] h=0.5
12
t=[1;3] h=0.005
13
t=[-100;100] h=0.5
14
t=[0;20] h=0.01
15
t=[-100;100] h=0.5
16
t=[1;20] h=0.01
17
x=[1;20] h=0.01
18
t=[-1;200] h=0.5
19
t=[1;200] h=0.5
20
x=[0;180] h=0.5
21
x=[0;180] h=0.5
22
x=[5;180] h=0.5
23
x=[0;180] h=0.5
24
t=[1;17] h=0.01
25
x=[5;150] h=0.1
2.2. Робота в лабораторiї 1. Ввести в комп'ютер програму, написану на одній з мов програмування (Delphi, С++, С#) згідно з отриманим завданням. 2. Здійснити налагодження введеної програми, виправивши виявлені компілятором помилки. 3. Виконати програму. Текст налагодженої програми та отримані результати оформити в звіт з лабораторної роботи. 3. ЗМIСТ ЗВIТУ 1. Повний текст завдання. 2. Блок-схема алгоритму програми. 3. Список ідентифікаторів констант, змінних, процедур і функцій, використаних в програмі, та їх пояснення. 4. Остаточно відлагоджений текст програми згідно з отриманим завданням. 5. Результати виконання програми. 4. КОНТРОЛЬНI ПИТАННЯ Як здійснюється масштабування при побудові графіків на екрані монітора комп’ютера. Які процедури та функції мов програмування для управління текстом в графічному режимі вам відомі? Як встановити поточний стиль тексту? СПИСОК ЛIТЕРАТУРИ Петров М. Компьютерная графика. Підручник|посібник| для вузів. - Спб.: Пітер, 2002. Рейбоу В. Компьютерная графика. Енциклопедія. - Спб.: Пітер, 2002. Роджерс Д. Алгоритмические основы машинной графики. - М.: Мир|світ|, 1989.. Сніжко Е. Компьютерная геометрия и графика: Конспект лекций. – Спб.: Видавництво БГТУ, 2005. Блинова Т.А., Порев В.Н. Компьютерная графика. Учебное пособие. К. Юниор., 2006. www.esate.ru