Компьютерная графика, мультимедиа и игры на Visual C#

         

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


Листинг 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);
}

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