Attribute VB_Name = "Views"

Option Explicit

'Views
Private Type ViewRect
    Left As Double
    Top As Double
    Width As Double
    Height As Double
End Type
Private Type Component
    Region As ViewRect
End Type
Const ComponentType_None As Integer = 0 'ComponentIndex should be -1
Const ComponentType_Tab As Integer = 1
Private Type ViewComponentIndex
    ViewIndex As Integer
    ComponentType As Integer
    ComponentIndex As Integer
End Type
Const NumberOfTabsPerView As Integer = 7
Const TabHeight As Double = 20
Private Type View
    WindowCenter As Vector2
    Zoom As Double
    FlatZoom As Double
    ViewType As ViewTypeEnum
    DrawSurface As Long
    Visible As Boolean
    ParentRect As ViewRect
    ContentRect As ViewRect
    Tabs(0 To NumberOfTabsPerView - 1) As Component 'One tab for each type of view
End Type
Dim Views(0 To 3) As View
Private Enum ViewTypeEnum
    ViewType_Left
    ViewType_Right
    ViewType_Top
    ViewType_Bottom
    ViewType_Back
    ViewType_Front
    ViewType_Perspective
End Enum

'Layout
Public Enum ViewLayout
    SingleView
    VerticalView
    HorizontalView
    QuadView
End Enum
Dim CurrentViewLayout As ViewLayout

'Draw surfaces
Dim DrawSurface_Final As Long

'Cameras
Dim Camera_Perspective As Long 'The first person perspective camera
Dim CameraLocation As Vector3 'The location of the first person perspective camera
Dim CameraDirection As Polar 'The direction of the first person perspective camera
Dim Camera_Orthogonal As Long
Dim FlatTarget As Vector3 'The center of all flat views

'Mouse speeds
Const MouseZoomSpeed As Double = 0.015

'Light sources
Dim LightSource_Sun As Long

'Tools
Dim DisableTools As Boolean
Public DrawTarget As FractionPoint 'The three planes deciding the depth of any actions in 2D that needs 3D

'Mouse data in 3D space
Dim StartDragMousePosition_3D As FractionPoint 'The 3D position in the beginning of the last drag
Dim RoundedMousePosition_3D As FractionPoint 'The current 3D position of the mouse with rounding

'Mouse data in screen space
Dim LastMouseScreenPosition As Vector2 'The last 2D mouse position
Dim PreviousMouseScreenPosition As Vector2 'The previous 2D mouse position
Dim LastMouseDownScreenPosition As Vector2 'Where did you press down the left mouse button the last time

'Keyboard data
Dim LastShift As Integer 'A copy of the Shift argument from mouse events so that everything using it works the same way

'View data
Dim OutputWidth As Long
Dim OutputHeight As Long
Dim LastActiveViewIndex As Integer 'An index to the last active view
Dim LastMouseDownViewIndex As Integer 'An index to the last view that pressed down a mouse button

'Mouse button data for different view types
Dim MDLeft_Flat As Boolean 'Is the left mouse button pressed in a flat view
Dim MDRight_Flat As Boolean 'Is the right mouse button pressed in a flat view
Dim MDLeft_P As Boolean 'Is the left mouse button pressed in a perspective view
Dim MDRight_P As Boolean 'Is the right mouse button pressed in a perspective view

Public Sub Views_Init()
    DrawSurface_Final = frmMain.DGE.DrawSurface_GetFinalOutput: RE
    frmMain.DGE.Engine_SetAutomaticSwapChain False
    
    Camera_Perspective = frmMain.DGE.Camera_Create: frmMain.DGE.Camera_SetHorizontalFOV_InDegrees Camera_Perspective, 90: RE
    Camera_Orthogonal = frmMain.DGE.Camera_Create: frmMain.DGE.Camera_SetOrthogonal Camera_Orthogonal, True: RE
    
    Views(0).ViewType = ViewType_Top
    Views(1).ViewType = ViewType_Right
    Views(2).ViewType = ViewType_Back
    Views(3).ViewType = ViewType_Perspective
    StartDragMousePosition_3D = MakeFractionPoint_Zero
    RoundedMousePosition_3D = MakeFractionPoint_Zero
    
    frmMain.DGE.LightSource_SetAmbientLight 0.2, 0.2, 0.2
    LightSource_Sun = frmMain.DGE.LightSource_Create_Sun_Shadowless(0, 1, 0, 1, 1, 1)
    frmMain.DGE.Enviroment_SetBackgroundColor 0.2, 0.2, 0.2, 0
    frmMain.DGE.Enviroment_SetFarClipPlane 10000
    frmMain.DGE.Enviroment_SetNearClipPlane 0.1
    
    CurrentViewLayout = ViewLayout.QuadView
    ResetViews
End Sub

Public Sub Views_SetLayout(Layout As ViewLayout)
    CurrentViewLayout = Layout
    Views_Resize
End Sub

