Глава 34. Изображение и управление трехмерными объектами в трехмерном пространстве на Visual Basic для интеграции с Visual C# и другими языками
Листинг 34.1. Код выше и в теле процедуры Form1_Load.
'Начало координат:
Private Const x_focus As Double = 0
Private Const y_focus As Double = 0
Private Const z_focus As Double = 0
'Сферические координаты точки E (глаза наблюдателя Eye):
Private r_Eye As Single
Private phi_Eye As Single
Private theta_Eye As Single
'Объявляем матрицу (как массив) и переменные
'(во всех массивах нулевые индексы не используем):
Private Const pi As Double = Math.PI
Private MatrixProjection(4, 4) As Single
Private Tetrahedron As Integer
Private Cube As Integer
Private Octahedron As Integer
Private Dodecahedron As Integer
Private Icosahedron_first As Integer
Private Icosahedron_last As Integer
'Для параллельного проецирования объекта на экран
'(parallel projection) задаем константу:
Private Const ParallelProjection As Integer = 0
'Для перспективного проецирования объекта на экран
'(perspective projection)задаем константу:
Private Const PerspectiveProjection As Integer = 1
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As EventArgs) Handles MyBase.Load
'Задаем координаты глаза наблюдателя, например:
r_Eye = 4 : phi_Eye = 0.05 * pi : theta_Eye = 0.3 * pi
'Вызываем процедуру для перспективного проецирования,
'когда type_of_projection = PerspectiveProjection
'(для параллельного проецирования вместо
'PerspectiveProjection пишем ParallelProjection):
Projection(MatrixProjection, PerspectiveProjection, _
r_Eye, phi_Eye, theta_Eye, _
x_focus, y_focus, z_focus, 0, 1, 0)
'Рассчитываем параметры геометрического тела:
СalculateParameters()
'Связываем элемент PictureBox1 с классом Bitmap:
PictureBox1.Image = New Bitmap(PictureBox1.Width, _
PictureBox1.Height)
'Проектируем и в PictureBox1 рисуем выбранное нами тело:
Designing(DirectCast(PictureBox1.Image, Bitmap))
End Sub
Чтобы мы могли управлять (например, вращать) объектами при помощи нажатия клавиш, в окне Class Name выбираем (Overrides), а в окне Method Name выбираем ProcessCmdKey. Появляется файл Form1.vb с шаблоном (метода ProcessCmdKey), который после записи нашего кода принимает следующий вид. Отметим, что если в версии VS, которая имеется у читателя, отсутствует метод ProcessCmdKey, то необходимо полностью записать нижеследующий метод вместе с шаблоном (или скопировать весь метод из прилагаемого к книге диска).
Листинг 34.2. Метод 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 Single = pi / 20
Select Case keyData
Case System.Windows.Forms.Keys.Left
theta_Eye = theta_Eye - delta_theta
Case System.Windows.Forms.Keys.Right
theta_Eye = theta_Eye + delta_theta
Case System.Windows.Forms.Keys.Up
phi_Eye = phi_Eye - delta_theta
Case System.Windows.Forms.Keys.Down
phi_Eye = phi_Eye + delta_theta
Case Else
Return MyBase.ProcessCmdKey(msg, keyData)
End Select
Projection(MatrixProjection, PerspectiveProjection, _
r_Eye, phi_Eye, theta_Eye, _
x_focus, y_focus, z_focus, 0, 1, 0)
'В элементе PictureBox1 перерисовываем объект:
Designing(DirectCast(PictureBox1.Image, Bitmap))
PictureBox1.Refresh()
Return True
End Function
Ниже этого кода записываем следующие все процедуры и функции.
Листинг 34.3. Процедуры и функции.
'Проектируем и при помощи процедуры DrawSolid
'рисуем выбранное флажком CheckBox геометрическое тело:
Private Sub Designing(ByVal bmp As Bitmap)
'Создаем объект g класса Graphics:
Dim g As Graphics
'Связываем объект g с изображением bmp:
g = Graphics.FromImage(bmp)
'Задаем белый цвет типа Window
'для элемента управления PictureBox1:
g.Clear(SystemColors.Window)
'Высвобождаем ресурсы от графического объекта g:
g.Dispose()
'Преобразуем точки:
TransformAllDataFull(MatrixProjection)
'Проектируем и рисуем выбранное на CheckBox тело:
If CheckBox1.CheckState = _
System.Windows.Forms.CheckState.Checked Then
DrawSolid(bmp, Tetrahedron, Cube - 1, _
System.Drawing.Color.Red, False)
End If
If CheckBox2.CheckState = _
System.Windows.Forms.CheckState.Checked Then _
DrawSolid(bmp, Cube, Octahedron - 1, _
System.Drawing.Color.Black, False)
If CheckBox3.CheckState = _
System.Windows.Forms.CheckState.Checked Then _
DrawSolid(bmp, Octahedron, Dodecahedron - 1, _
System.Drawing.Color.Green, False)
If CheckBox4.CheckState = _
System.Windows.Forms.CheckState.Checked Then _
DrawSolid(bmp, Dodecahedron, Icosahedron_first - 1, _
System.Drawing.Color.Blue, False)
If CheckBox5.CheckState = _
System.Windows.Forms.CheckState.Checked Then _
DrawSolid(bmp, Icosahedron_first, Icosahedron_last, _
System.Drawing.Color.Orange, False)
If CheckBox6.CheckState = _
System.Windows.Forms.CheckState.Checked Then _
DrawSolid(bmp, 1, Tetrahedron - 1, _
System.Drawing.Color.Salmon, False)
End Sub
'Рассчитываем параметры геометрических тел и осей:
Private Sub СalculateParameters()
Dim theta1 As Single : Dim theta2 As Single
Dim s1 As Single : Dim s2 As Single
Dim c1 As Single : Dim c2 As Single
Dim S As Single : Dim R As Single
Dim H As Single : Dim A As Single
Dim B As Single : Dim C As Single
Dim D As Single : Dim X As Single
Dim Y As Single : Dim y2 As Single
Dim M As Single : Dim N As Single
'Оси координат:
DesigningLine(0, 0, 0, 0.5, 0, 0) 'Ось x.
DesigningLine(0, 0, 0, 0, 0.5, 0) 'Ось y.
DesigningLine(0, 0, 0, 0, 0, 0.5) 'Ось z.
'Тетраэдр (Tetrahedron):
Tetrahedron = NumLines + 1
S = CSng(Sqrt(6))
A = S / CSng(Sqrt(3))
B = -A / 2
C = A * CSng(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 = pi * 0.4 : theta2 = pi * 0.8
s1 = CSng(Sin(theta1))
c1 = CSng(Cos(theta1))
s2 = CSng(Sin(theta2))
c2 = CSng(Cos(theta2))
M = 1 - (2 - 2 * c1 - 4 * s1 * s1) / (2 * c1 - 2)
N = CSng(Sqrt((2 - 2 * c1) - M * M)) * _
(1 + (1 - c2) / (c1 - c2)) : R = 2 / N
S = R * CSng(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 = CSng(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 = 2 / (2 * CSng(Sqrt(1 - 2 * c1)) + _
CSng(Sqrt(3 / 4 * (2 - 2 * c1) - _
2 * c2 - c2 * c2 - 1)))
S = R * CSng(Sqrt(2 - 2 * c1))
H = 1 - CSng(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
End Sub
Public Structure Line
'Массивы для соединения точек (points):
<VBFixedArray(4)> Dim fr_points() As Single
<VBFixedArray(4)> Dim to_points() As Single
'Массивы для соединения преобразованных точек
'(transformed points):
<VBFixedArray(4)> Dim fr_tr_points() As Single
<VBFixedArray(4)> Dim to_tr_points() As Single
Public Sub Initialize()
ReDim fr_points(4) : ReDim to_points(4)
ReDim fr_tr_points(4) : ReDim to_tr_points(4)
End Sub
End Structure
'Объявляем массив Lines структуры Line (создавать массив
'из изменяемого количества элементов и инициализировать его 'при помощи оператора ReDim мы будем ниже):
Public Lines() As Line
'Объявляем и инициализируем переменную для индекса массива:
Public NumLines As Integer
'Проектируем линию между точками (x1,y1,z1),(x2,y2,z2):
Public Sub DesigningLine(ByVal x1 As Single, _
ByVal y1 As Single, ByVal z1 As Single, ByVal x2 As Single, _
ByVal y2 As Single, ByVal z2 As Single)
NumLines = NumLines + 1
'Создаем массив Lines структуры Line из изменяемого
'количества элементов NumLines, инициализируем его
'оператором ReDim и при помощи ключевого слова Preserve
'сохраняем предыдущие данные массива:
ReDim Preserve Lines(NumLines)
'Инициализируем и рассчитываем массивы:
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
End Sub
'Применяем матрицу переноса (translation matrix)
'ко всем линиям, используя MatrixApplyFull.
'Преобразование не имеет 0, 0, 0, 1 в последнем столбце:
Public Sub TransformAllDataFull(ByRef M(,) As Single)
TransformDataFull(M, 1, NumLines)
End Sub
'Применяем матрицу переноса (translation matrix)
'ко всем выделенным линиям, используя MatrixApplyFull.
'Преобразование не имеет 0, 0, 0, 1 в последнем столбце:
Public Sub TransformDataFull(ByRef M(,) As Single, _
ByVal line1 As Integer, ByVal line2 As Integer)
Dim i As Integer
For i = line1 To line2
MatrixApplyFull(Lines(i).fr_points, M, _
Lines(i).fr_tr_points)
MatrixApplyFull(Lines(i).to_points, M, _
Lines(i).to_tr_points)
Next i
End Sub
'Вводим перем-ю N_Graphics для номера многих геом-х изобр-й.
'Номер первого изображения равен 1:
Dim N_Graphics As Integer = 1
'Рисуем выделенные преобразованные линии и экпорт-м в файлы:
Public Sub DrawSolid(ByVal bmp As Bitmap, _
ByVal first_line As Integer, ByVal last_line As Integer, _
ByVal color As Color, ByVal clear As Boolean)
Dim k As Integer
Dim x1 As Single : Dim y1 As Single
Dim x2 As Single : Dim y2 As Single
Dim g As Graphics : Dim pen As Pen
'Задаем толщину линии рисования, например, 2
'(цвет линии мы задали в процедуре Designing):
pen = New Pen(color, 2)
'Связываем объект g с изображением bmp:
g = Graphics.FromImage(bmp)
If clear Then g.Clear(System.Drawing.Color.Black)
'Объявляем индексы элементов всех массивов:
Dim i, j As Integer
'Если этот метод DrawSolid вызван второй раз
'для рисования второго изображения и N_Graphics = 2,
'то обходим 1-й массив для первого изобр-я до метки M2:
If N_Graphics = 2 Then GoTo M2
'Программируем первый массив для первого изображения:
'Задаем границы индексов первого массива myArrayVB(i, j)
Dim N_x As Integer = 200
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 'Автомат-ки обнуляется.
'Значение первой границы массива myArrayVB:
Dim N_1_myArrayVB As Integer
'Рассчитываем элементы массива myArrayVB(i, j)
'для рисования линий первого геом-го изображения:
i = -1 'Задаем до цикла.
For k = first_line To last_line
x1 = Lines(k).fr_tr_points(1)
y1 = Lines(k).fr_tr_points(2)
x2 = Lines(k).to_tr_points(1)
y2 = Lines(k).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) _
')
'Масштабируем значения координат:
x1 = (x1 * bmp.Width / 4) + bmp.Width / 2.0F
y1 = bmp.Height / 2.0F - (y1 * bmp.Height / 4)
x2 = (x2 * bmp.Width / 4) + bmp.Width / 2.0F
y2 = bmp.Height / 2.0F - (y2 * bmp.Height / 4)
'Записываем координаты точек в массив:
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
'Начало N_first_line и конец N_last_line цикла
'при рисовании из массива myArrayVB:
Dim N_first_line, N_last_line As Integer
N_first_line = first_line
N_last_line = last_line
'Передаем значения начала N_first_line
'и конца цикла N_last_line в элементы массива
'myArrayVB(0, 0) и myArrayVB(0, 1):
myArrayVB(0, 0) = N_first_line
myArrayVB(0, 1) = N_last_line
'Рисуем при помощи массива координат myArrayVB(200, 1).
i = -1
For k = N_first_line To N_last_line
i = i + 2
x1 = myArrayVB(i, 0)
y1 = myArrayVB(i, 1)
x2 = myArrayVB(i + 1, 0)
y2 = myArrayVB(i + 1, 1)
g.DrawLine(pen, x1, y1, x2, y2)
Next
'Записываем массив координат myArrayVB(200, 1) в файл.
'Создаем объект sw класса StreamWriter для записи
'в файл по адресу D:\MyDocs\MyTest3D_Graphics.txt.
If N_Graphics = 1 Then
Dim sw As StreamWriter = _
New StreamWriter("D:\MyDocs\MyTest3D_Graphics.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 If
M2:
'Если этот метод DrawSolid вызван первый раз
'для рисования первого изображения и N_Graphics = 1,
'то обходим 2-й массив для 2-го изобр-я до метки M_End:
If N_Graphics = 1 Then GoTo M_End
'Программируем второй массив для второго изображения.
'Задаем границы индексов 2-го массива myArrayVB_2(i, j):
Dim N_x_2 As Integer = 200
Dim N_y_2 As Integer = 1
'Задаем массив myArrayVB_2(i, j) переменных типа Single,
'когда i = 0,1,2,3,...,N_x; j = 0,1,2,3,...,N_y:
Dim myArrayVB_2(N_x_2, N_y_2) As Single
'Значение первой границы массива myArrayVB_2:
Dim N_1_myArrayVB_2 As Integer
'Рассчитываем элементы массива myArrayVB_2(i, j)
'для рисования линий второго геом-го изображения:
i = -1 'Задаем до цикла.
For k = first_line To last_line
x1 = Lines(k).fr_tr_points(1)
y1 = Lines(k).fr_tr_points(2)
x2 = Lines(k).to_tr_points(1)
y2 = Lines(k).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) _
')
'Масштабируем значения координат:
x1 = (x1 * bmp.Width / 4) + bmp.Width / 2.0F
y1 = bmp.Height / 2.0F - (y1 * bmp.Height / 4)
x2 = (x2 * bmp.Width / 4) + bmp.Width / 2.0F
y2 = bmp.Height / 2.0F - (y2 * bmp.Height / 4)
'Записываем координаты точек в массив:
i = i + 2
myArrayVB_2(i, 0) = x1
myArrayVB_2(i, 1) = y1
myArrayVB_2(i + 1, 0) = x2
myArrayVB_2(i + 1, 1) = y2
N_1_myArrayVB_2 = i + 1 'Значение границы массива.
Next
'Начало N_first_line_2 и конец N_last_line_2 цикла
'при рисовании из массива myArrayVB_2:
Dim N_first_line_2, N_last_line_2 As Integer
N_first_line_2 = first_line
N_last_line_2 = last_line
'Передаем значения начала N_first_line_2
'и конца цикла N_last_line_2 в элементы массива
'myArrayVB_2(0, 0) и myArrayVB_2(0, 1):
myArrayVB_2(0, 0) = N_first_line_2
myArrayVB_2(0, 1) = N_last_line_2
'Рисуем при помощи массива координат myArrayVB_2(200, 1):
i = -1
For k = N_first_line_2 To N_last_line_2
i = i + 2
x1 = myArrayVB_2(i, 0)
y1 = myArrayVB_2(i, 1)
x2 = myArrayVB_2(i + 1, 0)
y2 = myArrayVB_2(i + 1, 1)
g.DrawLine(pen, x1, y1, x2, y2)
Next
'Записываем массив координат myArrayVB_2(200, 1) в файл.
'Создаем объект sw_2 класса StreamWriter для записи
'в файл по адресу D:\MyDocs\MyTest3D_Graphics_2.txt.
'Файл автоматически "опустошается":
Dim sw_2 As StreamWriter = _
New StreamWriter("D:\MyDocs\MyTest3D_Graphics_2.txt")
'Каждый элемент массива myArrayVB_2(i, j) запис-м в файл
'в виде отдельной строки при помощи процедуры WriteLine:
For i = 0 To N_x_2
For j = 0 To N_y_2
sw_2.WriteLine(myArrayVB_2(i, j))
Next
Next
sw_2.Close()
'Высвобождаем ресурсы от объектов g и pen:
g.Dispose() : pen.Dispose()
M_End:
'Если эта метод DrawSolid вызвана еще раз
'для рисования следующего изображения,
'то увеличиваем номер изображения N_Graphics на 1:
N_Graphics = N_Graphics + 1
End Sub
'Строим единичную матрицу:
Public Sub MatrixIdentity(ByRef M(,) As Single)
Dim i As Integer : Dim j As Integer
For i = 1 To 4
For j = 1 To 4
If i = j Then
M(i, j) = 1
Else
M(i, j) = 0
End If
Next
Next
End Sub
'Строим матрицу преобразования (3-D transformation matrix)
'для перспективной проекции вдоль оси z на плоскость x,y
'с центром объекта (фокусом) в начале координат
'и c центром проецирования на расстоянии (0, 0, Distance):
Public Sub MatrixPerspectiveXZ(ByRef M(,) As Single, _
ByVal Distance As Single)
MatrixIdentity(M)
If Distance <> 0 Then M(3, 4) = -1 / Distance
End Sub
'Строим матрицу преобразования (3-D transformation matrix)
'для проецирования с координатами:
'центр проецирования (cx, cy, cz),
'фокус (fx, fy, fx),
'вектор от объекта до экрана UP <ux, yx, uz>,
'тип проецирования (type_of_projection):
'PerspectiveProjection или ParallelProjection:
Public Sub MatrixTransformation(ByRef M(,) As Single, _
ByVal type_of_projection As Integer, _
ByVal Cx As Single, _
ByVal Cy As Single, ByVal Cz As Single, _
ByVal Fx As Single, ByVal Fy As Single, _
ByVal Fz As Single, ByVal ux As Single, _
ByVal uy As Single, ByVal uz As Single)
Static M1(4, 4) As Single : Static M2(4, 4) As Single
Static M3(4, 4) As Single : Static M4(4, 4) As Single
Static M5(4, 4) As Single : Static M12(4, 4) As Single
Static M34(4, 4) As Single
Static M1234(4, 4) As Single
Dim sin1 As Single : Dim cos1 As Single
Dim sin2 As Single : Dim cos2 As Single
Dim sin3 As Single : Dim cos3 As Single
Dim A As Single : Dim B As Single
Dim C As Single : Dim d1 As Single
Dim d2 As Single : Dim d3 As Single
Dim up1(4) As Single : Dim up2(4) As Single
'Переносим фокус (центр объекта) в начало координат:
MatrixTranslate(M1, -Fx, -Fy, -Fz)
A = Cx - Fx : B = Cy - Fy : C = Cz - Fz
d1 = CSng(Sqrt(A * A + C * C))
If d1 <> 0 Then
sin1 = -A / d1 : cos1 = C / d1
End If
d2 = CSng(Sqrt(A * A + B * B + C * C))
If d2 <> 0 Then
sin2 = B / d2 : cos2 = d1 / d2
End If
'Вращаем объект вокруг оси y, чтобы разместить
'центр проекции в y-z плоскости:
MatrixIdentity(M2)
'Если d1 = 0, тогда центр проекции
'уже находится на оси y и в y-z плоскости:
If d1 <> 0 Then
M2(1, 1) = cos1 : M2(1, 3) = -sin1
M2(3, 1) = sin1 : M2(3, 3) = cos1
End If
'Вращаем вокруг оси x,
'чтобы разместить центр проекции на оси Z.
MatrixIdentity(M3)
'Если d2 = 0, то центр проекции
'находится в начале координат.
'Это делает проекцию невозможной.
If d2 <> 0 Then
M3(2, 2) = cos2 : M3(2, 3) = sin2
M3(3, 2) = -sin2 : M3(3, 3) = cos2
End If
'Вращаем вектор UP:
up1(1) = ux : up1(2) = uy : up1(3) = uz
up1(4) = 1 : MatrixApply(up1, M2, up2)
MatrixApply(up2, M3, up1)
' Rotate around the Z axis to put the UP
' vector in the Y-Z plane.
'Вращаем вокруг оси z, чтобы разместить
'вектор UP в y-z плоскости:
d3 = CSng(Sqrt(up1(1) * up1(1) + _
up1(2) * up1(2)))
MatrixIdentity(M4)
'Если d3 = 0, то вектор UP равен нулю:
If d3 <> 0 Then
sin3 = up1(1) / d3 : cos3 = up1(2) / d3
M4(1, 1) = cos3 : M4(1, 2) = sin3
M4(2, 1) = -sin3 : M4(2, 2) = cos3
End If
'Проецируем:
If type_of_projection = _
PerspectiveProjection And d2 <> 0 Then
MatrixPerspectiveXZ(M5, d2)
Else
MatrixIdentity(M5)
End If
'Комбинируем преобразования:
m3MatMultiply(M12, M1, M2)
m3MatMultiply(M34, M3, M4)
m3MatMultiply(M1234, M12, M34)
If type_of_projection = PerspectiveProjection Then
m3MatMultiplyFull(M, M1234, M5)
Else
m3MatMultiply(M, M1234, M5)
End If
End Sub
'Строим матрицу преобразования (3-D transformation matrix)
'для перспективного проецирования (perspective projection):
'центр проецирования (r, phi, theta),
'фокус (fx, fy, fx),
'вектор от объекта до экрана UP <ux, yx, uz>,
'тип проецирования (type_of_projection):
'PerspectiveProjection:
Public Sub Projection(ByRef M(,) As Single, _
ByVal type_of_projection As Integer, ByVal R As Single, _
ByVal phi As Single, ByVal theta As Single, _
ByVal Fx As Single, ByVal Fy As Single, ByVal Fz As Single, _
ByVal ux As Single, ByVal uy As Single, ByVal uz As Single)
Dim Cx As Single : Dim Cy As Single
Dim Cz As Single : Dim r2 As Single
'Переходим к прямоугольным координатам:
Cy = R * CSng(Sin(phi))
r2 = R * CSng(Cos(phi))
Cx = r2 * CSng(Cos(theta))
Cz = r2 * CSng(Sin(theta))
MatrixTransformation(M, type_of_projection, _
Cx, Cy, Cz, Fx, Fy, Fz, ux, uy, uz)
End Sub
'Строим матрицу преобразования, чтобы получить
'отражение напротив плоскости, проходящей
'через (p1, p2, p3) с вектором нормали <n1, n2, n3>:
Public Sub m3Reflect(ByRef M(,) As Single, _
ByVal p1 As Single, ByVal p2 As Single, _
ByVal p3 As Single, ByVal n1 As Single, _
ByVal n2 As Single, ByVal n3 As Single)
Dim T(4, 4) As Single 'Перенос.
Dim R1(4, 4) As Single 'Вращение 1.
Dim r2(4, 4) As Single 'Вращение 2.
Dim S(4, 4) As Single 'Отражение.
Dim R2i(4, 4) As Single 'Не вращать 2.
Dim R1i(4, 4) As Single 'Не вращать 1.
Dim Ti(4, 4) As Single 'Не переносить.
Dim D As Single : Dim L As Single
Dim M12(4, 4) As Single : Dim M34(4, 4) As Single
Dim M1234(4, 4) As Single
Dim M56(4, 4) As Single : Dim M567(4, 4) As Single
'Переносим плоскость к началу координат:
MatrixTranslate(T, -p1, -p2, -p3)
MatrixTranslate(Ti, p1, p2, p3)
'Вращаем вокруг оси z,
'пока нормаль не будет в y-z плоскости:
MatrixIdentity(R1)
D = CSng(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(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(r2)
L = CSng(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(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(S)
S(2, 2) = -1
'Комбинируем матрицы:
m3MatMultiply(M12, T, R1) : m3MatMultiply(M34, r2, S)
m3MatMultiply(M1234, M12, M34)
m3MatMultiply(M56, R2i, R1i)
m3MatMultiply(M567, M56, Ti)
m3MatMultiply(M, M1234, M567)
End Sub
'Строим матрицу преобразования для поворота на угол theta
'вокруг линии, проходящей через (p1, p2, p3)
'в направлении <d1, d2, d3>.
'Угол theta откладывается против часовой стрелки,
'если мы смотрим вниз в направлении,
'противоположном направлению линии:
Public Sub m3LineRotate(ByRef M(,) As Single, _
ByVal p1 As Single, ByVal p2 As Single, ByVal p3 As Single, _
ByVal d1 As Single, ByVal d2 As Single, ByVal d3 As Single, _
ByVal theta As Single)
Dim T(4, 4) As Single 'Перенос.
Dim R1(4, 4) As Single 'Вращение 1.
Dim r2(4, 4) As Single 'Вращение 2.
Dim Rot3(4, 4) As Single 'Вращение.
Dim R2i(4, 4) As Single 'Стоп вращению 2.
Dim R1i(4, 4) As Single 'Стоп вращению 1.
Dim Ti(4, 4) As Single 'Стоп переносу.
Dim D As Single : Dim L As Single
Dim M12(4, 4) As Single : Dim M34(4, 4) As Single
Dim M1234(4, 4) As Single
Dim M56(4, 4) As Single : Dim M567(4, 4) As Single
'Переносим плоскость к началу координат:
MatrixTranslate(T, -p1, -p2, -p3)
MatrixTranslate(Ti, p1, p2, p3)
'Вращаем вокруг оси z,
'пока линия не окажется в y-z плоскости:
MatrixIdentity(R1)
D = CSng(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(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(r2)
L = CSng(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(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(Rot3, theta)
'Комбинируем матрицы:
m3MatMultiply(M12, T, R1)
m3MatMultiply(M34, r2, Rot3)
m3MatMultiply(M1234, M12, M34)
m3MatMultiply(M56, R2i, R1i)
m3MatMultiply(M567, M56, Ti)
m3MatMultiply(M, M1234, M567)
End Sub
'Строим матрицу преобразования (3-D transformation matrix)
'для переноса на Tx, Ty, Tz:
Public Sub MatrixTranslate(ByRef M(,) As Single, _
ByVal Tx As Single, ByVal Ty As Single, ByVal Tz As Single)
MatrixIdentity(M)
M(4, 1) = Tx : M(4, 2) = Ty : M(4, 3) = Tz
End Sub
'Строим матрицу преобразования (3-D transformation matrix)
'для поворота вокруг оси y (угол - в радианах):
Public Sub MatrixYRotate(ByRef M(,) As Single, _
ByVal theta As Single)
MatrixIdentity(M)
M(1, 1) = CSng(Cos(theta))
M(3, 3) = M(1, 1)
M(3, 1) = CSng(Sin(theta))
M(1, 3) = -M(3, 1)
End Sub
'Применяем матрицу преобразования к точке,
'где матрица не может иметь 0, 0, 0, 1
'в последнем столбце. Нормализуем только
'x и y компоненты результата, чтобы сохранить z информацию:
Public Sub MatrixApplyFull(ByRef V() As Single, _
ByRef M(,) As Single, ByRef Result() As Single)
Dim i As Integer : Dim j As Integer
Dim value As Single
For i = 1 To 4
value = 0
For j = 1 To 4
value = value + V(j) * M(j, i)
Next j
Result(i) = value
Next i
'Повторно нормализуем точку (value = Result(4)):
If value <> 0 Then
Result(1) = Result(1) / value
Result(2) = Result(2) / value
'Не преобразовываем z-составляющую:
Else
'Если значение z больше, чем от центра проекции,
'эта точка будет удалена:
Result(3) = Single.MaxValue
End If
Result(4) = 1
End Sub
'Применяем матрицу преобразования к точке:
Public Sub MatrixApply(ByRef V() As Single, _
ByRef M(,) As Single, ByRef Result() As Single)
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
End Sub
'Умножаем две матрицы. Матрицы
'не могут содержать 0, 0, 0, 1 в последних столбцах:
Public Sub m3MatMultiplyFull(ByRef Result(,) As Single, _
ByRef A(,) As Single, ByRef B(,) As Single)
Dim i As Integer : Dim j As Integer
Dim k As Integer : Dim value As Single
For i = 1 To 4
For j = 1 To 4
value = 0
For k = 1 To 4
value = value + A(i, k) * B(k, j)
Next k
Result(i, j) = value
Next
Next
End Sub
'Умножаем две матрицы:
Public Sub m3MatMultiply(ByRef Result(,) As Single, _
ByRef A(,) As Single, ByRef B(,) As Single)
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
End Sub
Листинг 34.4. Метод для печати изображения с элемента PictureBox.
Private Sub PrintDocument1_PrintPage( _
ByVal sender As System.Object, _
ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
Handles PrintDocument1.PrintPage
e.Graphics.DrawImage(PictureBox1.Image, 0, 0)
End Sub