Деление треугольника икосаэдра



Рисунок 6.3. Деление треугольника икосаэдра


Треугольник с вершинами VI, V2 и V3 разбивается на четыре треугольника: (V1,V12,V31), (V2,V23,V12), (V3,V32,V23) и (V12.V23.V31). После этого промежуточные точки деления надо посадить на поверхность шара, то есть изменить их координаты так, чтобы концы векторов (V12, V23 и V31) дотянулись до поверхности шара. Для этого достаточно нормировать векторы с помощью уже существующей процедуры Scale. Она впоследствии будет использована как для масштабирования нормали, так и для нормировки координат вершин новых треугольников. Но сейчас мы будем вычислять нормаль приближенно. Введем еще две вспомогательные функции:

//=== Команды OpenGL для изображения одного треугольника

void setTria(double *vl, double *v2, double *v3)

{

//====== Нормаль и вершина задаются одним вектором

glNormal3dv(vl);

glVertex3dv(vl);

glNormalSdv (v2);

glVertex3dv(v2);



glNormal3dv(v3);

glVertex3dv(v3);

glEnd() ;

}

//====== Генерация внутренних треугольников

void Split(double *vl, double *v2, double *v3)

{

//====== Промежуточные вершины

double v!2[3], v23[3], v31[3);

for (int l=0; l< 3; i++) {

//====== Можно не делить пополам,

//====== так как будем нормировать

v12[i] = vl[i]+v2[i];

v23[i] = v2[i]+v3[i];

v31 [i] = v3[i]+vl [i];

}

//====== Нормируем три новые вершины

Scale(v!2);

Scale(v23);

Scale(v31); //====== и рисуем четыре треугольника

setTria(vl, v!2, v31);

setTria (v2, v23, v!2);

setTria(v3, v31, v23);

setTria(v!2,v23, v31);

}

Вставьте эти глобальные функции в файл и дайте следующую версию функцию DrawScene, в которой отсутствует вызов функции getNorm для точного вычисления нормали, но есть вызов функции Split для каждой из 20 граней икосаэдра. В результате мы получаем фигуру из 80 треугольных граней, которая значительно ближе к сфере, чем икосаэдр:

void DrawScene()

{

static double

angle = 3. * atan(l.)/2.5, V = cos (angle), W = sin (angle),

v[12] [3] =

{-V,0.,W}, {V,0.,W}, {-V,.0.,-W},

(V,0.,-W), {0.,W,V}, {0.,W,-V},

(0.,-W,V), (0.,-W,-V), {W,V,0.},

{-W,V,0.}, {W,-V,0.}, {-W,-V,0.}

};

static GLuint id[20][3] =

{

(0,1, 4), (0,4, 9), {9,4, 5}, (4,8, 5), (4,1,8),

(8,1,10), (8,10,3), (5,8, 3), (5,3, 2), (2,3,7),

(7,3,10), (7,10,6), (7,6,11), (11,6,0), (0,6,1),

(6,10,1), (9,11,0), (9,2,11), (9,5, 2), (7,11,2)

};

glNewList(l,GL_COMPILE);

glColor3d (1., 0.4, 1.) ;

glBegin(GLJTRIANGLES);

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

Split (v[id[i][0]], v[id[i][l]], v[id[i] [2] ]) ;

glEnd() ;

glEndList () ;

}

На этой стадии я рекомендую посмотреть, какие интересные и неожиданные результаты могут быть получены вследствие ошибок. Все мы ошибаемся, вот и я так долго возился с направлением обхода и со знаком нормали, что в промежуточных вариантах получал чудовищные комбинации. Многие из них «канули в Лету», но один любопытный вариант легко смоделировать. Если ошибки происходят в условиях симметричного отражения, то возникают ситуации, сходные со случайными изменениями узоров в калейдоскопе. Замените на обратные знаки компонентов вектора в функции Scale. Это действие в предыдущих версиях программы было эквивалентно изменению знака нормали. Найдите строку, похожую на ту, что приведена ниже, и замените знаки так, как показано, на минусы.

v[0] /= -d; v[l] /= -d; v[2] /= -d;



Содержание раздела