Начальная стадия: рисование фигур


Пример 2.3.2. Рисование ломаных линий, заданных в файле



бет3/3
Дата29.05.2016
өлшемі211.5 Kb.
#100717
1   2   3

Пример 2.3.2. Рисование ломаных линий, заданных в файле
Наиболее интересные изображения, которые можно создать из ломаных линий, содержат значительное количество отрезков прямых. Описание этих ломаных удобно записывать в файл, чтобы при желании изображение можно было бы нарисовать снова. (Ряд интересных примеров можно найти в Интернете; см. введение к данной книге.)



Рис. 2.12. Рисование ломаных линий, записанных в файле

Не составляет труда написать подпрограмму, рисующую ломаные, записанные в файл. Рисунок 2.12 — это пример изображения, которое можно нарисовать таким способом.

Рассмотрим файл dino.dat, содержащий набор ломаных в следующем формате (комментарии не являются частью файла).

21
// number of polylines in the file


// число ломаных в файле
4
// number of points in the first polyline
// число точек в первой ломаной
169 118
// first point of first polyline
// первая точка первой ломаной
174 120
// second point of first polyline
// вторая точка первой ломаной
179 124
178 126
5
// number of points in the second polyline
// число точек во второй ломаной
298 86
// first point of second polyline
// первая точка второй ломаной
304 92
310 104
314 114
314 119
29
32 435
10 439
и т.д.

(Полностью данный файл доступен на web-сайте к этой книге. См. введение.) В листинге 2.9 приведена подпрограмма на С++, которая открывает такой файл и затем рисует все ломаные линии, которые тот содержит. Читается файл с именем, содержащимся в строке fileName, после чего рисуется каждая ломаная линия. Данную подпрограмму можно использовать вместо myDisplay() в листинге 2.7 в качестве функции обратного вызова на событие перерисовки redraw. Величины A, B, C и D следует выбрать так, чтобы ломаные линии имели нормальный масштаб. Мы изложим общие принципы такого выбора в главе 3.

Вариант функции drawPolyLineFile(), приведенный в листинге 2.9, имеет очень слабый уровень контроля ошибок. Если файл нельзя открыть — возможно, потому, что в функцию передано неверное имя, — подпрограмма просто возвращает управление в главную программу. Если файл содержит неверные данные, например вместо целых чисел стоят вещественные, — результаты будут непредсказуемы. Очевидно, что данную подпрограмму следует рассматривать только как точку отсчета для разработки более адекватной версии.

Листинг 2.9. Вычерчивание ломаных линий, записанных в файле

#include


void drawPolyLineFile(char * fileName)
{
fstream inStream;
inStream.open(fileName, ios ::in);
// open the file
// открываем файл
if(inStream.fail())
return;
glClear(GL_COLOR_BUFFER_BIT);
// clear the screen
// очищаем экран
GLint numpolys, numLines, x, y;
inStream >> numpolys;
// read the number of polylines
// читаем число ломаных
for(int j = 0; j < numpolys; j++)
// read each polyline
// читаем каждую ломаную
{
inStream >> numLines;
glBegin(GL_LINE_STRIP);
// draw the next polyline
// чертим очередную ломаную
for (int i = 0; i < numLines; i++)
{
inStream >> x >> y;
// read the next x, y pair
// читаем очередную пару x, y
glVertex2i(x, y);
}
glEnd();
}
glFlush();
inStream.close();
}

Пример 2.3.3. Параметризация рисунков
На рис. 2.13 изображен простой домик, состоящий из нескольких ломаных. Его можно нарисовать с использованием кода, частично приведенного в листинге 2.10. (Какой код понадобится для рисования двери и окна?)



Рис. 2.13. Домик

Листинг 2.10. Рисование домика с «вмонтированными» размерами

void hardwirededHouse()


{
glBegin(GL_LINE_LOOP);
glVertex2i(40, 40);
// draw the shell of house
// рисуем остов домика
glVertex2i(40, 90);
glVertex2i(70, 120);
glVertex2i(100, 90);
glVertex2i(100, 40);
glEnd();
glBegin(GL_LINE_STRIP);
glVertex2i(50, 100);
// draw the chimney
// рисуем трубу
glVertex2i(50, 120);
glVertex2i(60, 120);
glVertex2i(60, 110);
glEnd();
. . .
// draw the door
// рисуем дверь
. . .
// draw the window
// рисуем окно
}

Данный подход не является достаточно гибким. Координаты каждой концевой точки «вмонтированы» внутрь кода, вследствие чего функция hardwirededHouse() может рисовать только единственный домик одного размера, размещенный единственным образом. Большей гибкости можно достигнуть, если параметризовать этот рисунок и передавать значения этих параметров в подпрограмму. Таким способом мы можем рисовать семейства объектов, отличающихся различными значениями параметров. Листинг 2.11 демонстрирует такой подход. Параметры задают расположение верха крыши, а также ширину и высоту домика. Такие детали, как рисование трубы, двери и окна, оставлены для вас в качестве упражнения.



