GDI+实现三维三叉树

版权声明:转载注明出处 https://blog.csdn.net/york1996/article/details/81748447

首先看看效果:

三维三叉树
3-D Tree

 在学习二叉树数据结构的时候,用GDI+结合队列或者栈来画出来一个二叉树的结构,如果你不是一次绘制完成,而是从每两个线的绘制有时间间隔的话,你就能看到树的“生长过程”,而使用栈和队列将得到完全不同的生长方式,也许这就是深度优先遍历和广度优先遍历的区别把。那么,我当时就在想,如果绘制一个空间中的三叉树会是什么样的效果呢?有了想法就一步步的实现。之前也知道GDI+提供了一些旋转的函数和一些投影的知识,所以应该不会太难。

如果你已经完成了二叉树的绘制,那么当绘制三维三叉树的时候你也许将会遇到两个大问题:

1,如何生成?虽然规则看起来就是1个分成3个,最后每个分支的角度就将会越来越小。你可以想象它们的位置是相对的,这样思考起来就比较简单了,也就是对于任何一个支,它所生成三个枝杈都满足一个空间正四面体这样的结构,然后采用一些空间几何的运算法则你或许可以准确的计算出来每个枝杈的生长方向(角度)或者终点的XYZ坐标,但是这里有一种更为简单的方式,也就是运用一定的旋转缩放规则,在经过一系列的变换,你就可以把一个线段从母分支变换到子分支。而在具体的实现细节中,一定心中始终要有一个全局坐标系和局部坐标系的概念。

2,如何显示?在二维的情况下,每个线段只需要xy坐标就可以显示出来,并且也不会有遮挡的问题。如果你解决了第一个问题,现在假设你已经生成了一个完好的三维三叉树,在屏幕上显示的时候如果只输出xyz坐标中的两个,那么得到的结果和二叉树应该是类似的,即使看到的不是一个二叉树,那么树也是死的,看不到它的另一面或者侧面。这个时候就需要一个投影法则,就是根据侧投影角度和正投影角度来把(x,y,z)坐标变成(x',y'),然后就可以显示在屏幕上,改变投影角度,就可以得到上述动态图的效果。

这是以前写的程序,一些公式和旋转步骤,细节等等记不太清了,大体思路如上。

下面是所有的VB.NET代码:

用到了一些简单控件,从代码中可以看出来。

Imports System.Math
Imports System.Windows.Media.Media3D
Public Class Form1
    Dim bmp As New Bitmap(1000, 1000)
    Dim g As Graphics = Graphics.FromImage(bmp)
    Private Sub _3DbinaryTree_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        HScrollBar1.Value = 45
        VScrollBar1.Value = 45
        '用来存放所有的线条
        Dim line3dStack As New Queue(Of line3d)
        line3dStack.Enqueue(New line3d(0, 0, 0, 0, 0, 180))

        Do While line3dStack.Count <> 0
            Dim wire As line3d = line3dStack.Dequeue

            Dim q As New Point3D() With {.X = wire.x1, .Y = wire.y1, .Z = wire.z1}
            Dim dx, dy, dz As Single
            dx = wire.x2 - wire.x1
            dy = wire.y2 - wire.y1
            dz = wire.z2 - wire.z1
            Dim rate As Single = 0.6

            Dim Len As Single = Sqrt(dx * dx + dy * dy + dz * dz) * rate 'po长度
            '树的最小长度
            If Len < 8 Then
                Continue Do
            End If

            Dim p As New Point3D With {.X = wire.x2, .Y = wire.y2, .Z = wire.z2}
            Dim o As New Point3D() With
                {.X = p.X + dx * rate, .Y = p.Y + dy * rate, .Z = p.Z + dz * rate}
            Dim n As New Vector3D(dx, dy, dz) 'po向量

            Dim m As New Matrix3D() '绕po轴,o点旋转的矩阵
            m.RotateAt(New Quaternion(n, 120), o)
            Dim mv As New Matrix3D()
            Dim zz As Single
            Dim pp As Vector3D
            If dz = 0 Then
                pp = New Vector3D(0, 0, 1)
            Else
                zz = -(dx + dy) / dz
                pp = New Vector3D(1, 1, zz)
            End If
            mv.RotateAt(New Quaternion(New Vector3D(1, 1, zz), 90), o) '旋转po到ABC平面
            mv.ScaleAt(New Vector3D(1 / Sqrt(4), 1 / Sqrt(4), 1 / Sqrt(4)), o)
            Dim b As Point3D = mv.Transform(p)
            Dim a As Point3D = m.Transform(b)
            Dim c As Point3D = m.Transform(a)
            line3DList.Add(wire)
            line3dStack.Enqueue(New line3d(p, a))
            line3dStack.Enqueue(New line3d(p, b))
            line3dStack.Enqueue(New line3d(p, c))
        Loop
        draw3d(line3DList, 45, 30) '45°,30°的视角观察三维图

    End Sub
    Dim line3DList As New List(Of line3d)
    Sub draw3d(lines As List(Of line3d), a As Single, b As Single)
        g.Clear(Color.Black)

        a = a * PI / 180
        b = b * PI / 180

        For Each item As line3d In lines
            Dim xoffset As Single = 500
            Dim yoffset As Single = 700
            Dim x1 As Single = item.x1 * Cos(a) - item.y1 * Sin(a) + xoffset
            Dim y1 As Single = -item.x1 * Sin(a) * Sin(b) - item.y1 * Cos(a) * Cos(b) + item.z1 * Cos(b) + yoffset
            Dim x2 As Single = item.x2 * Cos(a) - item.y2 * Sin(a) + xoffset
            Dim y2 As Single = -item.x2 * Sin(a) * Sin(b) - item.y2 * Cos(a) * Cos(b) + item.z2 * Cos(b) + +yoffset
            Dim rate As Single = 1
            x2 *= rate
            x1 *= rate
            y1 *= rate
            y2 *= rate
            Dim startPoint As New Point(x1, y1)
            Dim endPoint As New Point(x2, y2)
            g.DrawLine(Pens.White, startPoint, endPoint)
        Next
        PictureBox1.Image = bmp
    End Sub
    Private Sub HScrollBar1_ValueChanged(sender As Object, e As EventArgs) Handles HScrollBar1.ValueChanged
        draw3d(line3DList, HScrollBar1.Value, VScrollBar1.Value)
    End Sub

    Private Sub VScrollBar1_ValueChanged(sender As Object, e As EventArgs) Handles VScrollBar1.ValueChanged
        draw3d(line3DList, HScrollBar1.Value, VScrollBar1.Value)
    End Sub

End Class

猜你喜欢

转载自blog.csdn.net/york1996/article/details/81748447
今日推荐