Imports System.Windows.Forms

Imports OpenTK
Imports OpenTK.Mathematics

Namespace CameraN

    Public Class Camera

        Public Event NeedSelectedObject(sender As Object, e As NeedSelectedObjectEventArgs)
        Public Event PerspectiveChanged(sender As Object, e As EventArgs)

        'P R I V A T E   F I E L D S

        Private ReadOnly TAU As Single = Math.PI * 2
        Private myCamMode As CameraMode = CameraMode.FLY
        Private pos As New Vector3(-5000.0F, 3000.0F, 4000.0F)
        Private myLookat As New Vector3(0F, 0F, 0F)
        Private myFarPoint As New Vector3(0F, 0F, 0F)
        Private myNearPoint As New Vector3(0F, 0F, 0F)
        Private lastMouseX As Integer = -1, lastMouseY As Integer = -1
        Private CamAngleX As Single = 0F, CamAngleY As Single = -(Math.PI / 2)
        Private resetMouse As Boolean = True

        Private orbitDistance As Single = 500.0F
        Private orbitTheta As Single = 0.0F, orbitPhi As Single = 0.0F

        Private currentLookDirection As LookDirection
        Private lookPositions() As Vector3 = {
                New Vector3(0, 12500, 0),
                New Vector3(0, -12500, 0),
                New Vector3(-12500, 0, 0),
                New Vector3(12500, 0, 0),
                New Vector3(0, 0, 12500),
                New Vector3(0, 0, -12500)
            }

        'A U T O M A T I C   P R O P E R T I E S

        Public Property CamSpeedMultiplier As Single = 1

        'P R O P E R T I E S

        Public ReadOnly Property CamMode As CameraMode
            Get
                Return myCamMode
            End Get
        End Property

        Public ReadOnly Property Yaw As Single
            Get
                Return CamAngleX
            End Get
        End Property

        Public ReadOnly Property Pitch As Single
            Get
                Return CamAngleY
            End Get
        End Property

        Public ReadOnly Property Yaw_Degrees As Single
            Get
                Return CamAngleX * (180.0F / 3.14159274F)
            End Get
        End Property

        Public ReadOnly Property Pitch_Degrees As Single
            Get
                Return CamAngleY * (180.0F / 3.14159274F)
            End Get
        End Property

        Public Property Position As Vector3
            Get
                Return pos
            End Get
            Set(value As Vector3)
                pos = value
            End Set
        End Property

        Public Property LookAt As Vector3
            Get
                Return myLookat
            End Get
            Set(value As Vector3)
                myLookat = value
            End Set
        End Property

        Public Property NearPoint As Vector3
            Get
                Return myNearPoint
            End Get
            Set(value As Vector3)
                myNearPoint = value
            End Set
        End Property

        Public Property FarPoint As Vector3
            Get
                Return myFarPoint
            End Get
            Set(value As Vector3)
                myFarPoint = value
            End Set
        End Property

        'C O N S T R U C T O R

        Public Sub New()
            SetRotationFromLookAt()
        End Sub

        'F E A T U R E S

        Private Function Clampf(value As Single, min As Single, max As Single) As Single
            Return If(value > max, max, If(value < min, min, value))
        End Function

        Private Sub OrientateCam(ang As Single, ang2 As Single)
            Dim CamLX As Single = CSng(Math.Sin(ang)) * CSng(Math.Sin(-ang2))
            Dim CamLY As Single = CSng(Math.Cos(ang2))
            Dim CamLZ As Single = CSng(-Math.Cos(ang)) * CSng(Math.Sin(-ang2))

            myLookat.X = pos.X + (-CamLX) * 100.0F
            myLookat.Y = pos.Y + (-CamLY) * 100.0F
            myLookat.Z = pos.Z + (-CamLZ) * 100.0F
        End Sub

        Private Sub OffsetCam(xAmt As Integer, yAmt As Integer, zAmt As Integer)
            Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2)
            Dim CamLX As Single = CSng(Math.Sin(CamAngleX)) * CSng(Math.Cos(-pitch_Renamed))
            Dim CamLY As Single = CSng(Math.Sin(pitch_Renamed))
            Dim CamLZ As Single = CSng(-Math.Cos(CamAngleX)) * CSng(Math.Cos(-pitch_Renamed))
            pos.X = pos.X + xAmt * (CamLX) * CamSpeedMultiplier
            pos.Y = pos.Y + yAmt * (CamLY) * CamSpeedMultiplier
            pos.Z = pos.Z + zAmt * (CamLZ) * CamSpeedMultiplier
        End Sub

        Public Sub Move(y As Single, ByRef camMtx As Matrix4)
            OffsetCam(0, y, 0)
            OrientateCam(CamAngleX, CamAngleY)
            UpdateMatrix(camMtx)
        End Sub

        Public Sub Move(x As Single, z As Single, ByRef camMtx As Matrix4)
            UpdateCameraOffsetDirectly(x, z, camMtx)
            OrientateCam(CamAngleX, CamAngleY)
            UpdateMatrix(camMtx)
        End Sub

        Public Sub SetRotationFromLookAt()
            Dim x_diff As Single = myLookat.X - pos.X
            Dim y_diff As Single = myLookat.Y - pos.Y
            Dim z_diff As Single = myLookat.Z - pos.Z
            Dim dist As Single = CSng(Math.Sqrt(x_diff * x_diff + y_diff * y_diff + z_diff * z_diff))
            If z_diff = 0 Then
                z_diff = 0.001F
            End If
            Dim nxz_ratio As Single = -x_diff / z_diff
            If z_diff < 0 Then
                CamAngleX = CSng(Math.Atan(nxz_ratio) + Math.PI)
            Else
                CamAngleX = CSng(Math.Atan(nxz_ratio))
            End If
            CamAngleY = -3.1459F - (CSng(Math.Asin(y_diff / dist)) - 1.57F)
        End Sub

        Public Sub ResetOrbitToSelectedObject()
            Dim objs As ICameraPoint() = GetSelectedObject()
            If objs?.Length > 0 Then
                orbitTheta = -(CalculateCenterYRotationOfObjects(objs) * (CSng(Math.PI) / 180.0F))
                orbitPhi = -0.3F
                orbitDistance = 1200.0F
            End If
        End Sub

        Public Sub UpdateOrbitCamera(ByRef cameraMatrix As Matrix4)
            If myCamMode = CameraMode.ORBIT Then
                Dim objs As ICameraPoint() = GetSelectedObject()
                If objs?.Length > 0 Then
                    Dim centerPos As Numerics.Vector3 = CalculateCenterPositionOfObjects(objs)
                    pos.X = centerPos.X + CSng(Math.Cos(orbitPhi) * -Math.Sin(orbitTheta) * orbitDistance)
                    pos.Y = centerPos.Y + CSng(-Math.Sin(orbitPhi) * orbitDistance)
                    pos.Z = centerPos.Z + CSng(Math.Cos(orbitPhi) * Math.Cos(orbitTheta) * orbitDistance)
                    myLookat.X = centerPos.X
                    myLookat.Y = centerPos.Y
                    myLookat.Z = centerPos.Z
                    UpdateMatrix(cameraMatrix)
                    SetRotationFromLookAt()
                End If
            End If
        End Sub

        Public Function IsOrbitCamera() As Boolean
            Return myCamMode = CameraMode.ORBIT
        End Function

        Public Sub SetCameraMode(mode As CameraMode, ByRef cameraMatrix As Matrix4)
            myCamMode = mode
            If IsOrbitCamera() Then
                ResetOrbitToSelectedObject()
                UpdateOrbitCamera(cameraMatrix)
            End If
        End Sub

        Public Sub SetCameraMode_LookDirection(dir As LookDirection, ByRef cameraMatrix As Matrix4)
            myCamMode = CameraMode.LOOK_DIRECTION
            currentLookDirection = dir
            Select Case currentLookDirection
                Case LookDirection.Top
                    pos = lookPositions(CInt(LookDirection.Top))
                    myLookat = New Vector3(pos.X, -25000, pos.Z - 1)
                    UpdateMatrix(cameraMatrix)
                    SetRotationFromLookAt()
                Case LookDirection.Bottom
                    pos = lookPositions(CInt(LookDirection.Bottom))
                    myLookat = New Vector3(pos.X, 25000, pos.Z + 1)
                    UpdateMatrix(cameraMatrix)
                    SetRotationFromLookAt()
                Case LookDirection.Left
                    pos = lookPositions(CInt(LookDirection.Left))
                    myLookat = New Vector3(25000, pos.Y, pos.Z)
                    UpdateMatrix(cameraMatrix)
                    SetRotationFromLookAt()
                Case LookDirection.Right
                    pos = lookPositions(CInt(LookDirection.Right))
                    myLookat = New Vector3(-25000, pos.Y, pos.Z)
                    UpdateMatrix(cameraMatrix)
                    SetRotationFromLookAt()
                Case LookDirection.Front
                    pos = lookPositions(CInt(LookDirection.Front))
                    myLookat = New Vector3(pos.X, pos.Y, -25000)
                    UpdateMatrix(cameraMatrix)
                    SetRotationFromLookAt()
                Case LookDirection.Back
                    pos = lookPositions(CInt(LookDirection.Back))
                    myLookat = New Vector3(pos.X, pos.Y, 25000)
                    UpdateMatrix(cameraMatrix)
                    SetRotationFromLookAt()
            End Select
        End Sub

        Public Sub UpdateCameraMatrixWithMouse(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4)
            If myCamMode = CameraMode.ORBIT AndAlso GetSelectedObject() IsNot Nothing Then
                UpdateCameraMatrixWithMouse_ORBIT(mouseX, mouseY, cameraMatrix)
            ElseIf myCamMode = CameraMode.LOOK_DIRECTION Then
                UpdateCameraMatrixWithMouse_LOOK(pos, mouseX, mouseY, cameraMatrix)
            Else
                UpdateCameraMatrixWithMouse_FLY(mouseX, mouseY, cameraMatrix)
            End If
        End Sub

        Public Sub UpdateCameraOffsetWithMouse(orgPos As Vector3, mouseX As Integer, mouseY As Integer, w As Integer, h As Integer, ByRef cameraMatrix As Matrix4)
            If myCamMode = CameraMode.ORBIT AndAlso GetSelectedObject() IsNot Nothing Then
                UpdateCameraOffsetWithMouse_ORBIT(mouseX, mouseY, cameraMatrix)
            ElseIf myCamMode = CameraMode.LOOK_DIRECTION Then
                UpdateCameraMatrixWithMouse_LOOK(pos, mouseX, mouseY, cameraMatrix)
            Else
                UpdateCameraOffsetWithMouse_FLY(orgPos, mouseX, mouseY, w, h, cameraMatrix)
            End If
        End Sub

        Public Sub UpdateCameraMatrixWithScrollWheel(amt As Integer, ByRef cameraMatrix As Matrix4)
            If myCamMode = CameraMode.ORBIT AndAlso GetSelectedObject() IsNot Nothing Then
                UpdateCameraMatrixWithScrollWheel_ORBIT(amt, cameraMatrix)
            ElseIf myCamMode = CameraMode.LOOK_DIRECTION Then
                UpdateCameraMatrixWithScrollWheel_LOOK(amt, cameraMatrix)
            Else
                UpdateCameraMatrixWithScrollWheel_FLY(amt, cameraMatrix)
            End If
        End Sub

        Private Sub UpdateCameraMatrixWithScrollWheel_FLY(amt As Integer, ByRef cameraMatrix As Matrix4)
            OffsetCam(amt, amt, amt)
            OrientateCam(CamAngleX, CamAngleY)
            UpdateMatrix(cameraMatrix)
        End Sub

        Public Sub RaisePerspectiveChanged()
            RaiseEvent PerspectiveChanged(Me, New EventArgs)
        End Sub

        Public Sub UpdateMatrix(ByRef cameraMatrix As Matrix4)
            cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z, 0, 1, 0)
            RaisePerspectiveChanged()
        End Sub

        Private Sub UpdateCameraMatrixWithScrollWheel_LOOK(amt As Integer, ByRef cameraMatrix As Matrix4)
            OffsetCam(amt, amt, amt)
            OrientateCam(CamAngleX, CamAngleY)
            Select Case currentLookDirection
                Case LookDirection.Top
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z - 1, 0, 1, 0)
                    RaisePerspectiveChanged()
                Case LookDirection.Bottom
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z + 1, 0, 1, 0)
                    RaisePerspectiveChanged()
                Case Else
                    UpdateMatrix(cameraMatrix)
            End Select
        End Sub

        Private Sub UpdateCameraMatrixWithMouse_LOOK(orgPos As Vector3, mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4)
            If resetMouse Then
                lastMouseX = mouseX
                lastMouseY = mouseY
                resetMouse = False
            End If
            Dim MousePosX As Integer = mouseX - lastMouseX
            Dim MousePosY As Integer = mouseY - lastMouseY

            Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2)
            Dim yaw_Renamed As Double = CamAngleX - (Math.PI / 2)
            Dim CamLX As Single = CSng(Math.Sin(yaw_Renamed))
            Dim CamLY As Single = CSng(Math.Cos(pitch_Renamed))
            Dim CamLZ As Single = CSng(-Math.Cos(yaw_Renamed))

            Dim m As Single = 8.0F

            Select Case currentLookDirection
                Case LookDirection.Top
                    pos.X = orgPos.X - ((MousePosX * CamSpeedMultiplier) * (CamLX) * m) - ((MousePosY * CamSpeedMultiplier) * (CamLZ) * m)
                    pos.Z = orgPos.Z - ((MousePosX * CamSpeedMultiplier) * (CamLZ) * m) - ((MousePosY * CamSpeedMultiplier) * (CamLX) * m)
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y - 1000, pos.Z - 1, 0, 1, 0)
                    lookPositions(CInt(currentLookDirection)) = pos
                Case LookDirection.Bottom
                    pos.X = orgPos.X - ((MousePosX * CamSpeedMultiplier) * (CamLX) * m) + ((MousePosY * CamSpeedMultiplier) * (CamLZ) * m)
                    pos.Z = orgPos.Z - ((MousePosX * CamSpeedMultiplier) * (CamLZ) * m) + ((MousePosY * CamSpeedMultiplier) * (CamLX) * m)
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y + 1000, pos.Z + 1, 0, 1, 0)
                    lookPositions(CInt(currentLookDirection)) = pos
                Case LookDirection.Left
                    pos.X = orgPos.X - ((MousePosX * CamSpeedMultiplier) * (CamLX) * m)
                    pos.Y = orgPos.Y - ((MousePosY * CamSpeedMultiplier) * (-1.0F) * m)
                    pos.Z = orgPos.Z - ((MousePosX * CamSpeedMultiplier) * (CamLZ) * m)
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X + 12500, pos.Y, pos.Z, 0, 1, 0)
                    lookPositions(CInt(currentLookDirection)) = pos
                Case LookDirection.Right
                    pos.X = orgPos.X - ((MousePosX * CamSpeedMultiplier) * (CamLX) * m)
                    pos.Y = orgPos.Y - ((MousePosY * CamSpeedMultiplier) * (-1.0F) * m)
                    pos.Z = orgPos.Z - ((MousePosX * CamSpeedMultiplier) * (CamLZ) * m)
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X - 12500, pos.Y, pos.Z, 0, 1, 0)
                    lookPositions(CInt(currentLookDirection)) = pos
                Case LookDirection.Front
                    pos.X = orgPos.X - ((MousePosX * CamSpeedMultiplier) * (CamLX) * m)
                    pos.Y = orgPos.Y - ((MousePosY * CamSpeedMultiplier) * (-1.0F) * m)
                    pos.Z = orgPos.Z - ((MousePosX * CamSpeedMultiplier) * (CamLZ) * m)
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y, pos.Z - 12500, 0, 1, 0)
                    lookPositions(CInt(currentLookDirection)) = pos
                Case LookDirection.Back
                    pos.X = orgPos.X - ((MousePosX * CamSpeedMultiplier) * (CamLX) * m)
                    pos.Y = orgPos.Y - ((MousePosY * CamSpeedMultiplier) * (-1.0F) * m)
                    pos.Z = orgPos.Z - ((MousePosX * CamSpeedMultiplier) * (CamLZ) * m)
                    cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y, pos.Z + 12500, 0, 1, 0)
                    lookPositions(CInt(currentLookDirection)) = pos
            End Select

            RaisePerspectiveChanged()

            lastMouseX = mouseX
            lastMouseY = mouseY
            'Console.WriteLine("CamAngleX = " + CamAngleX + ", CamAngleY = " + CamAngleY);
            'setRotationFromLookAt();
        End Sub

        Private Sub UpdateCameraMatrixWithMouse_FLY(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4)
            If resetMouse Then
                lastMouseX = mouseX
                lastMouseY = mouseY
                resetMouse = False
            End If
            Dim MousePosX As Integer = mouseX - lastMouseX
            Dim MousePosY As Integer = mouseY - lastMouseY
            CamAngleX = CamAngleX + (0.01F * MousePosX)
            ' This next part isn't neccessary, but it keeps the Yaw rotation value within [0, 2*pi] which makes debugging simpler.
            If CamAngleX > TAU Then
                CamAngleX -= TAU
            ElseIf CamAngleX < 0 Then
                CamAngleX += TAU
            End If

            '             Lock pitch rotation within the bounds of [-3.1399.0, -0.0001], otherwise the LookAt function will snap to the 
            '               opposite side and reverse mouse looking controls.
            CamAngleY = Clampf((CamAngleY + (0.01F * MousePosY)), -3.1399F, -0.0001F)

            lastMouseX = mouseX
            lastMouseY = mouseY
            OrientateCam(CamAngleX, CamAngleY)
            UpdateMatrix(cameraMatrix)
            'Console.WriteLine("CamAngleX = " + CamAngleX + ", CamAngleY = " + CamAngleY);
            'setRotationFromLookAt();
        End Sub

        Private Sub UpdateCameraOffsetWithMouse_FLY(orgPos As Vector3, mouseX As Integer, mouseY As Integer, w As Integer, h As Integer, ByRef cameraMatrix As Matrix4)
            If resetMouse Then
                lastMouseX = mouseX
                lastMouseY = mouseY
                resetMouse = False
            End If
            Dim MousePosX As Integer = (-mouseX) + lastMouseX
            Dim MousePosY As Integer = (-mouseY) + lastMouseY
            Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2)
            Dim yaw_Renamed As Double = CamAngleX - (Math.PI / 2)
            Dim CamLX As Single = Math.Sin(yaw_Renamed)
            Dim CamLZ As Single = -Math.Cos(yaw_Renamed)
            pos.X = orgPos.X - ((MousePosX * CamSpeedMultiplier) * (CamLX) * 6.0F)
            pos.Y = orgPos.Y - ((MousePosY * CamSpeedMultiplier) * (-1.0F) * 6.0F)
            pos.Z = orgPos.Z - ((MousePosX * CamSpeedMultiplier) * (CamLZ) * 6.0F)

            OrientateCam(CamAngleX, CamAngleY)
            UpdateMatrix(cameraMatrix)
        End Sub

        Public Sub UpdateCameraOffsetDirectly(horz_amount As Integer, vert_amount As Integer, ByRef cameraMatrix As Matrix4)
            If myCamMode = CameraMode.ORBIT Then
                UpdateCameraOffsetDirectly_ORBIT(horz_amount / 5, vert_amount / 5, cameraMatrix)
            Else
                'Console.WriteLine(MousePosX+","+ MousePosY);
                Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2)
                Dim yaw_Renamed As Double = CamAngleX - (Math.PI / 2)
                Dim CamLX As Single = CSng(Math.Sin(yaw_Renamed))
                ' float CamLY = (float)Math.Cos(pitch);
                Dim CamLZ As Single = CSng(-Math.Cos(yaw_Renamed))
                pos.X += ((horz_amount * CamSpeedMultiplier) * (CamLX))
                pos.Y += ((vert_amount * CamSpeedMultiplier) * (-1.0F))
                pos.Z += ((horz_amount * CamSpeedMultiplier) * (CamLZ))

                OrientateCam(CamAngleX, CamAngleY)
                UpdateMatrix(cameraMatrix)
            End If
        End Sub

        Private Sub UpdateCameraOffsetDirectly_ORBIT(moveSpeedX As Integer, moveSpeedY As Integer, ByRef cameraMatrix As Matrix4)
            Dim MousePosX As Integer = moveSpeedX
            Dim MousePosY As Integer = moveSpeedY
            orbitTheta += MousePosX * 0.01F * CamSpeedMultiplier
            orbitPhi -= MousePosY * 0.01F * CamSpeedMultiplier
            orbitPhi = Clampf(orbitPhi, -1.57F, 1.57F)
            UpdateOrbitCamera(cameraMatrix)
        End Sub

        Private Sub UpdateCameraMatrixWithMouse_ORBIT(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4)
            UpdateCameraOffsetWithMouse_ORBIT(mouseX, mouseY, cameraMatrix)
        End Sub

        Private Sub UpdateCameraOffsetWithMouse_ORBIT(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4)
            If resetMouse Then
                lastMouseX = mouseX
                lastMouseY = mouseY
                resetMouse = False
            End If
            Dim MousePosX As Integer = (-mouseX) + lastMouseX
            Dim MousePosY As Integer = (-mouseY) + lastMouseY
            orbitTheta += MousePosX * 0.01F * CamSpeedMultiplier
            orbitPhi -= MousePosY * 0.01F * CamSpeedMultiplier
            orbitPhi = Clampf(orbitPhi, -1.57F, 1.57F)
            UpdateOrbitCamera(cameraMatrix)
            lastMouseX = mouseX
            lastMouseY = mouseY
        End Sub

        Private Sub UpdateCameraMatrixWithScrollWheel_ORBIT(amt As Integer, ByRef cameraMatrix As Matrix4)
            orbitDistance -= amt
            If orbitDistance < 300.0F Then
                orbitDistance = 300.0F
            End If
            UpdateOrbitCamera(cameraMatrix)
        End Sub

        Public Sub ResetMouseStuff()
            resetMouse = True
        End Sub

        Private Function CalculateCenterPositionOfObjects(objs As ICameraPoint()) As Numerics.Vector3
            If objs.Length <= 1 Then
                Dim obj As ICameraPoint = objs.FirstOrDefault
                If obj Is Nothing Then
                    Return Numerics.Vector3.Zero
                Else
                    Return New Numerics.Vector3(obj.Position.X,
                                                obj.Position.Y,
                                                obj.Position.Z)
                End If
            End If

            Dim maxX As Single? = Nothing
            Dim maxY As Single? = Nothing
            Dim maxZ As Single? = Nothing
            Dim minX As Single? = Nothing
            Dim minY As Single? = Nothing
            Dim minZ As Single? = Nothing

            For Each obj As ICameraPoint In objs
                Dim pos As Numerics.Vector3 = obj.Position
                If maxX Is Nothing OrElse pos.X > maxX Then maxX = pos.X
                If maxY Is Nothing OrElse pos.Y > maxY Then maxY = pos.Y
                If maxZ Is Nothing OrElse pos.Z > maxZ Then maxZ = pos.Z
                If minX Is Nothing OrElse pos.X < minX Then minX = pos.X
                If minY Is Nothing OrElse pos.Y < minY Then minY = pos.Y
                If minZ Is Nothing OrElse pos.Z < minZ Then minZ = pos.Z
            Next

            If maxX Is Nothing Then maxX = 0
            If maxY Is Nothing Then maxY = 0
            If maxZ Is Nothing Then maxZ = 0
            If minX Is Nothing Then minX = 0
            If minY Is Nothing Then minY = 0
            If minZ Is Nothing Then minZ = 0

            Dim upper As New Numerics.Vector3(maxX, maxY, maxZ)
            Dim lower As New Numerics.Vector3(minX, minY, minZ)

            Dim middle As Numerics.Vector3 = (upper + lower) / 2

            Return middle
        End Function

        Private Function CalculateCenterYRotationOfObjects(objs As ICameraPoint()) As Single
            If objs.Length <= 1 Then
                Dim obj As ICameraPoint = objs.FirstOrDefault
                If obj Is Nothing Then
                    Return 0
                Else
                    Return obj.Rotation.Y
                End If
            End If

            Dim yRot As New List(Of Single)

            For Each obj As ICameraPoint In objs
                yRot.Add(obj.Rotation.Y)
            Next

            Return yRot.Average
        End Function

        Private Function GetSelectedObject() As ICameraPoint()
            Dim e As New NeedSelectedObjectEventArgs
            RaiseEvent NeedSelectedObject(Me, e)

            Dim stopw As New Stopwatch
            stopw.Start()

            Do Until e.HasObjectSetted OrElse stopw.ElapsedMilliseconds > 1000
                Application.DoEvents()
            Loop

            stopw.Stop()

            Return e.Points
        End Function

        'C A P S E L T   C L A S S E S

        Public Class NeedSelectedObjectEventArgs
            Inherits EventArgs

            Private _HasObjectSetted As Boolean = False
            Public ReadOnly Property HasObjectSetted As Boolean
                Get
                    Return _HasObjectSetted
                End Get
            End Property

            Private _Points As ICameraPoint() = Nothing
            Public Property Points As ICameraPoint()
                Get
                    Return _Points
                End Get
                Set(value As ICameraPoint())
                    _Points = value
                    _HasObjectSetted = True
                End Set
            End Property

        End Class

    End Class

End Namespace