Public Sub Views_Resize()
    Dim I As Integer
    Dim J As Integer
    On Error Resume Next
    
    'Update output size
    OutputWidth = frmMain.DGE.Width
    OutputHeight = frmMain.DGE.Height
    
    'Delete old draw surfaces
    For I = 0 To 3
        If Views(I).DrawSurface > 0 Then
            frmMain.DGE.DrawSurface_Delete Views(I).DrawSurface: RE
            Views(I).DrawSurface = 0
        End If
    Next I
    
    'Place views
    Select Case CurrentViewLayout
    Case ViewLayout.QuadView
        'Place perspective view
        Views(3).Visible = True
        Views(3).ParentRect.Left = RoundToPixels(OutputWidth / 2) + 2
        Views(3).ParentRect.Top = RoundToPixels(OutputHeight / 2) + 2
        Views(3).ParentRect.Width = RoundToPixels(OutputWidth / 2) - 6
        Views(3).ParentRect.Height = RoundToPixels(OutputHeight / 2) - 6
        
        'Place flat view
        Views(0).Visible = True
        Views(0).ParentRect.Left = 4
        Views(0).ParentRect.Top = 4
        Views(0).ParentRect.Width = RoundToPixels(OutputWidth / 2) - 6
        Views(0).ParentRect.Height = RoundToPixels(OutputHeight / 2) - 6
        
        'Place flat view
        Views(1).Visible = True
        Views(1).ParentRect.Left = RoundToPixels(OutputWidth / 2) + 2
        Views(1).ParentRect.Top = 4
        Views(1).ParentRect.Width = RoundToPixels(OutputWidth / 2) - 6
        Views(1).ParentRect.Height = RoundToPixels(OutputHeight / 2) - 6
        
        'Place flat view
        Views(2).Visible = True
        Views(2).ParentRect.Left = 4
        Views(2).ParentRect.Top = RoundToPixels(OutputHeight / 2) + 2
        Views(2).ParentRect.Width = RoundToPixels(OutputWidth / 2) - 6
        Views(2).ParentRect.Height = RoundToPixels(OutputHeight / 2) - 6
    Case ViewLayout.SingleView
        'Place a single view
        Views(3).Visible = True
        Views(3).ParentRect.Left = 0
        Views(3).ParentRect.Top = 0
        Views(3).ParentRect.Width = OutputWidth
        Views(3).ParentRect.Height = OutputHeight
        
        'Hide the remaining flat views
        Views(0).Visible = False
        Views(1).Visible = False
        Views(2).Visible = False
    Case ViewLayout.VerticalView
        'Place perspective view
        Views(3).Visible = True
        Views(3).ParentRect.Left = RoundToPixels(OutputWidth / 2) + 2
        Views(3).ParentRect.Top = 4
        Views(3).ParentRect.Width = RoundToPixels(OutputWidth / 2) - 6
        Views(3).ParentRect.Height = frmMain.DGE.Height - 8
        
        'Place flat view
        Views(0).Visible = True
        Views(0).ParentRect.Left = 4
        Views(0).ParentRect.Top = 4
        Views(0).ParentRect.Width = RoundToPixels(OutputWidth / 2) - 6
        Views(0).ParentRect.Height = frmMain.DGE.Height - 8
        
        'Hide the remaining flat views
        Views(1).Visible = False
        Views(2).Visible = False
    Case ViewLayout.HorizontalView
        'Place perspective view
        Views(3).Visible = True
        Views(3).ParentRect.Left = 4
        Views(3).ParentRect.Top = RoundToPixels(frmMain.DGE.Height / 2) + 2
        Views(3).ParentRect.Width = OutputWidth - 8
        Views(3).ParentRect.Height = RoundToPixels(frmMain.DGE.Height / 2) - 6
        
        'Place flat view
        Views(0).Visible = True
        Views(0).ParentRect.Left = 4
        Views(0).ParentRect.Top = 4
        Views(0).ParentRect.Width = OutputWidth - 8
        Views(0).ParentRect.Height = RoundToPixels(frmMain.DGE.Height / 2) - 6
        
        'Hide the remaining flat views
        Views(1).Visible = False
        Views(2).Visible = False
    Case Else
        Views(0).Visible = False
        Views(1).Visible = False
        Views(2).Visible = False
        Views(3).Visible = False
    End Select
    
    For I = 0 To 3
        'Place the content
        Views(I).ContentRect.Left = Views(I).ParentRect.Left
        Views(I).ContentRect.Width = Views(I).ParentRect.Width
        Views(I).ContentRect.Top = Views(I).ParentRect.Top + TabHeight
        Views(I).ContentRect.Height = Views(I).ParentRect.Height - TabHeight
        For J = 0 To NumberOfTabsPerView - 1
            Views(I).Tabs(J).Region.Top = Views(I).ParentRect.Top
            Views(I).Tabs(J).Region.Left = RoundToPixels(Views(I).ParentRect.Left + (J * (Views(I).ParentRect.Width / NumberOfTabsPerView)))
            Views(I).Tabs(J).Region.Height = TabHeight
        Next J
        
        'Place tabs
        Views(I).Tabs(NumberOfTabsPerView - 1).Region.Width = Views(I).ParentRect.Width - (Views(I).Tabs(NumberOfTabsPerView - 1).Region.Left - Views(I).ParentRect.Left)
        For J = NumberOfTabsPerView - 2 To 0 Step -1
            Views(I).Tabs(J).Region.Width = Views(I).Tabs(J + 1).Region.Left - Views(I).Tabs(J).Region.Left
        Next J
        
        'Allocate a new draw surface for the content
        If Views(I).Visible Then
            If Views(I).ContentRect.Width < 1 Then Views(I).ContentRect.Width = 1
            If Views(I).ContentRect.Height < 1 Then Views(I).ContentRect.Height = 1
            Views(I).DrawSurface = frmMain.DGE.DrawSurface_CreateFixed(Views(I).ContentRect.Width, Views(I).ContentRect.Height, 2, False): RE
        End If
    Next I
    
    For I = 0 To 3
        PrepareView I
    Next I
End Sub

Private Sub ResetViews()
    FlatTarget = MakeVector3(0, 0, 0)
    DrawTarget = MakeFractionPoint_Zero
    CameraLocation = MakeVector3(0, 4, -16)
    Views(0).FlatZoom = 32
    Views(1).FlatZoom = 32
    Views(2).FlatZoom = 32
    Views(3).FlatZoom = 32
End Sub

Public Sub Views_DrawGUI()
    Geometry_NeedGrid
    Geometry_NeedGeometry
    Dim ViewIndex As Integer
    frmMain.DGE.DrawSurface_FillWithColor DrawSurface_Final, 166 / 255, 154 / 255, 87 / 255, 1
    For ViewIndex = 0 To 3
        RenderView ViewIndex
    Next ViewIndex
    frmMain.DGE.Engine_UpdateSwapChain
End Sub

Private Sub Update3DMousePosition()
    Dim DepthDimension As Integer
    Dim Pos As Vector3
    DepthDimension = GetDepthDim(LastActiveViewIndex)
    Select Case DragState_Get
    Case DragStateEnum.Box_SetHeight
        If MouseIsLocked Then
            Pos = ApproximateFractionPoint(RoundedMousePosition_3D)
            Select Case DepthDimension
            Case 0
                Pos.X = ApproximateFraction(DrawTarget.X) - (Mouse_GetOffsetSinceLock.Y) / Views(LastActiveViewIndex).Zoom
            Case 1
                Pos.Y = ApproximateFraction(DrawTarget.Y) - (Mouse_GetOffsetSinceLock.Y) / Views(LastActiveViewIndex).Zoom
            Case 2
                Pos.Z = ApproximateFraction(DrawTarget.Z) - (Mouse_GetOffsetSinceLock.Y) / Views(LastActiveViewIndex).Zoom
            End Select
        Else
            Pos = ApproximateFractionPoint(RoundedMousePosition_3D)
            Select Case DepthDimension
            Case 0
                Pos.X = ApproximateFraction(DrawTarget.X) + (LastMouseDownScreenPosition.Y - LastMouseScreenPosition.Y) / Views(LastActiveViewIndex).Zoom
            Case 1
                Pos.Y = ApproximateFraction(DrawTarget.Y) + (LastMouseDownScreenPosition.Y - LastMouseScreenPosition.Y) / Views(LastActiveViewIndex).Zoom
            Case 2
                Pos.Z = ApproximateFraction(DrawTarget.Z) + (LastMouseDownScreenPosition.Y - LastMouseScreenPosition.Y) / Views(LastActiveViewIndex).Zoom
            End Select
        End If
    Case Else
        Pos = Convert2DTo3D(MakeVector2(LastMouseScreenPosition.X, LastMouseScreenPosition.Y), LastActiveViewIndex, True)
        Select Case DepthDimension
        Case 0
            Pos.X = ApproximateFraction(DrawTarget.X)
        Case 1
            Pos.Y = ApproximateFraction(DrawTarget.Y)
        Case 2
            Pos.Z = ApproximateFraction(DrawTarget.Z)
        End Select
    End Select
    RoundedMousePosition_3D = Tools_RoundToGrid(Pos)
End Sub

Private Function InsideViewRect(Area As ViewRect, X As Double, Y As Double) As Boolean
    InsideViewRect = (X >= Area.Left And Y >= Area.Top And X <= Area.Left + Area.Width And Y <= Area.Top + Area.Height)
End Function

Private Function GetComponentIndexFromMouseCoordinate(X As Double, Y As Double) As ViewComponentIndex
    Dim I As Integer
    Dim J As Integer
    Dim LocalX As Double
    Dim LocalY As Double
    GetComponentIndexFromMouseCoordinate.ViewIndex = -1
    GetComponentIndexFromMouseCoordinate.ComponentType = ComponentType_None
    GetComponentIndexFromMouseCoordinate.ComponentIndex = -1
    For I = 3 To 0 Step -1
        If InsideViewRect(Views(I).ParentRect, X, Y) And Views(I).Visible Then
            'We have found the view
            GetComponentIndexFromMouseCoordinate.ViewIndex = I
            'Calculate the local coordinate for components drawn inside the content
            LocalX = X - Views(I).ContentRect.Left
            LocalY = Y - Views(I).ContentRect.Top
            'Find tabs
            For J = NumberOfTabsPerView - 1 To 0 Step -1
                If InsideViewRect(Views(I).Tabs(J).Region, X, Y) Then
                    GetComponentIndexFromMouseCoordinate.ComponentType = ComponentType_Tab
                    GetComponentIndexFromMouseCoordinate.ComponentIndex = J
                    Exit Function
                End If
            Next J
            Exit Function
        End If
    Next I
End Function

