Глава 33. Изображение и управление трехмерными объектами в трехмерном пространстве

Листинг 33.1. Код выше и в теле метода Form1_Load.

//Начало координат:

private const double x_focus = 0;

private const double y_focus = 0;

private const double z_focus = 0;

//Сферические координаты глаза наблюдателя (точки E):

private float r_Eye;

private float phi_Eye;

private float theta_Eye;

//Переменные и матрица (как массив) MatrixProjection:

//(во всех массивах нулевые индексы не используем):

private const double pi = Math.PI;

private int Tetrahedron;

private int Cube;

private int Octahedron;

private int Dodecahedron;

private int Icosahedron_first;

private int Icosahedron_last;

private float[,] MatrixProjection = new float[5, 5];

//Для параллельного проецирования объекта на экран

//(parallel projection) задаем константу:

private const int ParallelProjection = 0;

//Для перспективного проецирования объекта на экран

//(perspective projection) задаем константу:

private const int PerspectiveProjection = 1;

private void Form1_Load(object sender, EventArgs e)

{

//Задаем координаты глаза наблюдателя, например:

r_Eye = 4;

phi_Eye = (float)(0.05 * pi);

theta_Eye = (float)(0.3 * pi);

//Вызываем метод для перспективного проецирования,

//когда type_of_projection = PerspectiveProjection

//(для параллельного проецирования вместо

//PerspectiveProjection пишем ParallelProjection):

Projection(ref MatrixProjection, PerspectiveProjection,

r_Eye, phi_Eye, theta_Eye,

(float)x_focus, (float)y_focus, (float)z_focus, 0, 1, 0);

//Рассчитываем параметры геометрического тела:

СalculateParameters();

//Связываем элемент PictureBox1 с классом Bitmap:

pictureBox1.Image = new Bitmap(pictureBox1.Width,

pictureBox1.Height);

//Проектируем и в PictureBox рисуем выбранное нами тело:

Designing((Bitmap)pictureBox1.Image);

}

Чтобы мы могли управлять (например, вращать) объектами при помощи нажатия клавиш, желательно в панели Properties на вкладке Events выбрать событие ProcessCmdKey. Если в версии VS, которая имеется у читателя, отсутствует событие ProcessCmdKey или имя этого события скрыто, то необходимо полностью записать нижеследующий метод вместе с шаблоном (или скопировать весь метод из прилагаемого к книге диска).

Листинг 33.2. Метод ProcessCmdKey.

protected override bool ProcessCmdKey(

ref System.Windows.Forms.Message msg,

System.Windows.Forms.Keys keyData)

{

//Задаем угол поворота фигуры после нажатия клавиши:

const float delta_theta = (float)pi / 20; ;

//Рассчитываем новые координаты глаза наблюдателя:

if (keyData == System.Windows.Forms.Keys.Left)

theta_Eye = theta_Eye - delta_theta;

if (keyData == System.Windows.Forms.Keys.Right)

theta_Eye = theta_Eye + delta_theta;

if (keyData == System.Windows.Forms.Keys.Up)

phi_Eye = phi_Eye - delta_theta;

if (keyData == System.Windows.Forms.Keys.Down)

phi_Eye = phi_Eye + delta_theta;

//Проектируем выбранное нами геометрическое тело:

Projection(ref MatrixProjection, PerspectiveProjection,

r_Eye, phi_Eye, theta_Eye,

(float)x_focus, (float)y_focus, (float)z_focus,

0, 1, 0);

Designing((Bitmap)pictureBox1.Image);

//В элементе PictureBox перерисовываем объект:

pictureBox1.Refresh();

return true;

}

Ниже этого кода записываем следующие все методы.

Листинг 33.3. Методы для решения поставленной задачи.

//Проектируем и при помощи процедуры DrawSolid

//рисуем выбранное флажком CheckBox геом-е тело:

private void Designing(Bitmap bmp)