Листинг 2.11. Рисование параметризованного домика

void parameterizedHouse(GLintPoint peak, Glint width, GLint height)


// the top of house is at the peak;
// верхняя точка домика;
// the size of house
// is given by the height and width
// размер домика задается его высотой и шириной
{
glBegin(GL_LINE_LOOP);
glVertex2i(peak.x, peak.y);
// draw shell of house
// рисуем каркас домика
glVertex2i(peak.x + width / 2, peak.y - 3 * height /8);
glVertex2i(peak.x + width / 2, peak.y – height);
glVertex2i(peak.x - width / 2, peak.y - height);
glVertex2i(peak.x - width / 2, peak.y - 3 * height /8);
glEnd();
draw the chimney in ihe same fashion
// рисуем трубу таким же способом
draw the door
// рисуем дверь
draw the window
// рисуем окно
}

Данная подпрограмма может быть использована для того, чтобы нарисовать целую «деревню», как это показано на рис. 2.14, посредством последовательных вызовов функции parameterizedHouse() с различными значениями параметров. (Каким образом один из домиков оказался «перевернутым»? Все ли домики с этого рисунка можно нарисовать с помощью данной подпрограммы?)





Рис. 2.14. «Деревня» из домиков, нарисованных с помощью подпрограммы parameterizedHouse()

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

Листинг 2.12. Тип данных для связного списка вершин

class GlintPointArray(


const int MAX_NUM = 100;
public:
int num;
GLintPoint pt[MAX_NUM];
}

В листинге 2.13 показана возможная реализация подпрограммы для рисования ломаных линий. Эта подпрограмма имеет, помимо прочих, параметр closed. Если closed не равен нулю, то последняя вершина ломаной соединяется с первой. Значение closed устанавливает аргумент для glBegin(). Эта подпрограмма просто посылает каждую из вершин ломаной в OpenGL.



Листинг 2.13. Тип данных «связный список» и рисование ломаной линии или полигона

void drawPolyLine(GlintPointArray poly, int closed)


{
glBegin(closed ? GL_LINE_LOOP; GL_LINE_STRIP);
for(int I = 0; I < poly.num; I++)
glVertex2i(poly,pt[I],x,poly,pt[I].y);
glEnd();
glFlush();
}

2.3.2. Рисование линий с использованием moveto() и lineto()
Как мы уже отмечали ранее, многие графические системы имеют инструменты для рисования линий на базе функций moveto() и lineto(). Эти функции используются столь часто, что представляется важным поближе познакомиться с их использованием. Мы приспособим свои собственные функции moveto() и lineto(), которые действуют при вызове инструментов OpenGL. Кроме того, в главе 3 мы «приоткроем завесу» и покажем, как бы вы создавали функции moveto() и lineto(), исходя из базовых принципов, если бы мощная библиотека типа OpenGL не была доступна.

Напомним, что функции moveto() и lineto() управляют гипотетическим пером, позиция которого называется текущими координатами (current position), или CP. Мы можем объединить действия обеих этих функций следующим образом:

moveto(x, y): устанавливаем CP в (x, y),
lineto(x, y): рисуем прямую линию от CP до (x, y) и затем присваиваем CP значение (x, y).

Таким образом, прямая линия из точки (x1, y1) до точки (x2, y2) может быть нарисована посредством двух вызовов: moveto(x1, y1) и lineto(x2, y2). Ломаная линия на базе списка точек (x0, y0), (x1, y1),…, (xn–1, yn–1) легко вычерчивается с помощью такого кода:

moveto(x[0], y[0]);
for(int i = 1; i < n; i++)
lineto(x[i], y[i]);

Нетрудно построить функции moveto() и lineto() в среде OpenGL. Чтобы осуществить это, нам необходимо определить и поддерживать свое собственное CP. Для случая целых координат проделать этот фокус могла бы реализация, приведенная в листинге 2.14.



Листинг 2.14. Определение moveto() и lineto() в OpenGL

GLintPoint CP;


// global current position
// глобальные текущие координаты (CP)
//<<<<<<<<<<<<< moveto >>>>>>>>>>>
void moveto(GLint x, GLint y)
{
CP.x = x; CP.y = y;
// update the CP
// обновляем CP
}
//<<<<<<<<<< lineto >>>>>>>>>>
void lineto(GLint x, GLint y)
{
glBegin(GL_LINES);
// draw the line
// рисуем линию
glVertex2i(CP.x, CP.y);
glVertex2i(x, y);
glEnd();
gIFlush();
CP.x = x; CP.y = y;
// update the CP
// обновляем CP
}

Достарыңызбен бөлісу:
1   2   3




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет