Глава 37. Изображение и управление поверхностями в трехмерном пространстве
Листинг 37.1. Код выше и в теле метода Form1_Load.
//Вводим функцию для изображаемой поверхности z = f(x, y):
double f1;
double f(double x, double y)
{
//Параметры поверхности z = f(x, y)для эллипсоида:
double a = 1.03;
double b = 1.02;
double c = 1.01;
//Уравнение поверхности z = f(x, y) в виде эллипсоида:
f1 = Math.Sqrt(c * c * (200 - x * x / (a * a) –
y * y / (b * b)));
return f1;
}
//Точка наблюдения:
myClassPoint3D myEye = new myClassPoint3D();
//Концы числового интервала области задания поверхности:
const int x_max = 20;
const int y_max = 20;
const int x_min = -10;
const int y_min = -10;
//Массив узловых точек:
myClassPoint3D[,] Points = new myClassPoint3D[x_max + 1, y_max + 1];
//Точки на осях координат:
myClassPoint3D[] Axes = new myClassPoint3D[4];
//Загрузка данных и их инициализация:
private void Form1_Load(object sender, EventArgs e)
{
//Задаем координаты точки наблюдения:
myEye = new myClassPoint3D(40, 20, 20);
//Объявляем координаты точки:
double x, y, z; int i, j;
//В каждом (i,j)-м узле плоскости x,y
//рассчитываем координату z точки пов-ти z = f(x, y):
for (i = 0; i <= x_max; i++)
{
x = i + x_min;
for (j = 0; j <= y_max; j++)
{
y = j + y_min;
//Координата z точек пов-ти z = f(x, y):
z = f(x, y);
Points[i, j] = new myClassPoint3D(x, y, z);
}
}
//Инициализация осей (axes) координат:
Axes[0] = new myClassPoint3D(0, 0, 0);
Axes[1] = new myClassPoint3D(30, 0, 0);
Axes[2] = new myClassPoint3D(0, 30, 0);
Axes[3] = new myClassPoint3D(0, 0, 30);
}
Чтобы мы могли рисовать заданную поверхность и оси координат внутри графического элемента PictureBox, в панели Properties (для этого элемента) на вкладке Events дважды щелкаем по имени события Paint. Появляется файл Form1.cs с шаблоном (метода pictureBox1_Paint), который после записи кода принимает такой вид.
Листинг 37.2. Методо pictureBox1_Paint для рисования поверхности.
private void pictureBox1_Paint(object sender,
PaintEventArgs e)
{
//Масштабируем все графические объекты на PictureBox1.
//Коэффициенты масштабирования:
float M_1 = 29; float M_2 = 31;
e.Graphics.ScaleTransform(pictureBox1.Width / M_1,
-pictureBox1.Height / M_2, MatrixOrder.Append);
float M_3 = 2; float M_4 = 2;
e.Graphics.TranslateTransform(pictureBox1.Width / M_3,
pictureBox1.Height / M_4, MatrixOrder.Append);
//Задавая M_1, M_2, M_3, M_4 другие значения,
//мы будем смещать поверхность по отношению к осям x,y,z.
//Матрица преобразования (transformation matrix):
myClassMatrix3D tr = CalculateTransformation();
//Применяем матрицу преобразования к точкам:
for (int x = 0; x <= x_max; x++)
for (int y = 0; y <= y_max; y++)
Points[x, y].Transformation(tr);
//Объявляем индексы массива myArrayVCsharp[i, j]:
int i, j;
//Задаем границы индексов массива myArrayVCsharp[i, j]:
int N_x = 2000; int N_y = 1;
//Задаем массив myArrayVCsharp[i, j]переменных Single,
//когда i = 0,1,2,3,...,N_x; j = 0,1,2,3,...,N_y:
float[,] myArrayVCsharp = new float[N_x + 1, N_y + 1];
//Первая, вторая и третья границы массива, разделяющие
//линии поверхности, параллельные xz, yz, и оси:
int N_1_myArrayCsharp = 0, N_2_myArrayCsharp = 0,
N_3_myArrayCsharp = 0;
//Рассчитываем элементы массива myArrayVCsharp[i, j]
//для рисования линий поверхности,
//параллельных плоскости координат xz:
float x1, y1, x2, y2;
Pen myPen = new Pen(Color.Black, 0);
i = -2;//Задаем до циклов.
for (int x = 0; x <= x_max; x++)
{
x2 =
Convert.ToSingle(Points[x, 0].trans_coord[0]);
y2 =
Convert.ToSingle(Points[x, 0].trans_coord[1]);
for (int y = 1; y <= y_max; y++)
{
x1 = x2; y1 = y2;
x2 =
Convert.ToSingle(Points[x, y].trans_coord[0]);
y2 =
Convert.ToSingle(Points[x, y].trans_coord[1]);
//Можно рисовать линии поверхности и здесь:
//e.Graphics.DrawLine(myPen, x1, y1, x2, y2);
//Записываем в массив координат:
i = i + 2;
myArrayVCsharp[i, 0] = x1;
myArrayVCsharp[i, 1] = y1;
myArrayVCsharp[i + 1, 0] = x2;
myArrayVCsharp[i + 1, 1] = y2;
N_1_myArrayCsharp = i + 1; //1-я гран. в массиве.
}
}
//Рассчитываем элементы массива myArrayVCsharp[i, j]
//для рисования линий поверхности,
//параллельных плоскости координат yz:
i = N_1_myArrayCsharp - 1;
for (int y = 0; y <= y_max; y++)
{
x2 =
Convert.ToSingle(Points[0, y].trans_coord[0]);
y2 =
Convert.ToSingle(Points[0, y].trans_coord[1]);
for (int x = 1; x <= x_max; x++)
{
x1 = x2; y1 = y2;
x2 =
Convert.ToSingle(Points[x, y].trans_coord[0]);
y2 =
Convert.ToSingle(Points[x, y].trans_coord[1]);
//Можно рисовать линии поверхности и здесь:
//e.Graphics.DrawLine(myPen,x1,y1, x2, y2);
//Записываем в массив координат:
i = i + 2;
myArrayVCsharp[i, 0] = x1;
myArrayVCsharp[i, 1] = y1;
myArrayVCsharp[i + 1, 0] = x2;
myArrayVCsharp[i + 1, 1] = y2;
N_2_myArrayCsharp = i + 1; //2-я гран. в массиве.
}
}
//Преобразовываем оси(axes):
for (int k = 0; k <= 3; k++)
Axes[k].Transformation(tr);
//Рассчитываем элементы массива для рисования осей:
Pen myPenAxes = new Pen(Color.Red, 0);
i = N_2_myArrayCsharp - 1;
x1 = Convert.ToSingle(Axes[0].trans_coord[0]);
y1 = Convert.ToSingle(Axes[0].trans_coord[1]);
for (int k = 1; k <= 3; k++)
{
x2 = Convert.ToSingle(Axes[k].trans_coord[0]);
y2 = Convert.ToSingle(Axes[k].trans_coord[1]);
//Можно рисовать оси координат и здесь:
//e.Graphics.DrawLine(myPenAxes, x1, y1, x2, y2);
//Записываем в массив координат:
i = i + 2;
myArrayVCsharp[i, 0] = x1;
myArrayVCsharp[i, 1] = y1;
myArrayVCsharp[i + 1, 0] = x2;
myArrayVCsharp[i + 1, 1] = y2;
N_3_myArrayCsharp = i + 1; //Число чисел в массиве.
}
//Рисуем при помощи массива
//координат myArrayVCsharp[2000, 1].
//Рисуем линии, параллельные плоскости xz:
i = -2;
for (int x = 0; x <= x_max; x++)
{
for (int y = 1; y <= y_max; y++)
{
i = i + 2;
x1 = myArrayVCsharp[i, 0];
y1 = myArrayVCsharp[i, 1];
x2 = myArrayVCsharp[i + 1, 0];
y2 = myArrayVCsharp[i + 1, 1];
e.Graphics.DrawLine(myPen, x1, y1, x2, y2);
}
}
//Рисуем линии, параллельные плоскости yz:
i = N_1_myArrayCsharp - 1;
for (int y = 0; y <= y_max; y++)
{
for (int x = 1; x <= x_max; x++)
{
i = i + 2;
x1 = myArrayVCsharp[i, 0];
y1 = myArrayVCsharp[i, 1];
x2 = myArrayVCsharp[i + 1, 0];
y2 = myArrayVCsharp[i + 1, 1];
e.Graphics.DrawLine(myPen, x1, y1, x2, y2);
}
}
//Рисуем три оси координат:
i = N_2_myArrayCsharp - 1;
Pen myPen2 = new Pen(Color.Red, 0);
for (int k = 1; k <= 3; k++)
{
i = i + 2;
x1 = myArrayVCsharp[i, 0];
y1 = myArrayVCsharp[i, 1];
x2 = myArrayVCsharp[i + 1, 0];
y2 = myArrayVCsharp[i + 1, 1];
e.Graphics.DrawLine(myPen2, x1, y1, x2, y2);
}
//Записываем массив myArrayVCsharp[2000, 2] в файл.
//Создаем объект sw класса StreamWriter
//для записи в файл по адресу D:\MyDocs\MyTest.txt.
//Файл автоматически “опустошается”:
StreamWriter sw =
new StreamWriter("D:\\MyDocs\\MyTest.txt");
//Каждый элемент myArrayVCsharp[i, j] записываем в файл
//в виде отдельной строки при помощи процедуры WriteLine:
for (i = 0; i <= N_x; i++)
for (j = 0; j <= N_y; j++)
sw.WriteLine(myArrayVCsharp[i, j]);
sw.Close();
}//Конец метода pictureBox1_Paint.
Далее, чтобы мы могли управлять (например, вращать) объектами при помощи нажатия клавиш, в панели Properties (для элемента PictureBox) на вкладке Events мы должны дважды щелкнуть по имени события ProcessCmdKey, если это имя там имеется. Если же такого имени там нет, то мы полностью переписываем (или копируем с прилагаемого диска) нижеследующий метод.
Листинг 37.3. Метод ProcessCmdKey для вращения поверхности.
protected override bool ProcessCmdKey(
ref System.Windows.Forms.Message msg,
System.Windows.Forms.Keys keyData)
{
//Задаем шаг поворота поверхности:
const double delta_theta = Math.PI / 32;
const double delta_phi = Math.PI / 16;
//Вычисляем сферич-е коорд-ты (spherical coordinates)
//точки наблюдения:
double theta = Math.Atan2(myEye.orig_coord[1],
myEye.orig_coord[0]);
double r1 = Math.Sqrt(myEye.orig_coord[0] *
myEye.orig_coord[0]
+ myEye.orig_coord[1] * myEye.orig_coord[1]);
double r2 = Math.Sqrt(myEye.orig_coord[0] *
myEye.orig_coord[0]
+ myEye.orig_coord[1] * myEye.orig_coord[1] +
myEye.orig_coord[2] * myEye.orig_coord[2]);
double phi = Math.Atan2(myEye.orig_coord[2], r1);
//Корректируем углы phi и theta:
switch (keyData)
{
case Keys.Left:
theta = theta - delta_theta;
break;
case Keys.Up:
phi = phi - delta_phi;
if (phi < -Math.PI / 2) phi = -Math.PI / 2;
break;
case Keys.Right:
theta = theta + delta_theta;
break;
case Keys.Down:
phi = phi + delta_phi;
if (phi > Math.PI / 2) phi = Math.PI / 2;
break;
}
//Изменяем координаты точки наблюдения:
myEye.orig_coord[0] = r1 * Math.Cos(theta);
myEye.orig_coord[1] = r1 * Math.Sin(theta);
myEye.orig_coord[2] = r2 * Math.Sin(phi);
//Перерисовываем изображение внутри PictureBox1:
pictureBox1.Invalidate();
return true;
}
Ниже записываем следующий метод.
Листинг 37.4. Метод CalculateTransformation.
//Метод для вычисления матрицы преобразования
//для текущей точки наблюдения:
myClassMatrix3D CalculateTransformation()
{
//Поворачиваем вокруг оси z,
//чтобы точка наблюдения оказалась в плоскости y-z:
myClassMatrix3D transformation1 =
myClassMatrix3D.GetZRotPointToYZ(myEye);
//Поворачиваем вокруг оси x,
//чтобы точка наблюдения оказалась на оси z:
myClassMatrix3D transformation2 =
myClassMatrix3D.GetXRotPointToZ(myEye);
//Проецируем по оси z, игнорируя координату z.
//Для этого умножаем матрицы преобразования:
return (transformation1.TimesMatrix(transformation2));
}
Ниже этого автоматически сгенерированного класса Form1:
public partial class Form1 : System.Windows.Forms.Form
{
}
вводим два новых класса с методами преобразования систем координат.
Листинг 37.5. Два новых класса.
//Вводим класс с методами преобразования систем координат
//в трехмерном пространстве:
public class myClassPoint3D
{
//Массив из 4-х элементов для первоначальных координат
//(original coordinates); нулевой индекс используем:
public double[] orig_coord = new double[4];
//Массив для преобразованных координат:
public double[] trans_coord = new double[4];
//Создаем неинициализирующий конструктор этого класса:
public myClassPoint3D()
{
}
//Создаем инициализирующий конструктор:
public myClassPoint3D(double x, double y, double z)
{
orig_coord[0] = x;
orig_coord[1] = y;
orig_coord[2] = z;
double myScale;
myScale = 1; //Масштаб.
orig_coord[3] = myScale;
}
//Матрица преобразования (transformation matrix):
bool normalize = true;
public void Transformation(myClassMatrix3D matrix)
{
double value = 0;
myClassPoint3D result = new myClassPoint3D();
int i, j;
for (i = 0; i <= 3; i++)
{
value = 0;
for (j = 0; j <= 3; j++)
value = value + orig_coord[j] * matrix.M[j, i];
trans_coord[i] = value;
}
//Повторно нормализуем точку:
if (normalize == true)
{
//После выхода из цикла value = trans_coord[4]:
trans_coord[0] = trans_coord[0] / value;
trans_coord[1] = trans_coord[1] / value;
trans_coord[2] = trans_coord[2] / value;
trans_coord[3] = 1;
}
}
} //Конец класса public class myClassPoint3D.
//Вводим класс с методами преобразования координат точки
//в трехмерном пространстве:
public class myClassMatrix3D
{
//Матрица (matrix) в виде массива размера 4x4:
public double[,] M = new double[4, 4];
//Создаем неинициализирующий конструктор этого класса:
public myClassMatrix3D()
{
}
//Создаем конструктор, инициализирующий матрицу:
public myClassMatrix3D(
double m00, double m01, double m02, double m03,
double m10, double m11, double m12, double m13,
double m20, double m21, double m22, double m23,
double m30, double m31, double m32, double m33)
{
M[0, 0] = m00; M[0, 1] = m01;
M[0, 2] = m02; M[0, 3] = m03;
M[1, 0] = m10; M[1, 1] = m11;
M[1, 2] = m12; M[1, 3] = m13;
M[2, 0] = m20; M[2, 1] = m21;
M[2, 2] = m22; M[2, 3] = m23;
M[3, 0] = m30; M[3, 1] = m31;
M[3, 2] = m32; M[3, 3] = m33;
}
//Метод для умножения матрицы на матрицу справа:
public myClassMatrix3D TimesMatrix(
myClassMatrix3D right_matrix)
{
myClassMatrix3D result = new myClassMatrix3D();
double value; int i, j, k;
for (i = 0; i <= 3; i++)
{
for (j = 0; j <= 3; j++)
{
value = 0;
for (k = 0; k <= 3; k++)
{
value = value + M[i, k] *
right_matrix.M[k, j];
}
result.M[i, j] = value;
}
}
return result;
}
//Метод для поворота вокруг оси z
//до точки в y-z плоскости:
public static myClassMatrix3D GetZRotPointToYZ(
myClassPoint3D pt)
{
double R = Math.Sqrt(
pt.orig_coord[0] * pt.orig_coord[0] +
pt.orig_coord[1] * pt.orig_coord[1]);
double stheta = pt.orig_coord[0] / R;
double ctheta = pt.orig_coord[1] / R;
return new myClassMatrix3D(
ctheta, stheta, 0, 0,
-stheta, ctheta, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
//Метод для поворота вокруг оси x до точки на оси z:
public static myClassMatrix3D
GetXRotPointToZ(myClassPoint3D pt)
{
double R1 = Math.Sqrt(
pt.orig_coord[0] * pt.orig_coord[0] +
pt.orig_coord[1] * pt.orig_coord[1]);
double R2 = Math.Sqrt(
pt.orig_coord[0] * pt.orig_coord[0] +
pt.orig_coord[1] * pt.orig_coord[1] +
pt.orig_coord[2] * pt.orig_coord[2]);
double sphi = -R1 / R1;
double cphi = -pt.orig_coord[2] / R2;
return new myClassMatrix3D(
1, 0, 0, 0,
0, cphi, sphi, 0,
0, -sphi, cphi, 0,
0, 0, 0, 1);
}
} //Конец класса class myClassMatrix3D.
Глава 38. Изображение и управление поверхностями в трехмерном пространстве на Visual Basic для интеграции с Visual C#, Visual C++ и другими языками
Листинг 38.1. Код выше и в теле процедуры Form1_Load.
'Вводим функцию для изображаемой поверхности z = f(x, y):
Public Function f(ByVal x As Double, ByVal y As Double) _
As Double
'Параметры поверхности z = f(x, y)для эллипсоида:
Dim a As Double = 1.03
Dim b As Double = 1.02
Dim c As Double = 1.01
'Уравнение поверхности z = f(x, y) в виде эллипсоида:
f = Sqrt(c * c * (200 - x * x / (a * a) - _
y * y / (b * b)))
End Function
'Точка наблюдения:
Private myEye As myClassPoint3D
'Концы числового интервала области задания поверхности:
Private Const x_max As Integer = 20
Private Const y_max As Integer = 20
Private Const x_min As Integer = -10
Private Const y_min As Integer = -10
'Массив узловых точек:
Private Points(x_max, y_max) As myClassPoint3D
'Точки на осях координат:
Private Axes(3) As myClassPoint3D
'Загрузка данных и их инициализация:
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As EventArgs) Handles MyBase.Load
'Задаем координаты точки наблюдения:
myEye = New myClassPoint3D(40, 20, 20)
'Объявляем координаты точки:
Dim x, y, z As Double : Dim i, j As Integer
'В каждом (i,j)-м узле плоскости x,y
'рассчитываем координату z точки поверхности z = f(x, y):
For i = 0 To x_max
x = i + x_min
For j = 0 To y_max
y = j + y_min
'Координата z точек поверхности z = f(x, y):
z = f(x, y)
Points(i, j) = New myClassPoint3D(x, y, z)
Next
Next
'Инициализация осей (axes) координат:
Axes(0) = New myClassPoint3D(0, 0, 0) 'Начало коорд-т.
Axes(1) = New myClassPoint3D(30, 0, 0) 'Ось (axis) x.
Axes(2) = New myClassPoint3D(0, 30, 0) 'Ось y.
Axes(3) = New myClassPoint3D(0, 0, 30) 'Ось z.
End Sub
Чтобы мы могли рисовать заданную поверхность и оси координат внутри графического элемента PictureBox, в окне Class Name выбираем PictureBox1, а в окне Method Name выбираем Paint. Появляется файл Form1.vb с шаблоном (процедуры PictureBox1_Paint), который после записи нашего кода принимает такой вид.
Листинг 38.2. Метод PictureBox1_Paint для рисования поверхности.
Private Sub PictureBox1_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles PictureBox1.Paint
'Масштабируем все графические объекты на PictureBox1.
'Коэффициенты масштабирования:
Dim M_1 As Single = 29
Dim M_2 As Single = 31
e.Graphics.ScaleTransform( _
PictureBox1.Size.Width / M_1, _
-PictureBox1.Size.Height / M_2, _
MatrixOrder.Append)
Dim M_3 As Single = 2
Dim M_4 As Single = 2
e.Graphics.TranslateTransform( _
PictureBox1.Size.Width / M_3, _
PictureBox1.Size.Height / M_4, _
MatrixOrder.Append)
'Задавая M_1, M_2, M_3, M_4 другие значения,
'мы будем смещать поверхность по отношению к осям x,y,z.
'Матрица преобразования (transformation matrix):
Dim tr As myClassMatrix3D = CalculateTransformation()
'Применяем матрицу преобразования к точкам:
For x As Integer = 0 To x_max
For y As Integer = 0 To y_max
Points(x, y).Transformation(tr)
Next
Next
'Объявляем индексы элементов массива myArrayVB(i, j):
Dim i, j As Integer
'Задаем границы индексов массива myArrayVB(i, j):
Dim N_x As Integer = 2000
Dim N_y As Integer = 1
'Задаем массив myArrayVB(i, j)переменных типа Single,
'когда i = 0,1,2,3,...,N_x; j = 0,1,2,3,...,N_y:
Dim myArrayVB(N_x, N_y) As Single
'Первая, вторая и третья границы массива, разделяющие
'линии поверхности, параллельные xz, yz, и оси:
Dim N_1_myArrayVB, N_2_myArrayVB, _
N_3_myArrayVB As Integer
'Рассчитываем элементы массива myArrayVB(i, j)
'для рисования линий поверхности,
'параллельных плоскости координат xz:
Dim x1, y1, x2, y2 As Single
i = -2 'Задаем до циклов.
For x As Integer = 0 To x_max
With Points(x, 0)
x2 = .trans_coord(0)
y2 = .trans_coord(1)
End With
For y As Integer = 1 To y_max
x1 = x2 : y1 = y2
With Points(x, y)
x2 = .trans_coord(0)
y2 = .trans_coord(1)
End With
'Можно рисовать линии поверхности и здесь:
'e.Graphics.DrawLine(myPen, x1, y1, x2, y2)
'Записываем в массив координат:
i = i + 2
myArrayVB(i, 0) = x1
myArrayVB(i, 1) = y1
myArrayVB(i + 1, 0) = x2
myArrayVB(i + 1, 1) = y2
N_1_myArrayVB = i + 1 'Первая граница в массиве.
Next
Next
'Рассчитываем элементы массива myArrayVB(i, j)
'для рисования линий поверхности,
'параллельных плоскости координат yz:
i = N_1_myArrayVB - 1
For y As Integer = 0 To y_max
With Points(0, y)
x2 = .trans_coord(0)
y2 = .trans_coord(1)
End With
For x As Integer = 1 To x_max
x1 = x2 : y1 = y2
With Points(x, y)
x2 = .trans_coord(0)
y2 = .trans_coord(1)
End With
'Можно рисовать линии поверхности и здесь:
'e.Graphics.DrawLine(myPen, x1, y1, x2, y2)
'Записываем в массив координат:
i = i + 2
myArrayVB(i, 0) = x1
myArrayVB(i, 1) = y1
myArrayVB(i + 1, 0) = x2
myArrayVB(i + 1, 1) = y2
N_2_myArrayVB = i + 1 'Вторая граница в массиве.
Next
Next
'Преобразовываем оси(axes):
For k As Integer = 0 To 3
Axes(k).Transformation(tr)
Next
'Рассчитываем элементы массива для рисования осей:
i = N_2_myArrayVB - 1
x1 = Axes(0).trans_coord(0)
y1 = Axes(0).trans_coord(1)
For k As Integer = 1 To 3
x2 = Axes(k).trans_coord(0)
y2 = Axes(k).trans_coord(1)
'Можно рисовать оси координат и здесь:
'e.Graphics.DrawLine(myPen, x1, y1, x2, y2)
'Записываем в массив координат:
i = i + 2
myArrayVB(i, 0) = x1
myArrayVB(i, 1) = y1
myArrayVB(i + 1, 0) = x2
myArrayVB(i + 1, 1) = y2
N_3_myArrayVB = i + 1 'Число всех чисел в массиве.
Next
'Рисуем при помощи массива координат myArrayVB(2000, 1).
'Рисуем линии, параллельные плоскости xz:
Dim myPen As New Pen(Color.Black, 0)
i = -2
For x As Integer = 0 To x_max
For y As Integer = 1 To y_max
i = i + 2
x1 = myArrayVB(i, 0)
y1 = myArrayVB(i, 1)
x2 = myArrayVB(i + 1, 0)
y2 = myArrayVB(i + 1, 1)
e.Graphics.DrawLine(myPen, x1, y1, x2, y2)
Next
Next
'Рисуем линии, параллельные плоскости yz:
i = N_1_myArrayVB - 1
For y As Integer = 0 To y_max
For x As Integer = 1 To x_max
i = i + 2
x1 = myArrayVB(i, 0)
y1 = myArrayVB(i, 1)
x2 = myArrayVB(i + 1, 0)
y2 = myArrayVB(i + 1, 1)
e.Graphics.DrawLine(myPen, x1, y1, x2, y2)
Next
Next
'Рисуем три оси координат:
i = N_2_myArrayVB - 1
Dim myPen2 As New Pen(Color.Red, 0)
For k As Integer = 1 To 3
i = i + 2
x1 = myArrayVB(i, 0)
y1 = myArrayVB(i, 1)
x2 = myArrayVB(i + 1, 0)
y2 = myArrayVB(i + 1, 1)
e.Graphics.DrawLine(myPen2, x1, y1, x2, y2)
Next
'Записываем массив координат myArrayVB(2000, 1) в файл.
'Создаем объект sw класса StreamWriter
'для записи в файл по адресу D:\MyDocs\MyTest.txt.
'Файл автоматически “опустошается”:
Dim sw As StreamWriter = _
New StreamWriter("D:\MyDocs\MyTest.txt")
'Каждый элемент массива myArrayVB(i, j) записываем в файл
'в виде отдельной строки при помощи процедуры WriteLine:
For i = 0 To N_x
For j = 0 To N_y
sw.WriteLine(myArrayVB(i, j))
Next
Next
sw.Close()
End Sub
Чтобы мы могли управлять (например, вращать) объектами при помощи нажатия клавиш, в окне Class Name выбираем (Overrides), а в окне Method Name выбираем ProcessCmdKey. Появляется файл Form1.vb с шаблоном (процедуры ProcessCmdKey), который после записи нашего кода принимает следующий вид.
Листинг 38.3. Метод ProcessCmdKey для вращения поверхности.
'Поворачиваем поверхность после нажатия клавиши:
Protected Overrides Function ProcessCmdKey( _
ByRef msg As System.Windows.Forms.Message, _
ByVal keyData As System.Windows.Forms.Keys) As Boolean
'Задаем шаг поворота поверхности:
Const delta_theta As Double = PI / 32
Const delta_phi As Double = PI / 16
'Вычисляем сферические координаты (spherical coordinates)
'точки наблюдения:
Dim theta As Double = Atan2(myEye.orig_coord(1), _
myEye.orig_coord(0))
Dim r1 As Double = Sqrt(myEye.orig_coord(0) * _
myEye.orig_coord(0) _
+ myEye.orig_coord(1) * myEye.orig_coord(1))
Dim r2 As Double = Sqrt(myEye.orig_coord(0) * _
myEye.orig_coord(0) _
+ myEye.orig_coord(1) * myEye.orig_coord(1) + _
myEye.orig_coord(2) * myEye.orig_coord(2))
Dim phi As Double = Atan2(myEye.orig_coord(2), r1)
'Корректируем углы phi и theta:
Select Case keyData
Case Keys.Left
theta = theta - delta_theta
Case Keys.Up
phi = phi - delta_phi
If phi < -PI / 2 Then phi = -PI / 2
Case Keys.Right
theta = theta + delta_theta
Case Keys.Down
phi = phi + delta_phi
If phi > PI / 2 Then phi = PI / 2
Case Else
Exit Function
End Select
'Изменяем координаты точки наблюдения:
myEye.orig_coord(0) = r1 * Cos(theta)
myEye.orig_coord(1) = r1 * Sin(theta)
myEye.orig_coord(2) = r2 * Sin(phi)
'Перерисовываем изображение внутри PictureBox1:
PictureBox1.Invalidate()
End Function
Ниже этого кода записываем следующую функцию.
Листинг 38.4. Метод CalculateTransformation.
'Вычисляем матрицу преобразования
'для текущей точки наблюдения:
Private Function CalculateTransformation() As myClassMatrix3D
'Поворачиваем вокруг оси z,
'чтобы точка наблюденияоказалась в плоскости y-z:
Dim transformation1 As myClassMatrix3D = _
myClassMatrix3D.GetZRotPointToYZ(myEye)
'Поворачиваем вокруг оси x,
'чтобы точка наблюденияоказалась на оси z:
Dim transformation2 As myClassMatrix3D = _
myClassMatrix3D.GetXRotPointToZ(myEye)
'Проецируем по оси z, игнорируя координату z.
'Умножаем матрицы преобразования:
Return transformation1.TimesMatrix(transformation2)
End Function
Ниже этого автоматически сгенерированного класса Form1:
Public Class Form1
Inherits System.Windows.Forms.Form
…
End Class
вводим два новых класса с методами преобразования систем координат.
Листинг 38.5. Два новых класса.
'Вводим класс с методами преобразования систем координат
'в трехмерном пространстве:
Public Class myClassPoint3D
'Массив из 4-х элементов для первоначальных координат
'(original coordinates); нулевой индекс используем:
Public orig_coord(3) As Double
'Массив для преобразованных координат
'(transformed coordinates):
Public trans_coord(3) As Double
'Создаем новый неинициализированный вектор:
Public Sub New()
End Sub
'Создаем новый инициализированный вектор:
Public Sub New(ByVal x As Double, ByVal y As Double, _
ByVal z As Double, Optional ByVal myScale As Double = 1)
orig_coord(0) = x
orig_coord(1) = y
orig_coord(2) = z
orig_coord(3) = myScale
End Sub
'Матрица преобразования (transformation matrix):
Public Function Transformation( _
ByVal matrix As myClassMatrix3D, _
Optional ByVal normalize As Boolean = True) _
As myClassPoint3D
Dim value As Double
Dim result As New myClassPoint3D
For i As Integer = 0 To 3
value = 0
For j As Integer = 0 To 3
value = value + _
orig_coord(j) * matrix.M(j, i)
Next
trans_coord(i) = value
Next
'Повторно нормализуем точку:
If normalize Then
'После выхода из цикла value = trans_coord(3):
trans_coord(0) = trans_coord(0) / value
trans_coord(1) = trans_coord(1) / value
trans_coord(2) = trans_coord(2) / value
trans_coord(3) = 1
End If
End Function
End Class
'Вводим класс с методами преобразования координатточки
'в трехмерном пространстве:
Public Class myClassMatrix3D
'Матрица (matrix) в виде массива размера 4x4:
Public M(3, 3) As Double
'Создаем новую неинициализированную матрицу:
Public Sub New()
End Sub
'Создаем новую инициализированную матрицу:
Public Sub New( _
ByVal m00 As Double, ByVal m01 As Double, _
ByVal m02 As Double, ByVal m03 As Double, _
ByVal m10 As Double, ByVal m11 As Double, _
ByVal m12 As Double, ByVal m13 As Double, _
ByVal m20 As Double, ByVal m21 As Double, _
ByVal m22 As Double, ByVal m23 As Double, _
ByVal m30 As Double, ByVal m31 As Double, _
ByVal m32 As Double, ByVal m33 As Double)
M(0, 0) = m00 : M(0, 1) = m01 : M(0, 2) = m02
M(0, 3) = m03
M(1, 0) = m10 : M(1, 1) = m11 : M(1, 2) = m12
M(1, 3) = m13
M(2, 0) = m20 : M(2, 1) = m21 : M(2, 2) = m22
M(2, 3) = m23
M(3, 0) = m30 : M(3, 1) = m31 : M(3, 2) = m32
M(3, 3) = m33
End Sub
'Умножение матрицы на матрицу справа:
Public Function TimesMatrix( _
ByVal right_matrix As myClassMatrix3D) As myClassMatrix3D
Dim result As New myClassMatrix3D
Dim value As Double
For i As Integer = 0 To 3
For j As Integer = 0 To 3
value = 0
For k As Integer = 0 To 3
value = value + M(i, k) * _
right_matrix.M(k, j)
Next
result.M(i, j) = value
Next
Next
Return result
End Function
'Поворот вокруг оси z до точки в y-z плоскости:
Public Shared Function GetZRotPointToYZ( _
ByVal pt As myClassPoint3D) As myClassMatrix3D
Dim R As Double = Sqrt(pt.orig_coord(0) * _
pt.orig_coord(0) + _
pt.orig_coord(1) * pt.orig_coord(1))
Dim stheta As Double = pt.orig_coord(0) / R
Dim ctheta As Double = pt.orig_coord(1) / R
Return New myClassMatrix3D( _
ctheta, stheta, 0, 0, _
-stheta, ctheta, 0, 0, _
0, 0, 1, 0, _
0, 0, 0, 1)
End Function
'Поворот вокруг оси x до точки на оси z:
Public Shared Function GetXRotPointToZ( _
ByVal pt As myClassPoint3D) As myClassMatrix3D
Dim R1 As Double = Sqrt(pt.orig_coord(0) * _
pt.orig_coord(0) + pt.orig_coord(1) * _
pt.orig_coord(1))
Dim R2 As Double = Sqrt(pt.orig_coord(0) * _
pt.orig_coord(0) + pt.orig_coord(1) * _
pt.orig_coord(1) + pt.orig_coord(2) * _
pt.orig_coord(2))
Dim sphi As Double = -R1 / R1
Dim cphi As Double = -pt.orig_coord(2) / R2
Return New myClassMatrix3D( _
1, 0, 0, 0, _
0, cphi, sphi, 0, _
0, -sphi, cphi, 0, _
0, 0, 0, 1)
End Function
End Class
Аналогично можно записать массивы с координатами точек для нескольких геометрических изображений в несколько различных файлов на жестком диске компьютера. И далее в проекте на Visual C#, Visual C++ 2005 (или другом языке) считывать массивы с координатами точек и выводить эти изображения на экран монитора, а также печатать их на принтере, как будет показано в следующей главе.
Глава 39. Изображение поверхностей в трехмерном пространстве на основе нового синтаксиса Visual C++ 2005
Листинг 39.1. Метод для рисования поверхности.
//Концы числового интервала области задания поверхно-сти:
private: static const int x_max = 20;
private: static const int y_max = 20;
private: static const int x_min = -10;
private: static const int y_min = -10;
private: System::Void pictureBox1_Paint(
System::Object^ sender,
System::Windows::Forms::PaintEventArgs^ e)
{
//Масштабируем объекты класса Graphics на pictureBox1.
//Коэффициенты масштабирования:
float M_1 = 31; float M_2 = 29;
e->Graphics->ScaleTransform(
Convert::ToSingle(pictureBox1->Width / M_1),
Convert::ToSingle(-pictureBox1->Height / M_2),
MatrixOrder::Append);
float M_3 = 1.9f;
float M_4 = 1.7f;
e->Graphics->TranslateTransform(
Convert::ToSingle(pictureBox1->Width / M_3),
Convert::ToSingle(pictureBox1->Height / M_4),
MatrixOrder::Append);
//Задавая M_1, M_2, M_3, M_4 другие значения,
//мы будем смещать пов-ть по отношению к осям x,y,z.
//Объявляем индексы элементов массива myArrayVC(i, j):
int i, j;
//Значение первой, второй и третьей границ индекса "i":
int N_1_myArray, N_2_myArray, N_3_myArray;
//Задаем границы индексов массива myArrayVC(i, j):
int N_x = 2001;
int N_y = 2;
//Объявляем массив myArrayVC(i, j) переменных float,
//когда i = 0,1,2,3,...,(N_x - 1);
// j = 0,1,2,3,...,(N_y - 1):
array<float,2>^ myArrayVC =
gcnew array<float,2>(N_x, N_y);
//Для считывания из файла D:\\MyDocs\\MyTest.txt
//координат изображения в массив myArrayVC[2001, 2]
//создаем объект sr класса StreamReader:
String^ path = "D:\\MyDocs\\MyTest.txt";
StreamReader^ sr =
gcnew StreamReader(path);
//Считываем из файла MyTest.txt координаты изображения
//в массив myArrayVC(2001, 2) при помощи ReadLine:
for (i = 0; i <= N_x - 1; i++)
for (j = 0; j <= N_y - 1; j++)
myArrayVC[i, j] =
Convert::ToSingle(sr->ReadLine());
sr->Close();
//Рисуем поверхность z=f(x,y) из массива.
//Объявляем координаты двух точек:
float x1, y1, x2, y2;
//Будем рисовать пером myPen толщиной 0:
Pen^ myPen = gcnew Pen(Color::Black, 0);
//Рисуем линии поверхности, параллельные плоскости xz:
i = -2;
for (int x = 0; x <= x_max; x++)
{
for (int y = 1; y <= y_max; y++)
{
i = i + 2;
x1 = myArrayVC[i, 0];
y1 = myArrayVC[i, 1];
x2 = myArrayVC[i + 1, 0];
y2 = myArrayVC[i + 1, 1];
e->Graphics->DrawLine(myPen, x1, y1, x2, y2);
}
}
N_1_myArray = i + 1; //Первая граница массива.
//Рисуем линии поверхности, параллельные плоскости yz:
i = N_1_myArray - 1;
for (int y = 0; y <= y_max; y++)
{
for (int x = 1; x <= x_max; x++)
{
i = i + 2;
x1 = myArrayVC[i, 0];
y1 = myArrayVC[i, 1];
x2 = myArrayVC[i + 1, 0];
y2 = myArrayVC[i + 1, 1];
e->Graphics->DrawLine(myPen, x1, y1, x2, y2);
}
}
N_2_myArray = i + 1; //Вторая граница массива.
//Рисуем оси координат:
Pen^ myPen2 = gcnew Pen(Color::Red, 0);
i = N_2_myArray - 1;
for (int k = 1; k <= 3; k++)
{
i = i + 2;
x1 = myArrayVC[i, 0];
y1 = myArrayVC[i, 1];
x2 = myArrayVC[i + 1, 0];
y2 = myArrayVC[i + 1, 1];
e->Graphics->DrawLine(myPen2, x1, y1, x2, y2);
N_3_myArray = i + 1; //Третья граница массива.
}
}
Интеграция Visual C# с Visual Basic, Visual C++ 2005 и другими языками состоит в том, что в одном из проектов на Visual C#, Visual Basic, Visual C++ 2005 или другом языке координаты геометрических изображений сначала записываются в файлы на жестком диске компьютера (например, в текстовый файл D:\MyDocs\MyTest.txt), а затем в другом проекте на любом языке эти координаты считываются из файлов в массивы с последующим построением геометрических изображений по данным этих массивов.
Аналогично по этой методологии мы можем разработать другие приложения для построения различных пространственных изображений.