Private Sub RenderView(ViewIndex As Integer)
    Dim I As Integer
    Dim Caption As String
    
    'Draw things in the view
    If Views(ViewIndex).Visible Then
        'Draw the tabs
        For I = 0 To NumberOfTabsPerView - 1
            If Views(ViewIndex).ParentRect.Width > 400 Then
                Select Case I
                Case ViewType_Left
                    Caption = "Left"
                Case ViewType_Right
                    Caption = "Right"
                Case ViewType_Top
                    Caption = "Top"
                Case ViewType_Bottom
                    Caption = "Bottom"
                Case ViewType_Back
                    Caption = "Back"
                Case ViewType_Front
                    Caption = "Front"
                Case ViewType_Perspective
                    Caption = "3D"
                End Select
            ElseIf Views(ViewIndex).ParentRect.Width > 300 Then
                Select Case I
                Case ViewType_Left
                    Caption = "Lef"
                Case ViewType_Right
                    Caption = "Rig"
                Case ViewType_Top
                    Caption = "Top"
                Case ViewType_Bottom
                    Caption = "Bot"
                Case ViewType_Back
                    Caption = "Bac"
                Case ViewType_Front
                    Caption = "Fro"
                Case ViewType_Perspective
                    Caption = "3D"
                End Select
            Else
                Select Case I
                Case ViewType_Left
                    Caption = "L"
                Case ViewType_Right
                    Caption = "R"
                Case ViewType_Top
                    Caption = "T"
                Case ViewType_Bottom
                    Caption = "B"
                Case ViewType_Back
                    Caption = "B"
                Case ViewType_Front
                    Caption = "F"
                Case ViewType_Perspective
                    Caption = "3"
                End Select
            End If
            If I = Views(ViewIndex).ViewType Then
                DrawTab_Final Caption, Views(ViewIndex).Tabs(I).Region, MakeVector4(0.2, 1, 0.2, 1), MakeVector4(1, 1, 1, 1)
            Else
                DrawTab_Final Caption, Views(ViewIndex).Tabs(I).Region, MakeVector4(0.2, 0.2, 0.2, 1), MakeVector4(0.8, 0.8, 0.8, 0.8)
            End If
        Next I
        
        'Render and paint the content
        If Views(ViewIndex).ViewType = ViewType_Perspective Then
            Render3DView ViewIndex
        Else
            RenderFlatView ViewIndex
        End If
        If Views(ViewIndex).ContentRect.Width > 0 And Views(ViewIndex).ContentRect.Height > 0 Then
            frmMain.DGE.Draw_GiveInputSurface 0, Views(ViewIndex).DrawSurface
            frmMain.DGE.Draw_RenderQuad DrawSurface_Final, DrawShader_Clamped_NN_1Tex, Lerp(-1, 1, (Views(ViewIndex).ContentRect.Left + (Views(ViewIndex).ContentRect.Width / 2)) / OutputWidth), Lerp(-1, 1, (Views(ViewIndex).ContentRect.Top + (Views(ViewIndex).ContentRect.Height / 2)) / OutputHeight), Views(ViewIndex).ContentRect.Width / OutputWidth, 0, 0, Views(ViewIndex).ContentRect.Height / OutputHeight, 0
        End If
    End If
End Sub

Private Function RoundToPixels(X As Double) As Double
    RoundToPixels = Int(X)
End Function

Private Function FlatTo3DDimIndex(ViewIndex As Integer, DimIndex As Integer) As Integer
    Dim CurDim3D As Vector3
    Select Case DimIndex
    Case 0
        CurDim3D = Transform2DTo3D(MakeVector2(1, 0), ViewIndex, False)
    Case 1
        CurDim3D = Transform2DTo3D(MakeVector2(0, 1), ViewIndex, False)
    Case Else
        MsgBox "Invalid dimension index to FlatTo3DDimIndex! DimIndex = 0 or 1"
    End Select
    If Abs(CurDim3D.X) > 0.1 Then
        FlatTo3DDimIndex = 0
    ElseIf Abs(CurDim3D.Y) > 0.1 Then
        FlatTo3DDimIndex = 1
    ElseIf Abs(CurDim3D.Z) > 0.1 Then
        FlatTo3DDimIndex = 2
    End If
End Function

Private Function GetNameOfDimension(V As Vector3) As String
    If V.X > 0.1 Then
        GetNameOfDimension = "+x"
    ElseIf V.X < -0.1 Then
        GetNameOfDimension = "-x"
    ElseIf V.Y > 0.1 Then
        GetNameOfDimension = "+y"
    ElseIf V.Y < -0.1 Then
        GetNameOfDimension = "-y"
    ElseIf V.Z > 0.1 Then
        GetNameOfDimension = "+z"
    ElseIf V.Z < -0.1 Then
        GetNameOfDimension = "-z"
    End If
End Function

Private Function GetColorOfDimension(V As Vector3) As Vector4
    If Abs(V.X) > 0.1 Then
        GetColorOfDimension = MakeVector4(1, 0.15, 0.15, 1)
    ElseIf Abs(V.Y) > 0.1 Then
        GetColorOfDimension = MakeVector4(0.15, 1, 0.15, 1)
    ElseIf Abs(V.Z) > 0.1 Then
        GetColorOfDimension = MakeVector4(0.3, 0.3, 1, 1)
    End If
End Function

Private Sub PrepareView(ViewIndex As Integer)
    Views(ViewIndex).Zoom = Sqr((Views(ViewIndex).ContentRect.Width ^ 2) + (Views(ViewIndex).ContentRect.Height ^ 2)) / Views(ViewIndex).FlatZoom
    Views(ViewIndex).WindowCenter.X = Views(ViewIndex).ContentRect.Width / 2
    Views(ViewIndex).WindowCenter.Y = Views(ViewIndex).ContentRect.Height / 2
End Sub

'A helper to Convert3DTo2D that can be reused
Private Function Transform3DTo2D(Pos As Vector3, ViewIndex As Integer) As Vector2
    Select Case Views(ViewIndex).ViewType
    Case ViewType_Left
        Transform3DTo2D.X = -Pos.Z
        Transform3DTo2D.Y = -Pos.Y
    Case ViewType_Right
        Transform3DTo2D.X = Pos.Z
        Transform3DTo2D.Y = -Pos.Y
    Case ViewType_Top
        Transform3DTo2D.X = Pos.X
        Transform3DTo2D.Y = -Pos.Z
    Case ViewType_Bottom
        Transform3DTo2D.X = Pos.X
        Transform3DTo2D.Y = Pos.Z
    Case ViewType_Back
        Transform3DTo2D.X = Pos.X
        Transform3DTo2D.Y = -Pos.Y
    Case ViewType_Front
        Transform3DTo2D.X = -Pos.X
        Transform3DTo2D.Y = -Pos.Y
    Case Else
        Transform3DTo2D.X = 0
        Transform3DTo2D.Y = 0
    End Select
End Function

Private Function GetDepthAxis(ViewIndex As Integer) As Vector3
    Select Case Views(ViewIndex).ViewType
    Case ViewType_Left
        GetDepthAxis = MakeVector3(1, 0, 0)
    Case ViewType_Right
        GetDepthAxis = MakeVector3(-1, 0, 0)
    Case ViewType_Top
        GetDepthAxis = MakeVector3(0, -1, 0)
    Case ViewType_Bottom
        GetDepthAxis = MakeVector3(0, 1, 0)
    Case ViewType_Back
        GetDepthAxis = MakeVector3(0, 0, -1)
    Case ViewType_Front
        GetDepthAxis = MakeVector3(0, 0, 1)
    Case Else
        GetDepthAxis = MakeVector3(0, 0, 0)
    End Select
End Function

