Міністерство освіти і науки, молоді та спорту України
Національний університет „Львівська Політехніка”
Кафедра БІТ
Розрахункова робота №2
З курсу:
“Комп’ютерна графіка”
Львів 2012
Мета роботи : Набути практичних навиків в складанні програм для побудови зображень на екрані комп’ютера.
Завдання:
Створити на екрані комп’ютера графічне вікно і сформувати в ньому рухоме зображення. Вікно розмістити в верхньому правому куті екрану. Навести межі вікна. Параметри рухомого зображення визначені в таблиці 3. Параметри зображення задані в пікселах. Варіант завдання визначає викладач.
Графік і вікно з рухомим зображенням повинні бути присутні на екрані одночасно, причому вікно не має перекривати графік (рис.1).
Рис.1. Розташування графіка і рухомого зображення на екрані комп’ютера
№ варіанту
Структура зображення
Пояснення
2
Кулька радіусу r зі спицями котиться по видимому круговому контуру радіусу R за годинниковою стрілкою. Кольори кульки і контуру різні.
Параметри : r=20, R=100
Короткі теоретичні відомості.
В основі побудови зображень на екрані комп'ютера лежать операції переносу, масштабування (гомотетії) і повороту, а також їх композиції.
Точку на ху-площині можна перенести в нове положення шляхом додавання до її координат констант переносу:
, .
Для векторної форми
, ,
;
,
P’=P+T.
Перенесення складного об'єкту виконується шляхом перенесення всіх його точок, – реперних точок: .
Масштабування точки передбачає домноження її координат на коефіцієнти масштабування:
, .
При переході до векторної форми, де
,
можна записати
,
P’=P(S
При повороті точки на кут відносно початку координат нові координати визначаються так (проти годинникової стрілки, додатний поворот):
,
.
В векторній формі, де ,
,
P’=P(R
При від'ємному повороті (за годинниковою стрілкою)
Розрахунок повороту в полярних координатах можна виразити так:
;
,
.
На практиці наведені елементарні перетворення при побудові зображень на екрані поєднують. Для цього координати точки приводяться до однорідних координат. При цьому з’являється можливість всі перетворення реалізувати з допомогою множення матриць.
Даний метод полягає в тому, що кожна точка в -мірному просторі є проекцією точки з -мірного простору. Так точка на площині може бути проекцією точки з простору , де може набувати будь-яких значень, крім .
Для координати є нормалізованими.
Декартові координати точки на площині враховуються як:
.
Надалі двомірні перетворення в площині екрану будемо розглядати як перетворення в однорідних нормалізованих координатах .
Для перетворень в нормалізованих однорідних координатах розмірність матриць і векторів перетворень збільшується на 1.
Перенесення.
, ,
P’=P(T(Dx,Dy)
Масштабування.
P’=P(S(Sx,Sy)
.
Поворот.
P’=P(R(()
Код програми:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
// для работи з бібліотекою OpenGL
using Tao.OpenGl;
// для работи з бібліотекою FreeGLUT
using Tao.FreeGlut;
// для работи з елементом управління SimpleOpenGLControl
using Tao.Platform.Windows;
namespace rozrah1
{
public partial class Form1 : Form
{
float Angle;
float PI = (float)Math.PI;
// разміри вікна
double ScreenW, ScreenH, Screen;
// відношення сторін вікна візуалізації
private float devX;
private float devY;
// масив, котрий буде зберігати значення x,y точок графіка
private float[,] GrapValuesArray;
// кількість елементів в масиві
private int elements_count = 0;
// прапорець, що означає, що масив із значеннями координат графіка поки що не заповнений
private bool not_calculate = true;
// допоміжні змінні для побудови ліній від курсора миші до координатних осей
float lineX, lineY;
// поточні координати курсора миші
float Mcoord_X = 0, Mcoord_Y = 0;
public Form1()
{
InitializeComponent();
AnT.InitializeContexts();
}
private void AnT_Load(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
Screen = 400;
// ініціалізація бібліотеки glut
Glut.glutInit();
// ініціалізація режиму екрану
Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE);
// установка кольору очищення екрану (RGBA)
Gl.glClearColor(255, 255, 255, 1);
// установка порта виводу
Gl.glViewport(0, 0, AnT.Width, AnT.Height);
// активація проекційної матриці
Gl.glMatrixMode(Gl.GL_PROJECTION);
// очистка матриці
Gl.glLoadIdentity();
// визначення параметрів настройки проекції, залежно від розмірів сторін елемента OnGl.
if ((float)AnT.Width <= (float)AnT.Height)
{
ScreenW = Screen;
ScreenH = Screen * (float)AnT.Height / (float)AnT.Width;
Glu.gluOrtho2D(0.0, ScreenW, 0.0, ScreenH);
}
else
{
ScreenW = Screen * (float)AnT.Width / (float)AnT.Height;
ScreenH = Screen;
Glu.gluOrtho2D(0.0, Screen * (float)AnT.Width / (float)AnT.Height, 0.0, Screen);
}
// збереження коефіцентов, які нам необхідні для перекладу координат покажчика у віконній системі, в координати
// прийняті в нашій OpenGL сцені
devX = (float)ScreenW / (float)AnT.Width;
devY = (float)ScreenH / (float)AnT.Height;
// установка об'єктно-видової матриці
Gl.glMatrixMode(Gl.GL_MODELVIEW);
PointInGrap.Start();
}
private void AnT_MouseMove(object sender, MouseEventArgs e)
{
// зберігаємо координат миші
Mcoord_X = e.X;
Mcoord_Y = e.Y;
// обчислюємо параметри для майбутнього домальовування ліній від покажчика миші до координатних осей.
lineX = devX * e.X;
lineY = (float)(ScreenH - devY * e.Y);
}
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);
}
}
private void functionCalculation()
{
float krok = 0.00001f;
float a = 19.0001f;
float b = 19.001f;
// визначення локальних змінних X і Y
float x = 0, y = 0;
// ініціалізація масиву, який зберігатиме значення 300 точок
// з яких складатиметься графік
GrapValuesArray = new float[(int)((b - a) / krok + 10), 2];
// щетчик елементів масиву
elements_count = 0;
// обчислення всіх значень у, для x пренадлежащего проміжку від -15 до 15, з кроком в //0.01f
for (x = a; x < b; x += krok)
{
// обчислення у для поточного x
// цей рядок задає формулу, що описує графік функції для нашого рівняння y = f(x).
y = (float)(Math.Log(x - 19));
// запис координати x
GrapValuesArray[elements_count, 0] = x;
// запис координати y
GrapValuesArray[elements_count, 1] = y;
// підрахунок елементів
elements_count++;
}
// змінюємо прапор, що сигналізує про те, що координати графіка не розраховані
not_calculate = false;
}
private void DrawDiagram()
{
// перевірка прапора, що сигналізує про те, що координати графіка вичеслени
if (not_calculate)
{
// якщо немає - те викликаємо функцію обчислення координат графіка
functionCalculation();
}
// стартуємо побудову в режимі візуалізації крапок
// об'єднуваних в лінії (GL_LINE_STRIP)
Gl.glColor3f(0, 1, 0);
Gl.glLineStipple(1, 0x00ff);
Gl.glEnable(Gl.GL_LINE_STIPPLE);
Gl.glEnable(Gl.GL_LINE_SMOOTH);
Gl.glBegin(Gl.GL_LINE_STRIP);
// малюємо початкову точку
Gl.glVertex2d((GrapValuesArray[0, 0] - 19) * 25000, GrapValuesArray[0, 1] * 15 + 250);
// проходимо по масиву з координатами обчислених точок
for (int ax = 1; ax < elements_count; ax += 1)
{
// передаємо в OPENGL інформацію про вершину, що бере участь в побудові ліній
Gl.glVertex2d((GrapValuesArray[ax, 0] - 19) * 25000, GrapValuesArray[ax, 1] * 15 + 250);
}
// завершуємо режим малювання
Gl.glEnd();
Gl.glDisable(Gl.GL_LINE_SMOOTH);
Gl.glDisable(Gl.GL_LINE_STIPPLE);
}
// функція, що управляє візуалізацією сцени
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(10, 10, 0);
// активуємо режим малювання (Вказані далі точки виводитимуться як точки GL_POINTS)
Gl.glBegin(Gl.GL_POINTS);
// за допомогою проходу двома циклами, створюємо сітку з крапок
for (double ax = -1; ax < Screen; ax += Screen / 10)
{
for (double bx = -1; bx < Screen; bx += Screen / 10)
{
// вивід точки
Gl.glVertex2d(ax, bx);
}
}
// завершення режиму малювання примітивів
Gl.glEnd();
// активуємо режим малювання, кожні 2 послідовно викликані комманди glVertex
// об'єднуються в лінії
Gl.glBegin(Gl.GL_LINES);
// далі ми малюємо координатні осі і стреклі на їх кінцях
Gl.glVertex2d(0, -10);
Gl.glVertex2d(0, Screen - 20);
Gl.glVertex2d(-10, Screen / 2 - 50);
Gl.glVertex2d(Screen - 1, Screen / 2 - 50);
// вертикальна стрілка
Gl.glVertex2d(0, Screen - 20);
Gl.glVertex2d(Screen / 100, Screen - 20 - Screen / 50);
Gl.glVertex2d(0, Screen - 20);
Gl.glVertex2d(-Screen / 100, Screen - 20 - Screen / 50);
// горизонтальна стрілка
Gl.glVertex2d(Screen - 1, Screen / 2 - 50);
Gl.glVertex2d(Screen - 1 - Screen / 50, Screen / 2 - 50 + Screen / 100);
Gl.glVertex2d(Screen - 1, Screen / 2 - 50);
Gl.glVertex2d(Screen - 1 - Screen / 50, Screen / 2 - 50 - Screen / 100);
// завершуємо режим малювання
Gl.glEnd();
// виводимо підписи осей "x" і "y"
PrintText2D((float)(Screen - 1 - Screen / 50), (float)(Screen / 2 - 50 + Screen / 50), "x");
PrintText2D((float)(Screen / 50), (float)(Screen - 20 - Screen / 50), "y");
// викликаємо функцію малювання графіка
DrawDiagram();
// повертаємо матрицю із стека
Gl.glPopMatrix();
// виводимо текст із значенням координат біля курсора
// PrintText2D(devX * Mcoord_X + 0.2f, (float)ScreenH - devY * Mcoord_Y + 0.4f, "[ x: " + ((devX * Mcoord_X - 10)/40).ToString() + " ; y: " + (((float)ScreenH - devY * Mcoord_Y - 20) / 40).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();
sircle(270, 270, 0, 100, 0);
spicy(20, 270, 270, 0, 115);
// чекаємо завершення візуалізації кадру
Gl.glFlush();
Angle = Angle + 1;
// сигнал для оновлення елементу того, що реалізовує візуалізацію.
AnT.Invalidate();
}
private void spicy(float size, float a, float b, float teta_begin, float r)
{
float teta, x, y;
teta = PI * (Angle / 360) + teta_begin;
x = a + Convert.ToSingle(r * Math.Cos(teta));
y = b + Convert.ToSingle(r * Math.Sin(teta));
float[,] spic = new float[4, 2];
spic[0, 0] = size / 2;
spic[0, 1] = size / 2;
spic[1, 0] = size / 2;
spic[1, 1] = -size / 2;
spic[2, 0] = -size / 2;
spic[2, 1] = -size / 2;
spic[3, 0] = -size / 2;
spic[3, 1] = size / 2;
spic = ToAngle(spic, 2, 1);
Gl.glColor3f(0, 0, 255);
int j;
Gl.glBegin(Gl.GL_LINES);
Gl.glVertex2d(spic[0, 0] + x, spic[0, 1] + y);
Gl.glVertex2d(spic[2, 0] + x, spic[2, 1] + y);
Gl.glVertex2d(spic[1, 0] + x, spic[1, 1] + y);
Gl.glVertex2d(spic[3, 0] + x, spic[3, 1] + y);
Gl.glEnd();
float x_centr = (spic[0, 0] + spic[2, 0]) / 2 + x;
float y_centr = (spic[0, 1] + spic[2, 1]) / 2 + y;
//Console.WriteLine("x={0}, y={1}", x_centr, y_centr);
int n = 25;
double[] x_c = new double[n];
double[] y_c = new double[n];
int R = 14;
for (int i = 0; i < n; i++)
{
x_c[i] = x_centr + R * Math.Cos(Math.PI / 2 + 2 * Math.PI * i / n);
y_c[i] = y_centr + R * Math.Sin(Math.PI / 2 + 2 * Math.PI * i / n);
// Console.WriteLine("x={0}, y={1}", x_c[i], y_c[i]);
}
Gl.glColor3f(0, 1, 0);
Gl.glBegin(Gl.GL_LINE_LOOP);
for (int i = 0; i < n; i++)
{
Gl.glVertex2d(x_c[i], y_c[i]);
}
Gl.glEnd();
Gl.glLineWidth(1);
}
private void sircle(float a, float b, float r_angle, float r_sircle, float teta_begin)
{
//початкові умови
float i = 0;
float x, y, x1, y1, teta = 0, teta2 = 0;
float teta3, x2, y2;
// Задання кута повороту кола відносно центру квадрата
teta3 = PI * (Angle / 360) + teta_begin;
//Координати центра кола відповідно повороту
x2 = a + Convert.ToSingle(r_angle * Math.Cos(teta3));
y2 = b + Convert.ToSingle(r_angle * Math.Sin(teta3));
//Зображення кола
Gl.glBegin(Gl.GL_LINE_STRIP);
Gl.glColor3f(0, 0, 0);
for (i = 0; i < 2 * PI + 1; i = i + 0.1309f)
{
teta = teta + 0.1309f;
teta2 = teta2 + 0.1309f;
x = Convert.ToSingle(r_sircle * Math.Cos(teta));
y = Convert.ToSingle(r_sircle * Math.Sin(teta));
x1 = Convert.ToSingle(r_sircle * Math.Cos(teta2));
y1 = Convert.ToSingle(r_sircle * Math.Sin(teta2));
Gl.glVertex2d(x + x2, y + y2);
Gl.glVertex2d(x1 + x2, y1 + y2);
}
Gl.glEnd();
}
public float[,] ToAngle(float[,] _in, float revers, float speed)
{
float teta;
teta = revers * PI * (speed * Angle / 360);
float[,] _out = new float[4, 2];
int i = 0;
for (i = 0; i < 4; i++)
{
_out[i, 0] = Convert.ToSingle(_in[i, 0] * Math.Cos(teta) - _in[i, 1] * Math.Sin(teta));
_out[i, 1] = Convert.ToSingle(_in[i, 0] * Math.Sin(teta) + _in[i, 1] * Math.Cos(teta));
}
return _out;
}
private void PointInGrap_Tick_1(object sender, EventArgs e)
{
// функция визуализации
Draw();
kola();
}
private void kola()
{
Gl.glTranslated(20, 40, 0);
Glut.glutSolidSphere(10, 100, 10);
}
}
}
Виконання програми:
Висновок: Виконуючи розрахункову роботу я набув практичних навиків в складанні програм для побудови зображень на екрані комп’ютера.