{

//Создаем объект g класса Graphics:

Graphics g;

//Связываем объект g с изображением bmp:

g = Graphics.FromImage(bmp);

//Задаем белый цвет типа Window

//для элемента управления PictureBox1:

g.Clear(SystemColors.Window);

//Высвобождаем ресурсы от графического объекта g:

g.Dispose();

//Преобразуем точки:

TransformAllDataFull(ref MatrixProjection);

//Проектируем и рисуем выбранное на CheckBox тело:

if (checkBox1.CheckState ==

System.Windows.Forms.CheckState.Checked)

{

DrawSolid(

bmp, Tetrahedron, Cube - 1,

System.Drawing.Color.Red, false);

}

if (checkBox2.CheckState ==

System.Windows.Forms.CheckState.Checked)

{

DrawSolid(bmp, Cube, Octahedron - 1,

System.Drawing.Color.Black, false);

}

if (checkBox3.CheckState ==

System.Windows.Forms.CheckState.Checked)

{

DrawSolid(bmp, Octahedron, Dodecahedron - 1,

System.Drawing.Color.Green, false);

}

if (checkBox4.CheckState ==

System.Windows.Forms.CheckState.Checked)

{

DrawSolid(bmp, Dodecahedron, Icosahedron_first - 1,

System.Drawing.Color.Blue, false);

}

if (checkBox5.CheckState ==

System.Windows.Forms.CheckState.Checked)

{

DrawSolid(bmp, Icosahedron_first, Icosahedron_last,

System.Drawing.Color.Orange, false);

}

if (checkBox6.CheckState ==

System.Windows.Forms.CheckState.Checked)

{

DrawSolid(bmp, 1, Tetrahedron - 1,

System.Drawing.Color.Salmon, false);

}

}

//Рассчитываем параметры геометрических тел и осей:

private void СalculateParameters()

{

float theta1; float theta2;

float s1; float s2; float c1; float c2;

float S; float R; float H; float A;

float B; float C; float D; float X;

float Y; float y2; float M; float N;

//Оси координат:

DesigningLine(0, 0, 0, 0.5f, 0, 0); //Ось x.

DesigningLine(0, 0, 0, 0, 0.5f, 0); //Ось y.

DesigningLine(0, 0, 0, 0, 0, 0.5f); //Ось z.

//Тетраэдр (Tetrahedron):

Tetrahedron = NumLines + 1;

S = (float)Math.Sqrt(6);

A = (float)(S / Math.Sqrt(3)); B = -A / 2;

C = (float)(A * Math.Sqrt(2) - 1); D = S / 2;

DesigningLine(0, C, 0, A, -1, 0);

DesigningLine(0, C, 0, B, -1, D);

DesigningLine(0, C, 0, B, -1, -D);

DesigningLine(B, -1, -D, B, -1, D);

DesigningLine(B, -1, D, A, -1, 0);

DesigningLine(A, -1, 0, B, -1, -D);

//Куб (Cube):

Cube = NumLines + 1;

DesigningLine(-1, -1, -1, -1, 1, -1);

DesigningLine(-1, 1, -1, 1, 1, -1);

DesigningLine(1, 1, -1, 1, -1, -1);

DesigningLine(1, -1, -1, -1, -1, -1);

DesigningLine(-1, -1, 1, -1, 1, 1);

DesigningLine(-1, 1, 1, 1, 1, 1);

DesigningLine(1, 1, 1, 1, -1, 1);

DesigningLine(1, -1, 1, -1, -1, 1);

DesigningLine(-1, -1, -1, -1, -1, 1);

DesigningLine(-1, 1, -1, -1, 1, 1);

DesigningLine(1, 1, -1, 1, 1, 1);

DesigningLine(1, -1, -1, 1, -1, 1);

//Октаэдр (Octahedron):

Octahedron = NumLines + 1;

DesigningLine(0, 1, 0, 1, 0, 0);

DesigningLine(0, 1, 0, -1, 0, 0);

DesigningLine(0, 1, 0, 0, 0, 1);

DesigningLine(0, 1, 0, 0, 0, -1);

DesigningLine(0, -1, 0, 1, 0, 0);

DesigningLine(0, -1, 0, -1, 0, 0);

DesigningLine(0, -1, 0, 0, 0, 1);

DesigningLine(0, -1, 0, 0, 0, -1);

DesigningLine(0, 0, 1, 1, 0, 0);

DesigningLine(0, 0, 1, -1, 0, 0);

DesigningLine(0, 0, -1, 1, 0, 0);

DesigningLine(0, 0, -1, -1, 0, 0);

//ДОдекаэдр (Dodecahedron):

Dodecahedron = NumLines + 1;

theta1 = (float)(pi * 0.4); theta2 = (float)(pi * 0.8);

s1 = (float)Math.Sin(theta1);

c1 = (float)Math.Cos(theta1);

s2 = (float)Math.Sin(theta2);

c2 = (float)Math.Cos(theta2);

M = 1 - (2 - 2 * c1 - 4 * s1 * s1) / (2 * c1 - 2);

N = (float)Math.Sqrt((2 - 2 * c1) - M * M) *

(1 + (1 - c2) / (c1 - c2)); R = 2 / N;

S = (float)(R * Math.Sqrt(2 - 2 * c1));

A = R * s1; B = R * s2; C = R * c1; D = R * c2;

H = R * (c1 - s1);

X = (R * R * (2 - 2 * c1) - 4 * A * A) /

(2 * C - 2 * R);

Y = (float)Math.Sqrt(S * S - (R - X) * (R - X));

y2 = Y * (1 - c2) / (c1 - c2);

DesigningLine(R, 1, 0, C, 1, A);

DesigningLine(C, 1, A, D, 1, B);

DesigningLine(D, 1, B, D, 1, -B);

DesigningLine(D, 1, -B, C, 1, -A);

DesigningLine(C, 1, -A, R, 1, 0);

DesigningLine(R, 1, 0, X, 1 - Y, 0);

DesigningLine(C, 1, A, X * c1, 1 - Y, X * s1);

DesigningLine(C, 1, -A, X * c1, 1 - Y, -X * s1);

DesigningLine(D, 1, B, X * c2, 1 - Y, X * s2);

DesigningLine(D, 1, -B, X * c2, 1 - Y, -X * s2);

DesigningLine(X, 1 - Y, 0, -X * c2, 1 - y2, -X * s2);

DesigningLine(X, 1 - Y, 0, -X * c2, 1 - y2, X * s2);

DesigningLine(X * c1, 1 - Y, X * s1,

-X * c2, 1 - y2, X * s2);

DesigningLine(X * c1, 1 - Y, X * s1,

-X * c1, 1 - y2, X * s1);

DesigningLine(X * c2, 1 - Y, X * s2,

-X * c1, 1 - y2, X * s1);

DesigningLine(X * c2, 1 - Y, X * s2, -X, 1 - y2, 0);

DesigningLine(X * c2, 1 - Y, -X * s2, -X, 1 - y2, 0);

DesigningLine(X * c2, 1 - Y, -X * s2,

-X * c1, 1 - y2, -X * s1);

DesigningLine(X * c1, 1 - Y, -X * s1,

-X * c1, 1 - y2, -X * s1);

DesigningLine(X * c1, 1 - Y, -X * s1,

-X * c2, 1 - y2, -X * s2);

DesigningLine(-R, -1, 0, -X, 1 - y2, 0);

DesigningLine(-C, -1, A, -X * c1, 1 - y2, X * s1);

DesigningLine(-D, -1, B, -X * c2, 1 - y2, X * s2);

DesigningLine(-D, -1, -B, -X * c2, 1 - y2, -X * s2);

DesigningLine(-C, -1, -A, -X * c1, 1 - y2, -X * s1);

DesigningLine(-R, -1, 0, -C, -1, A);

DesigningLine(-C, -1, A, -D, -1, B);

DesigningLine(-D, -1, B, -D, -1, -B);

DesigningLine(-D, -1, -B, -C, -1, -A);

DesigningLine(-C, -1, -A, -R, -1, 0);

//Икосаэдр (Icosahedron):

Icosahedron_first = NumLines + 1;

R = (float)(2f / (2f * Math.Sqrt(1 - 2f * c1) +

Math.Sqrt(3f / 4f * (2f - 2f * c1) -

2f * c2 - c2 * c2 - 1f)));

S = R * (float)Math.Sqrt(2 - 2 * c1);

H = 1 - (float)Math.Sqrt(S * S - R * R);

A = R * s1; B = R * s2; C = R * c1; D = R * c2;

DesigningLine(R, H, 0, C, H, A);

DesigningLine(C, H, A, D, H, B);

DesigningLine(D, H, B, D, H, -B);

DesigningLine(D, H, -B, C, H, -A);

DesigningLine(C, H, -A, R, H, 0);

DesigningLine(R, H, 0, 0, 1, 0);

DesigningLine(C, H, A, 0, 1, 0);

DesigningLine(D, H, B, 0, 1, 0);

DesigningLine(D, H, -B, 0, 1, 0);

DesigningLine(C, H, -A, 0, 1, 0);

DesigningLine(-R, -H, 0, -C, -H, A);

DesigningLine(-C, -H, A, -D, -H, B);

DesigningLine(-D, -H, B, -D, -H, -B);

DesigningLine(-D, -H, -B, -C, -H, -A);

DesigningLine(-C, -H, -A, -R, -H, 0);

DesigningLine(-R, -H, 0, 0, -1, 0);

DesigningLine(-C, -H, A, 0, -1, 0);

DesigningLine(-D, -H, B, 0, -1, 0);

DesigningLine(-D, -H, -B, 0, -1, 0);

DesigningLine(-C, -H, -A, 0, -1, 0);

DesigningLine(R, H, 0, -D, -H, B);

DesigningLine(R, H, 0, -D, -H, -B);

DesigningLine(C, H, A, -D, -H, B);

DesigningLine(C, H, A, -C, -H, A);

DesigningLine(D, H, B, -C, -H, A);

DesigningLine(D, H, B, -R, -H, 0);

DesigningLine(D, H, -B, -R, -H, 0);

DesigningLine(D, H, -B, -C, -H, -A);

DesigningLine(C, H, -A, -C, -H, -A);

DesigningLine(C, H, -A, -D, -H, -B);

Icosahedron_last = NumLines;

}

//Объявляем структуру Line и массивы этой структуры:

public struct Line

{

//Объявляем массивы для соединения точек (points):

public float[] fr_points;

public float[] to_points;

//Массивы для соединения преобразованных точек:

//(transformed (tr) points):

public float[] fr_tr_points;

public float[] to_tr_points;

//Создаем и инициализируем массивы, т.е.

//всем пяти элементам каждого массива присваиваем 0:

public void Initialize()

{

fr_points = new float[5];

to_points = new float[5];

fr_tr_points = new float[5];

to_tr_points = new float[5];

}

}

//Объявляем массив Lines структуры Line, оператором new

//создаем массив из 100 элементов и инициализируем его,

//т.е всем элементам этого массива присваиваем значение null:

public Line[] Lines = new Line[100];

//Объявляем и инициализируем переменную для индекса массива:

public int NumLines = 0;

//Проектируем линию между точками (x1,y1,z1),(x2,y2,z2):

public void DesigningLine(float x1, float y1, float z1,

float x2, float y2, float z2)

{

NumLines = NumLines + 1;

//Инициализируем и рассчитываем массив:

Lines[NumLines].Initialize();

Lines[NumLines].fr_points[1] = x1;

Lines[NumLines].fr_points[2] = y1;

Lines[NumLines].fr_points[3] = z1;

Lines[NumLines].fr_points[4] = 1;

Lines[NumLines].to_points[1] = x2;

Lines[NumLines].to_points[2] = y2;

Lines[NumLines].to_points[3] = z2;

Lines[NumLines].to_points[4] = 1;

}

//Применяем матрицу переноса (translation matrix)

//ко всем линиям, используя MatrixApplyFull.

//Преобразование не имеет 0, 0, 0, 1 в последнем столбце:

public void TransformAllDataFull(ref float[,] M)

{

TransformDataFull(ref M, 1, NumLines);

}

//Применяем матрицу переноса (translation matrix)

//ко всем выделенным линиям, используя MatrixApplyFull.

//Преобразование не имеет 0, 0, 0, 1 в последнем столбце:

public void TransformDataFull(ref float[,] M,

int line1, int line2)

{

for (int i = line1; i <= line2; i++)

{

MatrixApplyFull(ref Lines[i].fr_points, ref M,

ref Lines[i].fr_tr_points);

MatrixApplyFull(ref Lines[i].to_points, ref M,

ref Lines[i].to_tr_points);

}

}

//Рисуем выделенные преобразованные линии:

public void DrawSolid(Bitmap bmp,

int first_line, int last_line, Color color, bool clear)

{

float x1, y1, x2, y2;

Graphics g; Pen pen;

//Задаем толщину линии рисования, например, 2

//(цвет линии мы задали в процедуре Designing):

pen = new Pen(color, 2);

//Связываем объект g с изображением bmp:

g = Graphics.FromImage(bmp);

if (clear) g.Clear(System.Drawing.Color.Black);

//Рисуем линии:

for (int i = first_line; i <= last_line; i++)

{

x1 = Lines[i].fr_tr_points[1];

y1 = Lines[i].fr_tr_points[2];

x2 = Lines[i].to_tr_points[1];

y2 = Lines[i].to_tr_points[2];

//Нормализуем и рисуем многогранник:

g.DrawLine(pen,

(x1 * bmp.Width / 4) + bmp.Width / 2.0F,

bmp.Height / 2.0F - (y1 * bmp.Height / 4),

(x2 * bmp.Width / 4) + bmp.Width / 2.0F,

bmp.Height / 2.0F - (y2 * bmp.Height / 4));

}

//Высвобождаем ресурсы от объектов g и pen:

g.Dispose(); pen.Dispose();

}

//Строим единичную матрицу:

public void MatrixIdentity(ref float[,] M)

{

for (int i = 1; i <= 4; i++)

{

for (int j = 1; j <= 4; j++)

{

if (i == j) M[i, j] = 1;

else M[i, j] = 0;

}

}

}

//Строим матрицу преобразования (3-D transformation matrix)

//для перспективной проекции вдоль оси z на плоскость x,y

//с центром объекта (фокусом) в начале координат

//и c центром проецирования на расстоянии (0, 0, Distance):

public void MatrixPerspectiveXZ(ref float[,] M,

float Distance)

{

MatrixIdentity(ref M);

if (Distance != 0) M[3, 4] = -1 / Distance;

}

//Строим матрицу преобразования (3-D transformation matrix)

//для проецирования с координатами:

//центр проецирования (cx, cy, cz), фокус (fx, fy, fx),

//вектор от объекта до экрана UP <ux, yx, uz>,

//тип проецирования (type_of_projection):

//PerspectiveProjection или ParallelProjection:

public void MatrixTransformation(ref float[,] M,

int type_of_projection,

float Cx, float Cy, float Cz,

float Fx, float Fy, float Fz,

float ux, float uy, float uz)

{

float[,] M1 = new float[5, 5];

float[,] M2 = new float[5, 5];

float[,] M3 = new float[5, 5];

float[,] M4 = new float[5, 5];

float[,] M5 = new float[5, 5];

float[,] M12 = new float[5, 5];

float[,] M34 = new float[5, 5];

float[,] M1234 = new float[5, 5];

float sin1 = 0, cos1 = 0; float sin2 = 0, cos2 = 0;

float sin3, cos3; float A, B, C; float d1, d2, d3;

float[] up1 = new float[5]; float[] up2 = new float[5];

//Переносим фокус (центр объекта) в начало координат:

MatrixTranslate(ref M1, -Fx, -Fy, -Fz);

A = Cx - Fx; B = Cy - Fy; C = Cz - Fz;

d1 = (float)Math.Sqrt(A * A + C * C);

if (d1 != 0)

{

sin1 = -A / d1; cos1 = C / d1;

}

d2 = (float)Math.Sqrt(A * A + B * B + C * C);

if (d2 != 0)

{

sin2 = B / d2; cos2 = d1 / d2;

}

//Вращаем объект вокруг оси y, чтобы разместить

//центр проекции в y-z плоскости:

MatrixIdentity(ref M2);

//Если d1 = 0, тогда центр проекции

//уже находится на оси y и в y-z плоскости:

if (d1 != 0)

{

M2[1, 1] = cos1; M2[1, 3] = -sin1;

M2[3, 1] = sin1; M2[3, 3] = cos1;

}

//Вращаем вокруг оси x,

//чтобы разместить центр проекции на оси z:

MatrixIdentity(ref M3);

//Если d2 = 0, то центр проекции

//находится в начале координат.

//Это делает проекцию невозможной:

if (d2 != 0)

{

M3[2, 2] = cos2; M3[2, 3] = sin2;

M3[3, 2] = -sin2; M3[3, 3] = cos2;

}

//Вращаем вектор UP:

up1[1] = ux; up1[2] = uy; up1[3] = uz;

up1[4] = 1;

MatrixApply(ref up1, ref M2, ref up2);

MatrixApply(ref up2, ref M3, ref up1);

//Вращаем вокруг оси z, чтобы разместить

//вектор UP в y-z плоскости:

d3 = (float)Math.Sqrt(up1[1] * up1[1] +

up1[2] * up1[2]);

MatrixIdentity(ref M4);

//Если d3 = 0, то вектор UP равен нулю:

if (d3 != 0)

{

sin3 = up1[1] / d3; cos3 = up1[2] / d3;

M4[1, 1] = cos3; M4[1, 2] = sin3;

M4[2, 1] = -sin3; M4[2, 2] = cos3;

}

//Проецируем:

if (type_of_projection == PerspectiveProjection)

MatrixPerspectiveXZ(ref M5, d2);

else

MatrixIdentity(ref M5);

if (d2 != 0)

MatrixPerspectiveXZ(ref M5, d2);

else

MatrixIdentity(ref M5);

//Комбинируем преобразования:

m3MatMultiply(ref M12, ref M1, ref M2);

m3MatMultiply(ref M34, ref M3, ref M4);

m3MatMultiply(ref M1234, ref M12, ref M34);

if (type_of_projection == PerspectiveProjection)

m3MatMultiplyFull(ref M, ref M1234, ref M5);

else

m3MatMultiply(ref M, ref M1234, ref M5);

}

//Строим матрицу преобразования (3-D transformation matrix)

//для перспективного проецирования (perspective projection):

//центр проецирования (r, phi, theta),

//фокус (fx, fy, fx),

//вектор от объекта до экрана UP <ux, yx, uz>,

//тип проецирования (type_of_projection):

//PerspectiveProjection:

public void Projection(ref float[,] M,

int type_of_projection, float R,

float phi, float theta,

float Fx, float Fy, float Fz,

float ux, float uy, float uz)

{

float Cx, Cy, Cz, r2;

//Переходим к прямоугольным координатам:

Cy = R * (float)Math.Sin(phi);

r2 = R * (float)Math.Cos(phi);

Cx = r2 * (float)Math.Cos(theta);

Cz = r2 * (float)Math.Sin(theta);

MatrixTransformation(ref M, type_of_projection,

Cx, Cy, Cz, Fx, Fy, Fz, ux, uy, uz); //ref M

}

//Строим матрицу преобразования, чтобы получить

//отражение напротив плоскости, проходящей

//через (p1, p2, p3) с вектором нормали <n1, n2, n3>:

public void m3Reflect(ref float[,] M,

float p1, float p2, float p3,

float n1, float n2, float n3)

{

float[,] T = new float[5, 5]; //Перенос.

float[,] R1 = new float[5, 5]; //Вращение 1.

float[,] r2 = new float[5, 5]; //Вращение 2.

float[,] S = new float[5, 5]; //Отражение.

float[,] R2i = new float[5, 5]; //Не вращать 2.

float[,] R1i = new float[5, 5]; //Не вращать 1.

float[,] Ti = new float[5, 5]; //Не переносить.

float D, L;

float[,] M12 = new float[5, 5];

float[,] M34 = new float[5, 5];

float[,] M1234 = new float[5, 5];

float[,] M56 = new float[5, 5];

float[,] M567 = new float[5, 5];

//Переносим плоскость к началу координат:

MatrixTranslate(ref T, -p1, -p2, -p3);

MatrixTranslate(ref Ti, p1, p2, p3);

//Вращаем вокруг оси z,

//пока нормаль не будет в y-z плоскости:

MatrixIdentity(ref R1);

D = (float)Math.Sqrt(n1 * n1 + n2 * n2);

R1[1, 1] = n2 / D; R1[1, 2] = n1 / D;

R1[2, 1] = -R1[1, 2]; R1[2, 2] = R1[1, 1];

MatrixIdentity(ref R1i);

R1i[1, 1] = R1[1, 1]; R1i[1, 2] = -R1[1, 2];

R1i[2, 1] = -R1[2, 1]; R1i[2, 2] = R1[2, 2];

//Вращаем вокруг оси x, когда нормаль будет по оси y:

MatrixIdentity(ref r2);

L = (float)Math.Sqrt(n1 * n1 + n2 * n2 + n3 * n3);

r2[2, 2] = D / L; r2[2, 3] = -n3 / L;

r2[3, 2] = -r2[2, 3]; r2[3, 3] = r2[2, 2];

MatrixIdentity(ref R2i);

R2i[2, 2] = r2[2, 2]; R2i[2, 3] = -r2[2, 3];

R2i[3, 2] = -r2[3, 2]; R2i[3, 3] = r2[3, 3];

//Рисуем отражение объекта перпендикулярно x-z плоскости:

MatrixIdentity(ref S); S[2, 2] = -1;

//Комбинируем матрицы:

m3MatMultiply(ref M12, ref T, ref R1);

m3MatMultiply(ref M34, ref r2, ref S);

m3MatMultiply(ref M1234, ref M12, ref M34);

m3MatMultiply(ref M56, ref R2i, ref R1i);

m3MatMultiply(ref M567, ref M56, ref Ti);

m3MatMultiply(ref M, ref M1234, ref M567);

}

//Строим матрицу преобразования для поворота на угол theta

//вокруг линии, проходящей через (p1, p2, p3)

//в направлении <d1, d2, d3>.

//Угол theta откладывается против часовой стрелки,

//если мы смотрим вниз в направлении,

//противоположном направлению линии:

public void m3LineRotate(ref float[,] M,

float p1, float p2, float p3,

float d1, float d2, float d3, float theta)

{

float[,] T = new float[5, 5]; //Перенос.

float[,] R1 = new float[5, 5]; //Вращение 1.

float[,] r2 = new float[5, 5]; //Вращение 2.

float[,] Rot3 = new float[5, 5]; //Вращение.

float[,] R2i = new float[5, 5]; //Стоп вращению 2.

float[,] R1i = new float[5, 5]; //Стоп вращению 1.

float[,] Ti = new float[5, 5]; //Стоп переносу.

float D, L;

float[,] M12 = new float[5, 5];

float[,] M34 = new float[5, 5];

float[,] M1234 = new float[5, 5];

float[,] M56 = new float[5, 5];

float[,] M567 = new float[5, 5];

//Переносим плоскость к началу координат:

MatrixTranslate(ref T, -p1, -p2, -p3);

MatrixTranslate(ref Ti, p1, p2, p3);

//Вращаем вокруг оси z,

//пока линия не окажется в y-z плоскости:

MatrixIdentity(ref R1);

D = (float)Math.Sqrt(d1 * d1 + d2 * d2);

R1[1, 1] = d2 / D; R1[1, 2] = d1 / D;

R1[2, 1] = -R1[1, 2]; R1[2, 2] = R1[1, 1];

MatrixIdentity(ref R1i);

R1i[1, 1] = R1[1, 1]; R1i[1, 2] = -R1[1, 2];

R1i[2, 1] = -R1[2, 1]; R1i[2, 2] = R1[2, 2];

//Вращаем вокруг оси x, когда линия будет по оси y:

MatrixIdentity(ref r2);

L = (float)Math.Sqrt(d1 * d1 + d2 * d2 + d3 * d3);

r2[2, 2] = D / L; r2[2, 3] = -d3 / L;

r2[3, 2] = -r2[2, 3]; r2[3, 3] = r2[2, 2];

MatrixIdentity(ref R2i);

R2i[2, 2] = r2[2, 2]; R2i[2, 3] = -r2[2, 3];

R2i[3, 2] = -r2[3, 2]; R2i[3, 3] = r2[3, 3];

//Вращаем вокруг линии (оси y):

MatrixYRotate(ref Rot3, theta);

//Комбинируем матрицы:

m3MatMultiply(ref M12, ref T, ref R1);

m3MatMultiply(ref M34, ref r2, ref Rot3);

m3MatMultiply(ref M1234, ref M12, ref M34);

m3MatMultiply(ref M56, ref R2i, ref R1i);

m3MatMultiply(ref M567, ref M56, ref Ti);

m3MatMultiply(ref M, ref M1234, ref M567);

}

//Строим матрицу преобразования (3-D transformation matrix)

//для переноса на Tx, Ty, Tz:

public void MatrixTranslate(ref float[,] M,

float Tx, float Ty, float Tz)

{

MatrixIdentity(ref M);

M[4, 1] = Tx; M[4, 2] = Ty; M[4, 3] = Tz;

}

//Строим матрицу преобразования (3-D transformation matrix)

//для поворота вокруг оси y (угол - в радианах):

public void MatrixYRotate(ref float[,] M, float theta)

{

MatrixIdentity(ref M);

M[1, 1] = (float)Math.Cos(theta);

M[3, 3] = M[1, 1];

M[3, 1] = (float)Math.Sin(theta);

M[1, 3] = -M[3, 1];

}

//Применяем матрицу преобразования к точке,

//где матрица не может иметь 0, 0, 0, 1