Private Function GetDepthAxisFraction(ViewIndex As Integer) As FractionNormal
    Select Case Views(ViewIndex).ViewType
    Case ViewType_Left
        GetDepthAxisFraction = MakeFractionNormal_Args(1, 0, 0)
    Case ViewType_Right
        GetDepthAxisFraction = MakeFractionNormal_Args(-1, 0, 0)
    Case ViewType_Top
        GetDepthAxisFraction = MakeFractionNormal_Args(0, -1, 0)
    Case ViewType_Bottom
        GetDepthAxisFraction = MakeFractionNormal_Args(0, 1, 0)
    Case ViewType_Back
        GetDepthAxisFraction = MakeFractionNormal_Args(0, 0, -1)
    Case ViewType_Front
        GetDepthAxisFraction = MakeFractionNormal_Args(0, 0, 1)
    Case Else
        GetDepthAxisFraction = MakeFractionNormal_Args(0, 0, 0)
    End Select
End Function

Private Function GetDepthDim(ViewIndex As Integer) As Integer
    Select Case Views(ViewIndex).ViewType
    Case ViewType_Left
        GetDepthDim = 0
    Case ViewType_Right
        GetDepthDim = 0
    Case ViewType_Top
        GetDepthDim = 1
    Case ViewType_Bottom
        GetDepthDim = 1
    Case ViewType_Back
        GetDepthDim = 2
    Case ViewType_Front
        GetDepthDim = 2
    Case Else
        GetDepthDim = -1
    End Select
End Function

Private Function Convert3DTo2D(Pos As Vector3, ViewIndex As Integer) As Vector2
    'Transform to 2D
    Convert3DTo2D = Transform3DTo2D(SubVector3(Pos, FlatTarget), ViewIndex)
    
    'Scale to window coordinates
    Convert3DTo2D = AddVector2(MulVector2(Convert3DTo2D, Views(ViewIndex).Zoom), Views(ViewIndex).WindowCenter)
End Function

'A helper to Convert2DTo3D that can be reused
Private Function Transform2DTo3D(Pos As Vector2, ViewIndex As Integer, UseDrawTarget As Boolean) As Vector3
    'Transform from 2D
    If UseDrawTarget Then
        Transform2DTo3D = ApproximateFractionPoint(DrawTarget)
    Else
        Transform2DTo3D = MakeVector3(0, 0, 0)
    End If
    
    Select Case Views(ViewIndex).ViewType
    Case ViewType_Left
        Transform2DTo3D.Y = -Pos.Y
        Transform2DTo3D.Z = -Pos.X
    Case ViewType_Right
        Transform2DTo3D.Y = -Pos.Y
        Transform2DTo3D.Z = Pos.X
    Case ViewType_Top
        Transform2DTo3D.X = Pos.X
        Transform2DTo3D.Z = -Pos.Y
    Case ViewType_Bottom
        Transform2DTo3D.X = Pos.X
        Transform2DTo3D.Z = Pos.Y
    Case ViewType_Back
        Transform2DTo3D.X = Pos.X
        Transform2DTo3D.Y = -Pos.Y
    Case ViewType_Front
        Transform2DTo3D.X = -Pos.X
        Transform2DTo3D.Y = -Pos.Y
    Case Else
        Transform2DTo3D.X = 0
        Transform2DTo3D.Y = 0
    End Select
End Function

Private Function Convert2DTo3D(Pos As Vector2, ViewIndex As Integer, UseDrawTarget As Boolean) As Vector3
    Dim FlatPos As Vector2
    'Scale from window coordinates
    FlatPos = DivVector2(SubVector2(Pos, Views(ViewIndex).WindowCenter), Views(ViewIndex).Zoom)
    'Transform from 2D
    Convert2DTo3D = AddVector3(Transform2DTo3D(FlatPos, ViewIndex, UseDrawTarget), FlatTarget)
End Function

Private Sub RenderFlatView(ViewIndex As Integer)
    Dim Pos(3) As Vector3
    Dim ScreenPos(3) As Vector2
    Dim MidPos As Vector2
    Dim Vert As Long
    Dim DepthDim As Integer
    Dim Color As Vector4
    Dim TableIndex As Long
    Dim RightAxis As Vector3
    Dim UpAxis As Vector3
    Dim DepthAxis As Vector3
    
    DepthDim = GetDepthDim(LastActiveViewIndex)
    PrepareView ViewIndex
    
    RightAxis = Transform2DTo3D(MakeVector2(1, 0), ViewIndex, False)
    UpAxis = Transform2DTo3D(MakeVector2(0, -1), ViewIndex, False)
    DepthAxis = CrossProduct(RightAxis, UpAxis)
    
    Dim UpperLeftPos As Vector3
    Dim LowerRightPos As Vector3
    Dim MinU As Double
    Dim MaxU As Double
    Dim MinV As Double
    Dim MaxV As Double
    UpperLeftPos = Convert2DTo3D(MakeVector2(0, 0), ViewIndex, False)
    LowerRightPos = Convert2DTo3D(MakeVector2(Views(ViewIndex).ContentRect.Width, Views(ViewIndex).ContentRect.Height), ViewIndex, False)
    Select Case FlatTo3DDimIndex(ViewIndex, 0)
    Case 0
        MinU = UpperLeftPos.X
        MaxU = LowerRightPos.X
    Case 1
        MinU = UpperLeftPos.Y
        MaxU = LowerRightPos.Y
    Case 2
        MinU = UpperLeftPos.Z
        MaxU = LowerRightPos.Z
    End Select
    Select Case FlatTo3DDimIndex(ViewIndex, 1)
    Case 0
        MinV = UpperLeftPos.X
        MaxV = LowerRightPos.X
    Case 1
        MinV = UpperLeftPos.Y
        MaxV = LowerRightPos.Y
    Case 2
        MinV = UpperLeftPos.Z
        MaxV = LowerRightPos.Z
    End Select
    
    'Draw background grid
    Dim GridWidth As Double
    GridWidth = Views(ViewIndex).ContentRect.Width / (Abs(MinU - MaxU) / Tools_GetGridSize)
    If GridWidth > 4 Then
        If GridWidth > 64 Then
            frmMain.DGE.Draw_GiveInputSurface 0, Texture_2DGrid_128: RE
        ElseIf GridWidth > 32 Then
            frmMain.DGE.Draw_GiveInputSurface 0, Texture_2DGrid_64: RE
        ElseIf GridWidth > 16 Then
            frmMain.DGE.Draw_GiveInputSurface 0, Texture_2DGrid_32: RE
        ElseIf GridWidth > 8 Then
            frmMain.DGE.Draw_GiveInputSurface 0, Texture_2DGrid_16: RE
        Else
            frmMain.DGE.Draw_GiveInputSurface 0, Texture_2DGrid_8: RE
        End If
        frmMain.DGE.Draw_GiveInputColor 0.18, 0.18, 0.18, 1: RE
        frmMain.DGE.Draw_GiveInputSourceRectangleUV1 MinU / Tools_GetGridSize, MaxU / Tools_GetGridSize, MinV / Tools_GetGridSize, MaxV / Tools_GetGridSize: RE
        frmMain.DGE.Draw_RenderQuad Views(ViewIndex).DrawSurface, DrawShader_1Tex, 0, 0, 1, 0, 0, 1, 0: RE
    Else
        frmMain.DGE.DrawSurface_FillWithColor Views(ViewIndex).DrawSurface, 0, 0, 0, 1: RE
    End If
    
    'Draw center
    ScreenPos(0) = Convert3DTo2D(MakeVector3(0, 0, 0), ViewIndex)
    DrawVerticalLineInPixels ViewIndex, ScreenPos(0), 3, MakeVector4(0.5, 0.5, 0.5, 1), True, False
    DrawHorizontalLineInPixels ViewIndex, ScreenPos(0), 3, MakeVector4(0.5, 0.5, 0.5, 1), True, False
    
    'Place the camera
    Dim OrthoDist As Double
    OrthoDist = (frmMain.DGE.Enviroment_GetFarClipPlane - frmMain.DGE.Enviroment_GetNearClipPlane) / 2: RE
    frmMain.DGE.Camera_Place Camera_Orthogonal, FlatTarget.X - (DepthAxis.X * OrthoDist), FlatTarget.Y - (DepthAxis.Y * OrthoDist), FlatTarget.Z - (DepthAxis.Z * OrthoDist), FlatTarget.X, FlatTarget.Y, FlatTarget.Z, UpAxis.X, UpAxis.Y, UpAxis.Z: RE
    frmMain.DGE.Camera_SetHalfWidth Camera_Orthogonal, Abs(MaxU - MinU) / 2
    frmMain.DGE.Camera_SetHalfHeight Camera_Orthogonal, Abs(MaxV - MinV) / 2
    
    'Render the complete wire frame
    PlaceInstanceAtUnit Geometry_GetGridInstance
    frmMain.DGE.Camera_RenderInstance Camera_Orthogonal, Views(ViewIndex).DrawSurface, Geometry_GetGridInstance, MaterialShader_WireFrameNonSelection, 1, 2, False, 0, 1, 0, 1, -1: RE
    PlaceInstance Geometry_GetGridInstance, SelectionSystem, ApproximateFractionPoint(SelectionTranslation)
    frmMain.DGE.Camera_RenderInstance Camera_Orthogonal, Views(ViewIndex).DrawSurface, Geometry_GetGridInstance, MaterialShader_WireFrameSelection, 1, 2, False, 1, 1, 1, 1, -1: RE
    
    'Draw the draw target
    ScreenPos(0) = Convert3DTo2D(ApproximateFractionPoint(DrawTarget), ViewIndex)
    Dim DrawTargetOpacity As Double
    If Tools_GetCurrentTool = ToolEnum.SetDrawTarget Then
        DrawTargetOpacity = 0.6 + (Sin(Timer * 8) * 0.4)
    Else
        DrawTargetOpacity = 0.4
    End If
    DrawVerticalLineInPixels ViewIndex, ScreenPos(0), 3, MakeVector4(0.7, 0.7, 0, DrawTargetOpacity), False, True
    DrawHorizontalLineInPixels ViewIndex, ScreenPos(0), 3, MakeVector4(0.7, 0.7, 0, DrawTargetOpacity), False, True
    
    ScreenPos(0) = Convert3DTo2D(ApproximateFractionPoint(RoundedMousePosition_3D), ViewIndex)
    ScreenPos(1) = Convert3DTo2D(ApproximateFractionPoint(StartDragMousePosition_3D), ViewIndex)
    
    'Draw rounded mouse pointer
    If Tools_IsRounded And Views(LastActiveViewIndex).ViewType >= ViewType_Left And Views(LastActiveViewIndex).ViewType <= ViewType_Front Then
        DrawFilledCircleInPixels ViewIndex, ScreenPos(0), 6, MakeVector4(1, 1, 1, 1)
    End If
    
    Select Case DragState_Get
    'Case DragStateEnum.Depth
    '    'Draw it's depth line
    '    DrawLineInTwips ViewIndex, ScreenPos(0), ScreenPos(1), 1.5, MakeVector4(1, 1, 1, 1), True
    Case DragStateEnum.Rectangle
        If ViewIndex = LastMouseDownViewIndex Then
            'Draw rectangle
            DrawBoxInPixels ViewIndex, ScreenPos(0), ScreenPos(1), 2, MakeVector4(1, 1, 1, 1)
            'Draw text in the rectangle
            Select Case Tools_GetCurrentTool
            Case ToolEnum.SelectCells
                If LastShift = 2 Then
                    PrintLine_View ViewIndex, "Include", Min_Double(ScreenPos(0).X, ScreenPos(1).X), Min_Double(ScreenPos(0).Y, ScreenPos(1).Y) + 10, MakeVector4(1, 1, 1, 1)
                ElseIf LastShift = 4 Then
                    PrintLine_View ViewIndex, "Exclude", Min_Double(ScreenPos(0).X, ScreenPos(1).X), Min_Double(ScreenPos(0).Y, ScreenPos(1).Y) + 10, MakeVector4(1, 1, 1, 1)
                End If
            End Select
        End If
    Case DragStateEnum.Box_Base, DragStateEnum.Box_SetHeight
        'Draw rectangle
        DrawBoxInPixels ViewIndex, ScreenPos(0), ScreenPos(1), 2, MakeVector4(1, 1, 1, 1)
    Case DragStateEnum.InfinitePlane_2D
        'Draw line
        DrawLineInPixels ViewIndex, ScreenPos(0), ScreenPos(1), 6, MakeVector4(1, 1, 0, 1), True
    End Select
    
    'Draw dimension arrows
    'Right axis
    Color = GetColorOfDimension(RightAxis)
    PrintLine_View ViewIndex, GetNameOfDimension(RightAxis), 46, Views(ViewIndex).ContentRect.Height - 21, Color
    DrawLineInPixels ViewIndex, MakeVector2(24, Views(ViewIndex).ContentRect.Height - 20), MakeVector2(46, Views(ViewIndex).ContentRect.Height - 20), 5, Color, True
    'Up axis
    Color = GetColorOfDimension(UpAxis)
    PrintLine_View ViewIndex, GetNameOfDimension(UpAxis), 10, Views(ViewIndex).ContentRect.Height - 56, Color
    DrawLineInPixels ViewIndex, MakeVector2(20, Views(ViewIndex).ContentRect.Height - 24), MakeVector2(20, Views(ViewIndex).ContentRect.Height - 46), 5, Color, True
End Sub

Private Sub DrawBoxInPixels(ViewIndex As Integer, A As Vector2, B As Vector2, Thickness As Double, Color As Vector4)
    Dim MinX As Double
    Dim MaxX As Double
    Dim MinY As Double
    Dim MaxY As Double
    MinX = Min_Double(A.X, B.X)
    MaxX = Max_Double(A.X, B.X)
    MinY = Min_Double(A.Y, B.Y)
    MaxY = Max_Double(A.Y, B.Y)
    DrawLineInPixels ViewIndex, MakeVector2(MinX - (Thickness * 0.5), MinY), MakeVector2(MaxX + (Thickness * 0.5), MinY), Thickness, Color, False
    DrawLineInPixels ViewIndex, MakeVector2(MinX - (Thickness * 0.5), MaxY), MakeVector2(MaxX + (Thickness * 0.5), MaxY), Thickness, Color, False
    DrawLineInPixels ViewIndex, MakeVector2(MinX, MinY - (Thickness * 0.5)), MakeVector2(MinX, MaxY + (Thickness * 0.5)), Thickness, Color, False
    DrawLineInPixels ViewIndex, MakeVector2(MaxX, MinY - (Thickness * 0.5)), MakeVector2(MaxX, MaxY + (Thickness * 0.5)), Thickness, Color, False
End Sub

Private Sub DrawFilledCircleInPixels(ViewIndex As Integer, Center As Vector2, Radius As Double, Color As Vector4)
    frmMain.DGE.Draw_GiveInputColor Color.X, Color.Y, Color.Z, Color.W
    frmMain.DGE.Draw_RenderQuad Views(ViewIndex).DrawSurface, DrawShader_Circle, Lerp(-1, 1, Center.X / Views(ViewIndex).ContentRect.Width), Lerp(-1, 1, Center.Y / Views(ViewIndex).ContentRect.Height), (Radius * 2) / Views(ViewIndex).ContentRect.Width, 0, 0, (Radius * 2) / Views(ViewIndex).ContentRect.Height, 1
End Sub

Private Sub DrawCircleInPixels(ViewIndex As Integer, Center As Vector2, OutterRadius As Double, Thickness As Double, Color As Vector4)
    frmMain.DGE.Draw_GiveInputColor Color.X, Color.Y, Color.Z, Color.W
    frmMain.DGE.Draw_GiveInputVector 0, (OutterRadius - Thickness) / OutterRadius, (OutterRadius - Thickness) / OutterRadius, 0, 0
    frmMain.DGE.Draw_RenderQuad Views(ViewIndex).DrawSurface, DrawShader_Circle_EllipseCut, Lerp(-1, 1, Center.X / Views(ViewIndex).ContentRect.Width), Lerp(-1, 1, Center.Y / Views(ViewIndex).ContentRect.Height), (OutterRadius * 2) / Views(ViewIndex).ContentRect.Width, 0, 0, (OutterRadius * 2) / Views(ViewIndex).ContentRect.Height, 1
End Sub

Private Sub DrawLineInPixels(ViewIndex As Integer, A As Vector2, B As Vector2, Thickness As Double, Color As Vector4, SoftLine As Boolean)
    Dim AC As Vector2
    Dim BC As Vector2
    Dim Center As Vector2
    Dim XAxis As Vector2
    Dim YAxis As Vector2
    Dim DrawShader As Long
    
    'Convert from twips to -1 to +1 scale
    AC.X = Lerp(-1, 1, A.X / Views(ViewIndex).ContentRect.Width)
    AC.Y = Lerp(-1, 1, A.Y / Views(ViewIndex).ContentRect.Height)
    BC.X = Lerp(-1, 1, B.X / Views(ViewIndex).ContentRect.Width)
    BC.Y = Lerp(-1, 1, B.Y / Views(ViewIndex).ContentRect.Height)
    
    'Draw the line
    Center = MulVector2(AddVector2(AC, BC), 0.5)
    XAxis = MulVector2(ToVector2(AC, BC), 0.5)
    YAxis = NormalVector2(MakeVector2(XAxis.Y, -XAxis.X))
    If AbsVector2(XAxis) > 0 Then
        frmMain.DGE.Draw_GiveInputColor Color.X, Color.Y, Color.Z, Color.W
        If SoftLine Then
            DrawShader = DrawShader_ThinLine
        Else
            DrawShader = DrawShader_Color
        End If
        frmMain.DGE.Draw_RenderQuad Views(ViewIndex).DrawSurface, DrawShader, Center.X, Center.Y, XAxis.X, XAxis.Y, YAxis.X * Thickness / Views(ViewIndex).ContentRect.Width, YAxis.Y * Thickness / Views(ViewIndex).ContentRect.Height, 1
    End If
End Sub

Private Sub DrawVerticalLineInPixels(ViewIndex As Integer, Point As Vector2, Thickness As Double, Color As Vector4, SoftLine As Boolean, Dotted As Boolean)
    Dim DrawShader As Long
    
    'Draw the line
    frmMain.DGE.Draw_GiveInputColor Color.X, Color.Y, Color.Z, Color.W
    If Dotted Then
        If SoftLine Then
            DrawShader = DrawShader_ThinLine_Dotted
        Else
            DrawShader = DrawShader_Stripes
        End If
        frmMain.DGE.Draw_GiveInputSourceRectangleUV1 -Point.Y / 2, (Views(ViewIndex).ContentRect.Height - Point.Y) / 2, 0, 0
    Else
        If SoftLine Then
            DrawShader = DrawShader_ThinLine
        Else
            DrawShader = DrawShader_Color
        End If
    End If
    frmMain.DGE.Draw_RenderQuad Views(ViewIndex).DrawSurface, DrawShader, Lerp(-1, 1, Point.X / Views(ViewIndex).ContentRect.Width), 0, 0, 1, Thickness / Views(ViewIndex).ContentRect.Width, 0, 1
End Sub

Private Sub DrawHorizontalLineInPixels(ViewIndex As Integer, Point As Vector2, Thickness As Double, Color As Vector4, SoftLine As Boolean, Dotted As Boolean)
    Dim DrawShader As Long
    
    'Draw the line
    frmMain.DGE.Draw_GiveInputColor Color.X, Color.Y, Color.Z, Color.W
    If Dotted Then
        If SoftLine Then
            DrawShader = DrawShader_ThinLine_Dotted
        Else
            DrawShader = DrawShader_Stripes
        End If
        frmMain.DGE.Draw_GiveInputSourceRectangleUV1 -Point.X / 2, (Views(ViewIndex).ContentRect.Width - Point.X) / 2, 0, 0
    Else
        If SoftLine Then
            DrawShader = DrawShader_ThinLine
        Else
            DrawShader = DrawShader_Color
        End If
    End If
    frmMain.DGE.Draw_RenderQuad Views(ViewIndex).DrawSurface, DrawShader, 0, Lerp(-1, 1, Point.Y / Views(ViewIndex).ContentRect.Height), 1, 0, 0, Thickness / Views(ViewIndex).ContentRect.Height, 1
End Sub

Private Sub PrintLine_View(ViewIndex As Integer, Message As String, StartX As Double, StartY As Double, Color As Vector4)
    Dim I As Long
    Dim OutW As Long
    Dim OutH As Long
    Dim StringLength As Double
    Dim CharCode As Integer
    Dim X As Double
    Dim Y As Double
    Dim OutputDrawSurface As Long
    OutputDrawSurface = Views(ViewIndex).DrawSurface
    OutW = Views(ViewIndex).ContentRect.Width
    OutH = Views(ViewIndex).ContentRect.Height
    StringLength = Len(Message)
    For I = 1 To StringLength
        frmMain.DGE.Draw_GiveInputSurface 0, FontAtlas_LucidaConsole_10: RE
        CharCode = Asc(Mid(Message, I, 1))
        X = Int(CharCode Mod 16)
        Y = Int(CharCode / 16)
        frmMain.DGE.Draw_GiveInputSourceRectangleUV1 X * 0.0625, (X + 1) * 0.0625, Y * 0.0625, (Y + 1) * 0.0625: RE
        frmMain.DGE.Draw_GiveInputColor Color.X, Color.Y, Color.Z, Color.W: RE
        frmMain.DGE.Draw_RenderQuad OutputDrawSurface, DrawShader_Clamped_NN_1Tex, Lerp(-1, 1, StartX / Views(ViewIndex).ContentRect.Width) + (I * (FontTileWidth / OutW)), Lerp(-1, 1, StartY / Views(ViewIndex).ContentRect.Height), FontTileWidth / OutW, 0, 0, FontTileHeight / OutH, 1: RE
    Next I
End Sub

Private Sub PrintCenteredLine_Final(Message As String, CenterX As Double, CenterY As Double, Color As Vector4)
    Dim I As Long
    Dim StringLength As Double
    Dim CharCode As Integer
    Dim X As Double
    Dim Y As Double
    StringLength = Len(Message)
    For I = 1 To StringLength
        frmMain.DGE.Draw_GiveInputSurface 0, FontAtlas_LucidaConsole_10: RE
        CharCode = Asc(Mid(Message, I, 1))
        X = Int(CharCode Mod 16)
        Y = Int(CharCode / 16)
        frmMain.DGE.Draw_GiveInputSourceRectangleUV1 X * 0.0625, (X + 1) * 0.0625, Y * 0.0625, (Y + 1) * 0.0625: RE
        frmMain.DGE.Draw_GiveInputColor Color.X, Color.Y, Color.Z, Color.W: RE
        frmMain.DGE.Draw_RenderQuad DrawSurface_Final, DrawShader_Clamped_NN_1Tex, Lerp(-1, 1, CenterX / OutputWidth) + ((I - (StringLength / 2)) * (FontTileWidth / OutputWidth)), Lerp(-1, 1, CenterY / frmMain.DGE.Height), FontTileWidth / OutputWidth, 0, 0, FontTileHeight / OutputHeight, 1: RE
    Next I
End Sub