//в последнем столбце. Нормализуем только

//x и y компоненты результата, чтобы сохранить z информацию:

public void MatrixApplyFull(ref float[] V, ref float[,] M, ref float[] Result)

{

int i, j; float value = 0;

Result = new float[5] { 0, 0, 0, 0, 0 };

for (i = 1; i <= 4; i++)

{

value = 0;

for (j = 1; j <= 4; j++)

{

value = value + V[j] * M[j, i];

}

Result[i] = value;

}

//Повторно нормализуем точку (value = Result[4]):

if (value != 0)

{

Result[1] = Result[1] / value;

Result[2] = Result[2] / value;

}

else

{

//Не преобразовываем z - составляющую.

//Если значение z больше, чем от центра проекции,

//эта точка будет удалена:

Result[3] = Single.MaxValue;

}

Result[4] = 1;

}

//Применяем матрицу преобразования к точке:

public void MatrixApply(ref float[] V,

ref float[,] M, ref float[] Result)

{

Result[1] = V[1] * M[1, 1] + V[2] * M[2, 1] +

V[3] * M[3, 1] + M[4, 1];

Result[2] = V[1] * M[1, 2] + V[2] * M[2, 2] +

V[3] * M[3, 2] + M[4, 2];

Result[3] = V[1] * M[1, 3] + V[2] * M[2, 3] +

V[3] * M[3, 3] + M[4, 3];

Result[4] = 1;

}

//Умножаем две матрицы. Матрицы

//не могут содержать 0, 0, 0, 1 в последних столбцах:

public void m3MatMultiplyFull(ref float[,] Result,

ref float[,] A, ref float[,] B)

{

int i, j, k; float value; Result = new float[5, 5];

for (i = 1; i <= 4; i++)

{

for (j = 1; j <= 4; j++)

{

value = 0;

for (k = 1; k <= 4; k++)

value = value + A[i, k] * B[k, j];

Result[i, j] = value;

}

}

}

//Умножаем две матрицы:

public void m3MatMultiply(ref float[,] Result,

ref float[,] A, ref float[,] B)

{

Result[1, 1] = A[1, 1] * B[1, 1] + A[1, 2] * B[2, 1]

+ A[1, 3] * B[3, 1];

Result[1, 2] = A[1, 1] * B[1, 2] + A[1, 2] * B[2, 2]

+ A[1, 3] * B[3, 2];

Result[1, 3] = A[1, 1] * B[1, 3] + A[1, 2] * B[2, 3]

+ A[1, 3] * B[3, 3];

Result[1, 4] = 0;

Result[2, 1] = A[2, 1] * B[1, 1] + A[2, 2] * B[2, 1]

+ A[2, 3] * B[3, 1];

Result[2, 2] = A[2, 1] * B[1, 2] + A[2, 2] * B[2, 2]

+ A[2, 3] * B[3, 2];

Result[2, 3] = A[2, 1] * B[1, 3] + A[2, 2] * B[2, 3]

+ A[2, 3] * B[3, 3];

Result[2, 4] = 0;

Result[3, 1] = A[3, 1] * B[1, 1] + A[3, 2] * B[2, 1]

+ A[3, 3] * B[3, 1];

Result[3, 2] = A[3, 1] * B[1, 2] + A[3, 2] * B[2, 2]

+ A[3, 3] * B[3, 2];

Result[3, 3] = A[3, 1] * B[1, 3] + A[3, 2] * B[2, 3]

+ A[3, 3] * B[3, 3];

Result[3, 4] = 0;

Result[4, 1] = A[4, 1] * B[1, 1] + A[4, 2] * B[2, 1]

+ A[4, 3] * B[3, 1] + B[4, 1];

Result[4, 2] = A[4, 1] * B[1, 2] + A[4, 2] * B[2, 2]

+ A[4, 3] * B[3, 2] + B[4, 2];

Result[4, 3] = A[4, 1] * B[1, 3] + A[4, 2] * B[2, 3]

+ A[4, 3] * B[3, 3] + B[4, 3];

Result[4, 4] = 1;

}

Листинг 33.4. Метод для печати изображения с элемента PictureBox.

private void printDocument1_PrintPage(object sender,

System.Drawing.Printing.PrintPageEventArgs e)

{

e.Graphics.DrawImage(pictureBox1.Image, 0, 0);

}


Наши рекомендации