Private Sub DrawTabSection_Final(SubArea As ViewRect, MinU As Double, MaxU As Double, TabColor As Vector4)
    frmMain.DGE.Draw_GiveInputSurface 0, Texture_Tab: RE
    frmMain.DGE.Draw_GiveInputSourceRectangleUV1 MinU, MaxU, 0, 1: RE
    frmMain.DGE.Draw_GiveInputColor TabColor.X, TabColor.Y, TabColor.Z, TabColor.W: RE
    frmMain.DGE.Draw_RenderQuad DrawSurface_Final, DrawShader_Clamped_NN_1Tex, Lerp(-1, 1, (SubArea.Left + (SubArea.Width / 2)) / frmMain.DGE.Width), Lerp(-1, 1, (SubArea.Top + (SubArea.Height / 2)) / frmMain.DGE.Height), SubArea.Width / frmMain.DGE.Width, 0, 0, SubArea.Height / frmMain.DGE.Height, 1
End Sub

Private Sub DrawTab_Final(Message As String, Area As ViewRect, TabColor As Vector4, TextColor As Vector4)
    Dim LeftArea As ViewRect
    Dim CenterArea As ViewRect
    Dim RightArea As ViewRect
    
    'Calculate where to place it
    If Area.Width > TabTexWidth Then
        'Stretch the center to fit between the sides
        LeftArea = Area
        CenterArea = Area
        RightArea = Area
        LeftArea.Width = TabTexWidth / 2
        RightArea.Width = TabTexWidth / 2
        CenterArea.Width = Area.Width - (LeftArea.Width + RightArea.Width)
        CenterArea.Left = LeftArea.Left + LeftArea.Width
        RightArea.Left = CenterArea.Left + CenterArea.Width
        
        'Draw the background of the tab
        DrawTabSection_Final LeftArea, 0, 0.5, TabColor
        DrawTabSection_Final CenterArea, 0.5, 0.5, TabColor
        DrawTabSection_Final RightArea, 0.5, 1, TabColor
    Else
        'Stretch whole tab
        CenterArea = Area
        DrawTabSection_Final CenterArea, 0, 1, TabColor
    End If
    
    'Print it's caption
    PrintCenteredLine_Final Message, Area.Left + (Area.Width / 2) - 3, Area.Top + (Area.Height / 2), TextColor
End Sub

Private Function GetBeamFromMouseLocation(ViewIndex As Integer, LocalMousePos As Vector2) As Beam3
    Dim System As Matrix3
    Dim ViewRect As ViewRect
    Dim NormalizedMousePos As Vector2
    System = Polar_GetAxisSystem(CameraDirection)
    ViewRect = Views(ViewIndex).ContentRect
    NormalizedMousePos = MakeVector2((LocalMousePos.X - (ViewRect.Width / 2)) / (ViewRect.Width / 2), ((ViewRect.Height / 2) - LocalMousePos.Y) / (ViewRect.Width / 2))
    GetBeamFromMouseLocation = MakeBeam3(CameraLocation, AddVector3(AddVector3(MulVector3(System.XAxis, NormalizedMousePos.X), MulVector3(System.YAxis, NormalizedMousePos.Y)), System.ZAxis))
End Function

Public Sub Views_MouseDown(Button As Integer, Shift As Integer, X As Double, Y As Double)
    Dim VCIndex As ViewComponentIndex
    Dim LocalMousePos As Vector2
    LastShift = Shift
    VCIndex = GetComponentIndexFromMouseCoordinate(X, Y)
    If VCIndex.ViewIndex > -1 Then
        LastMouseDownViewIndex = VCIndex.ViewIndex
        LocalMousePos = MakeVector2(X - Views(VCIndex.ViewIndex).ContentRect.Left, Y - Views(VCIndex.ViewIndex).ContentRect.Top)
        LastMouseScreenPosition = LocalMousePos
        If VCIndex.ComponentType = ComponentType_Tab Then
            'Convert the view to a new type
            Views(VCIndex.ViewIndex).ViewType = VCIndex.ComponentIndex
            DisableTools = True 'Prevent doing things on the mouse up event when changing view
        ElseIf VCIndex.ComponentType = ComponentType_None Then
            'Initial steps
            If Views(VCIndex.ViewIndex).ViewType = ViewType_Perspective Then
                'Perspective
                If Button = 1 Then
                    MDLeft_P = True
                    If Not DisableTools Then
                        Tools_MouseDown_InitialPerspective Shift, GetBeamFromMouseLocation(VCIndex.ViewIndex, LocalMousePos)
                    End If
                ElseIf Button = 2 Then
                    MDRight_P = True
                End If
            ElseIf Views(VCIndex.ViewIndex).ViewType >= ViewType_Left And Views(VCIndex.ViewIndex).ViewType <= ViewType_Front Then
                'Flat
                Update3DMousePosition
                If Button = 1 Then
                    MDLeft_Flat = True
                    LastMouseDownScreenPosition = LocalMousePos
                    StartDragMousePosition_3D = RoundedMousePosition_3D
                    If Not DisableTools Then
                        Tools_MouseDown_InitialFlat Shift, RoundedMousePosition_3D
                    End If
                ElseIf Button = 2 Then
                    MDRight_Flat = True
                    DisableTools = True
                End If
            End If
        End If
        PreviousMouseScreenPosition = LocalMousePos
    End If
    'Continuing steps independent of view
    If Not DisableTools Then
        If Button = 1 And DragState_Get = DragStateEnum.Box_SetHeight Then
            Tools_CompleteBox Shift, DragBox
            DragState_Set DragStateEnum.None
            UnlockMouse
        End If
    End If
End Sub

Public Sub Views_MouseMove(Button As Integer, Shift As Integer, X As Double, Y As Double)
    Dim DragOffset_3D As Vector3
    Dim TotalDragOffset_3D As FractionPoint
    Dim ViewIndex As Integer
    Dim LocalMousePos As Vector2
    Dim Size As Double
    LastShift = Shift
    If MDLeft_P Or MDRight_P Or MDLeft_Flat Or MDRight_Flat Then
        ViewIndex = LastMouseDownViewIndex
    Else
        ViewIndex = GetComponentIndexFromMouseCoordinate(X, Y).ViewIndex
    End If
    If ViewIndex > -1 Then
        LocalMousePos = MakeVector2(X - Views(ViewIndex).ContentRect.Left, Y - Views(ViewIndex).ContentRect.Top)
        LastMouseScreenPosition = LocalMousePos
        If DragState_Get <> DragStateEnum.Box_SetHeight Then
            LastActiveViewIndex = ViewIndex
        End If
        Update3DMousePosition
        TotalDragOffset_3D = SubFractionPoint(RoundedMousePosition_3D, StartDragMousePosition_3D)
        Select Case DragState_Get
        Case DragStateEnum.Box_SetHeight
            DragBox = ExtendFractionBoxUsingPoint(DragBase, RoundedMousePosition_3D)
        Case DragStateEnum.Offset
            SelectionTranslation = TotalDragOffset_3D
        Case DragStateEnum.InfinitePlane_2D
            Dim Normal As FractionNormal
            Select Case Views(ViewIndex).ViewType
            Case ViewType_Left, ViewType_Right
                Normal = SimplifyFractionNormal(MakeFractionNormal_Args( _
                    0, _
                    -TotalDragOffset_3D.Z.Numerator * TotalDragOffset_3D.Y.Denominator, _
                    TotalDragOffset_3D.Y.Numerator * TotalDragOffset_3D.Z.Denominator))
            Case ViewType_Top, ViewType_Bottom
                Normal = SimplifyFractionNormal(MakeFractionNormal_Args( _
                    -TotalDragOffset_3D.Z.Numerator * TotalDragOffset_3D.X.Denominator, _
                    0, _
                    TotalDragOffset_3D.X.Numerator * TotalDragOffset_3D.Z.Denominator))
            Case ViewType_Back, ViewType_Front
                Normal = SimplifyFractionNormal(MakeFractionNormal_Args( _
                    -TotalDragOffset_3D.Y.Numerator * TotalDragOffset_3D.X.Denominator, _
                    TotalDragOffset_3D.X.Numerator * TotalDragOffset_3D.Y.Denominator, _
                    0))
            End Select
            PreviewPlane = MakeFractionPlane(StartDragMousePosition_3D, Normal)
        End Select
        If MDLeft_Flat And MDRight_Flat Then
            'Zoom
            Views(ViewIndex).FlatZoom = Views(ViewIndex).FlatZoom - ((LocalMousePos.Y - PreviousMouseScreenPosition.Y) * Views(ViewIndex).FlatZoom * MouseZoomSpeed)
            If Views(ViewIndex).FlatZoom > 256 Then Views(ViewIndex).FlatZoom = 256
            If Views(ViewIndex).FlatZoom < 0.0625 Then Views(ViewIndex).FlatZoom = 0.0625
        ElseIf MDRight_Flat Then
            'Flat pan
            DragOffset_3D = DivVector3(Transform2DTo3D(SubVector2(LocalMousePos, PreviousMouseScreenPosition), LastActiveViewIndex, False), Views(LastActiveViewIndex).Zoom)
            FlatTarget = SubVector3(FlatTarget, DragOffset_3D)
        ElseIf MDRight_P Then
            'Rotate camera
            CameraDirection.Longitude = CameraDirection.Longitude + ((LocalMousePos.X - PreviousMouseScreenPosition.X) * 0.01)
            CameraDirection.Lattitude = CameraDirection.Lattitude - ((LocalMousePos.Y - PreviousMouseScreenPosition.Y) * 0.01)
        End If
        PreviousMouseScreenPosition = LocalMousePos
    End If
End Sub

Public Sub Views_MouseUp(Button As Integer, Shift As Integer, X As Double, Y As Double)
    Dim ViewIndex As Integer
    Dim LocalMousePos As Vector2
    LastShift = Shift
    ViewIndex = LastMouseDownViewIndex
    LocalMousePos = MakeVector2(X - Views(ViewIndex).ContentRect.Left, Y - Views(ViewIndex).ContentRect.Top)
    Select Case DragState_Get
    Case DragStateEnum.Offset
        Tools_CompleteTransform SelectionSystem, SelectionTranslation
        DragState_Set DragStateEnum.None
    Case DragStateEnum.InfinitePlane_2D
        If IsRealFractionPlane(PreviewPlane) Then
            Tools_CompleteInfinitePlane Shift, PreviewPlane
        End If
        DragState_Set DragStateEnum.None
    Case DragStateEnum.Box_Base
        DragBase = MakeFractionBoxFromTwoPoints(StartDragMousePosition_3D, RoundedMousePosition_3D)
        DragState_Set DragStateEnum.Box_SetHeight
    Case DragStateEnum.Rectangle
        Dim InfiniteBox As FractionBox
        InfiniteBox = MakeFractionBoxFromTwoPoints(StartDragMousePosition_3D, RoundedMousePosition_3D)
        Select Case GetDepthDim(LastMouseDownViewIndex)
        Case 0 'YZ
            InfiniteBox.Min.X = MakeFraction_NegInf()
            InfiniteBox.Max.X = MakeFraction_PosInf()
        Case 1 'XZ
            InfiniteBox.Min.Y = MakeFraction_NegInf()
            InfiniteBox.Max.Y = MakeFraction_PosInf()
        Case Else 'XY
            InfiniteBox.Min.Z = MakeFraction_NegInf()
            InfiniteBox.Max.Z = MakeFraction_PosInf()
        End Select
        Tools_CompleteRectangle Shift, InfiniteBox
        DragState_Set DragStateEnum.None
    Case Else
        DragState_Set DragStateEnum.None
    End Select
    If Button = 1 Then
        MDLeft_P = False
        MDLeft_Flat = False
    ElseIf Button = 2 Then
        MDRight_P = False
        MDRight_Flat = False
    End If
    DisableTools = False
End Sub

Public Sub Views_KeyDown(KeyCode As Integer, Shift As Integer)
    LastShift = Shift
End Sub
    
Public Sub Views_KeyUp(KeyCode As Integer, Shift As Integer)
    LastShift = Shift
End Sub

Public Sub Views_Tick()
    If Not (KeyDown_Truth(vbKeyControl) Or KeyDown_Truth(vbKeyMenu)) Then
        'Turn camera
        CameraDirection.Longitude = CameraDirection.Longitude + ((KeyDown_ZeroToOne(vbKeyRight) - KeyDown_ZeroToOne(vbKeyLeft)) * 0.1)
        
        'Get orientation
        Dim CameraSystem As Matrix3
        CameraSystem = Polar_GetAxisSystem(CameraDirection)
        
        'Move camera
        Dim WalkDirection As Vector3
        WalkDirection = NormalVector3(MakeVector3(CameraSystem.ZAxis.X, 0, CameraSystem.ZAxis.Z))
        Dim TranslationSpeed As Double
        If KeyDown_Truth(vbKeyShift) Then
            TranslationSpeed = 2
        Else
            TranslationSpeed = 0.5
        End If
        CameraLocation = AddVector3(CameraLocation, MulVector3(CameraSystem.ZAxis, (KeyDown_ZeroToOne(vbKeyW) - KeyDown_ZeroToOne(vbKeyS)) * TranslationSpeed))
        CameraLocation = AddVector3(CameraLocation, MulVector3(CameraSystem.XAxis, (KeyDown_ZeroToOne(vbKeyD) - KeyDown_ZeroToOne(vbKeyA)) * TranslationSpeed))
        CameraLocation = AddVector3(CameraLocation, MulVector3(CameraSystem.YAxis, (KeyDown_ZeroToOne(vbKeyE) - KeyDown_ZeroToOne(vbKeyQ)) * TranslationSpeed))
        CameraLocation = AddVector3(CameraLocation, MulVector3(WalkDirection, (KeyDown_ZeroToOne(vbKeyUp) - KeyDown_ZeroToOne(vbKeyDown)) * TranslationSpeed))
    End If
End Sub

Private Sub Render3DView(ViewIndex As Integer)
    Dim CameraSystem As Matrix3
    CameraSystem = Polar_GetAxisSystem(CameraDirection)
    
    'Place camera
    frmMain.DGE.Camera_Place Camera_Perspective, CameraLocation.X, CameraLocation.Y, CameraLocation.Z, CameraLocation.X + CameraSystem.ZAxis.X, CameraLocation.Y + CameraSystem.ZAxis.Y, CameraLocation.Z + CameraSystem.ZAxis.Z, CameraSystem.YAxis.X, CameraSystem.YAxis.Y, CameraSystem.YAxis.Z: RE
    
    'Update sunlight
    frmMain.DGE.LightSource_SetDirection LightSource_Sun, CameraSystem.ZAxis.X, CameraSystem.ZAxis.Y, CameraSystem.ZAxis.Z, 0, 1, 0
    
    'Place selection
    PlaceInstance Geometry_GetGridInstance, SelectionSystem, ApproximateFractionPoint(SelectionTranslation)
    
    'Render the whole scene
    frmMain.DGE.Camera_SetDebugViewVisibility Camera_Perspective, 1, 1: RE
    frmMain.DGE.Camera_RenderScene Camera_Perspective, Views(ViewIndex).DrawSurface, 0: RE
    
    'Render the complete wire frame
    'PlaceInstanceAtUnit Geometry_GetGridInstance
    'frmMain.DGE.Camera_RenderInstance Camera_Perspective, Views(ViewIndex).DrawSurface, Geometry_GetGridInstance, MaterialShader_WireFrameNonSelection3D, 1, 2, False, 0, 1, 0, 1, -1: RE
    PlaceInstance Geometry_GetGridInstance, SelectionSystem, ApproximateFractionPoint(SelectionTranslation)
    frmMain.DGE.Camera_RenderInstance Camera_Perspective, Views(ViewIndex).DrawSurface, Geometry_GetGridInstance, MaterialShader_WireFrame3D, 1, 2, False, 1, 1, 1, 1, -1: RE
End Sub
