一个图片框是否有可能检测到另一个图片框中的颜色并避免了该颜色?

时间:2019-11-25 17:04:28

标签: vb.net

我目前正在尝试为当地城镇创建视觉路线规划器界面。 GUI是缺少详细信息的地图,只是简单地显示了带有灰色线条的绿色背景。 代表车辆的另一个图片框是否有可能避免并且无法访问“地图图片框”的绿色区域?

如何使汽车图片框沿着地图图片框遵循某些路线,并且可以通过计算得出?

1 个答案:

答案 0 :(得分:0)

我们将分两步进行此操作:

  1. 根据数据构建地图。
  2. 实施灵活的寻路算法。
  3. 绘制人类友好的地图版本。
  4. 结束用户交互。

随着您的进步,我会丰富这个答案,以便与您保持联系。


1

首先,我们将处理数据。路线图基本上是由点(或节点)链接的点(或节点)网络。

这些点具有坐标(因此我们以后可以绘制它们),有时还有其他值,例如名称。它们是道路的每一个起点和终点,有时介于两者之间。为了简单起见,用户只能在点上开始和结束。

顶点有长度,有时还包括诸如“难度”,“最大速度”之类的东西。这些统计信息将确定哪条道路最适合用户的出行。我们还可以确保当用户不想这样做时,算法不会考虑顶点,或者如果我们选择这样做,也可以强迫他穿过特定的顶点。

您会发现我通过在地图的构造函数中进行设置来包含了一个样本地图。我包括了近似坐标和距离。您可以按自己喜欢的任何方式进行更改。这里的数据代表什么:

Sample map


2

我在这里使用A *(读取A STAR)算法。您早些时候提到了Dijkstra的算法,这很好,但是为此,我将坚持使用A *。

这两种方法之间存在差异。如果您对它们进行维基百科,那么您会发现这两张图片,它们确实很好地显示了两者之间的主要区别:

Dijkstra的算法:

Dijkstra's algorithm

A *算法:

A* algorithm

如您所见,这两种算法都做得很好。

我评论了代码,但是您可能会对所有这些问题有很多疑问。我会闲逛回答他们。

要操纵此算法,您必须了解以下内容:

  • 顶点的difficulty值使该算法看起来更长。没必要(这就是为什么我到处都将其设置为1)的原因,但是如果您想将一条山路与一条高速公路或速度限制为30 km / h的道路与另一条可以以100 km / h的速度行驶的道路进行比较,您可以调整此值。
  • 节点的x和y坐标本身并不是很重要,但是A *算法使用它们来获得您刚刚在图像中看到的“我正朝着我的目标”效果。近似值就可以了,只要其中存在有意义的值即可。您可以用其他方式计算H值,但这是制图法,尽管我说得通。
  • 顶点必须始终连接到起点和终点。只要其中有两个(或者两次相同的点,但这将导致它开始的地方毫无任何意义)就没有意义。
  • 一个点可以具有任意数量的顶点。
  • 您可以提出一些“不可能通过”的观点,但是我没有这样编码。您可以将难度提高到疯狂的程度,以获得类似的结果,除了如果没有其他方法,算法最终还是会使用困难的路径。
  • 如果结束节点与起始节点的网络没有连接,则算法将尝试所有操作,然后放弃,并让您知道到终点没有路径。
  • 使用TestMyMap()子尝试一些寻路。自己计算以进行比较。更改其他地方的终点和起点。发狂!

代码

#Region " Mapping "
    Public Class MapPoint
        Private _vertexList As New List(Of MapVertex) 'one point can have as many "roads" as you want
        Private _xCoord As Decimal 'for now the coordinates are meaningless, but at some point we might want to use them to actually paint the map
        Private _yCoord As Decimal
        Private _name As String 'just a useful label
        Public Parent As MapVertex = Nothing 'useful for backtracking at the end (and get an itinary)

        'f is the total "cost" of the the node being analyzed (f = g + h) while doing pathfinding
        'g is the distance between the node being analyzed and the starting point while doing pathfinding
        'those values serve no other purpose
        'h is the estimated minimal distance between the two points while doing pathfinding
        'in the Sub 'CalculateH' I'm using the points coordinates and simple trigonometry to estimate h
        'technically you can skip the square root part, but I decided against it. There are no dealmaking advantages to my decision.
        'it's better to underestimate the minimal distance, that's why a theoretical straight line is a good choice
        Public f As Decimal = 0
        Public g As Decimal = 0
        Public h As Decimal = 0

        Public ReadOnly Property VertexList As List(Of MapVertex)
            Get
                Return _vertexList
            End Get
        End Property

        Public ReadOnly Property X As Decimal
            Get
                Return _xCoord
            End Get
        End Property

        Public ReadOnly Property Y As Decimal
            Get
                Return _yCoord
            End Get
        End Property

        Public ReadOnly Property Name As String
            Get
                Return _name
            End Get
        End Property

        Public Sub New(name As String, xx As Decimal, yy As Decimal, ParamArray vertex() As MapVertex)
            _name = name
            _xCoord = xx
            _yCoord = yy
            For Each v As MapVertex In vertex
                _vertexList.Add(v)
            Next
        End Sub

        Public Sub AddVertex(vertex As MapVertex)
            _vertexList.Add(vertex)
        End Sub

        Public Sub CalculateH(startingPoint As MapPoint, endPoint As MapPoint)
            h = Convert.ToDecimal(Math.Sqrt(Math.Pow((startingPoint.X - endPoint.X), 2) + Math.Pow((startingPoint.Y - endPoint.Y), 2)))
        End Sub

        Public Sub UpdateFandG(currentNodeG As Decimal, distance As Decimal)
            g = currentNodeG + distance
            f = g + h
        End Sub

        Public Sub ResetPathfindingValues()
            f = 0
            g = 0
            h = 0
            Parent = Nothing

            For Each v As MapVertex In _vertexList
                v.ResetPathfindingValues()
            Next
        End Sub
    End Class

    Public Class MapVertex
        Private _name As String 'just a useful label
        Private _length As Decimal 'length of the road. If you have straight roads we can use simple math to get the distances, but curvy roads have their own length, and I'll assume that your roads will be anything
        Private _difficulty As Decimal 'if you want to make some roads faster than others, we can use a variable like this one. It could be something else, like "maxSpeed", but for a prototype "difficulty" will do fine
        'I suggest making the difficulty a multiplier. It'll be easier to use that way (so a road twice faster would be a '0.5' and a road twice harder to use would be a '2.0')
        Public Parent As MapPoint = Nothing 'useful for backtracking at the end (and get an itinary)

        'start and end has no meaning here, as both are end points for the line. The important part is that they refer to existing points
        'a vertex must ALWAYS be linked to two points (or it makes no sense, you cannot have a line with only one end unless you go full moebius and we're not using enough dimensions for this to make sense
        'if you feel like it, you can have a vertex looping from one point and going back to it, but it'll serve no purpose and will never be actually used except by the pathfinding algorithm (which will note it as a bad move every time)
        Private _startPoint As MapPoint
        Private _endPoint As MapPoint

        Public ReadOnly Property Name As String
            Get
                Return _name
            End Get
        End Property

        'you can play around with difficulty if you like, it'll change which path the algorithm thinks is the better
        Public ReadOnly Property AdjustedLength As Decimal
            Get
                Return _length * _difficulty
            End Get
        End Property

        Public Sub New(name As String, startPoint As MapPoint, endPoint As MapPoint, length As Decimal, difficulty As Decimal)
            _name = name
            _startPoint = startPoint
            _endPoint = endPoint
            _length = length
            _difficulty = difficulty

            'automatically adding this vertex to it's points on creation:
            _startPoint.AddVertex(Me)
            _endPoint.AddVertex(Me)
        End Sub

        'this function is so we can get the "other side" of a path from it's start
        Public Function GetOtherNode(currentNode As MapPoint) As MapPoint
            If _startPoint Is currentNode Then
                Return _endPoint
            Else
                Return _startPoint
            End If
        End Function

        Public Sub ResetPathfindingValues()
            Parent = Nothing
        End Sub
    End Class

    Public Class Map
        'the Map object is a collection of points.
        'those points are linked by vertex
        'it could be different depending on what's your purpose

        Private _points As New List(Of MapPoint)

        Public Function GetPoint(name As String) As MapPoint
            For Each p As MapPoint In _points
                If p.Name = name Then
                    Return p
                End If
            Next

            Return Nothing
        End Function

        Public Sub New()
            'You can use this part to build a simple map with a handful of points. For the demonstration that's how we'll work, but later on you can design a mean to load maps from a specific source if you want
            'Here's one I threw on the screen at random. If you have one you like better, send me a pastebin and I'll use it instead

            'First I create the points:
            Dim oldBarn As MapPoint = New MapPoint("Old Barn", 0, 0)
            Dim home As MapPoint = New MapPoint("Home", 0, 12)
            Dim forest As MapPoint = New MapPoint("Forest", 0, 21)
            Dim creepyDeadEnd As MapPoint = New MapPoint("Creepy Dead End", 0, 26)
            Dim groceries As MapPoint = New MapPoint("Groceries", 11, 8)
            Dim girlfriendsPlace As MapPoint = New MapPoint("Girlfriend's Place", 20, 8)
            Dim friendsHome As MapPoint = New MapPoint("Friend's Home", 5, 13)
            Dim bar As MapPoint = New MapPoint("The Foo's Bar", 32, 14)   'hehehe

            'Second I create the roads between those points (as long as 2 points exist I could create the road, I just decided I would be systematic)
            Dim smallRoad As MapVertex = New MapVertex("Small Road", oldBarn, home, 12, 1)
            Dim oakStreet As MapVertex = New MapVertex("Oak Street", home, forest, 9, 1)
            Dim pathway As MapVertex = New MapVertex("Pathway", forest, creepyDeadEnd, 5, 1)
            Dim oldRoad As MapVertex = New MapVertex("Old Road", oldBarn, friendsHome, 17, 1)
            Dim arlingtonStreet As MapVertex = New MapVertex("Arlington Street", home, groceries, 7, 1)
            Dim fourthStreet As MapVertex = New MapVertex("4th Street", home, girlfriendsPlace, 12, 1)
            Dim placeLittlefinger As MapVertex = New MapVertex("Place Littlefinger", groceries, girlfriendsPlace, 9, 1)
            Dim cedarCreek As MapVertex = New MapVertex("Cedar Creek", forest, bar, 19, 1)
            Dim alley As MapVertex = New MapVertex("Alley", friendsHome, groceries, 7, 1)
            Dim mainStreet As MapVertex = New MapVertex("Main Street", groceries, bar, 22, 1)
            Dim durnhamRoad As MapVertex = New MapVertex("Durnham Road", friendsHome, bar, 27, 1)
            Dim secretRoad As MapVertex = New MapVertex("Secret Road", oldBarn, bar, 61, 1)

            'Adding the points to the Map
            _points.AddRange({oldBarn, home, forest, creepyDeadEnd, groceries, girlfriendsPlace, friendsHome, bar})
        End Sub

        'This is an implementation of the A* algorithm, which is a very popular pathfinding algorithm for simple grids/node networks
        'You could use Dijkstra's algorithm if you like it better or are a Sapkowsky's fan, but I think A* will do a great job here
        Public Function FindTheShortestPath(startingPoint As MapPoint, endPoint As MapPoint) As String
            Dim openList As New List(Of MapPoint)   'nodes that are going to be analyzed soon
            Dim closedList As New List(Of MapPoint) 'nodes that have been analyzed

            'we always start the analysis... from the starting point of course!
            openList.Add(startingPoint)
            startingPoint.CalculateH(startingPoint, endPoint)

            'as long as we haven't found the path to the end point, the algorithm will continue looking
            'there are 2 ways to exit this function:
            ' #1 is finding the endPoint
            ' #2 is having nowhere left to go and still not finding the end point (which means that it's impossible)
            While (openList.Count > 0)
                'look for the lowest f in the openList
                'this is our current node for the analysis
                Dim currentNode As MapPoint = Nothing
                For Each p As MapPoint In openList
                    If currentNode Is Nothing OrElse p.f < currentNode.f Then
                        currentNode = p
                    End If
                Next

                'remove the currentNode from the open list (it's being analyzed) and add to the closedList (no need to come back)
                openList.Remove(currentNode)
                closedList.Add(currentNode)

                'check if we've reached the end node
                If currentNode Is endPoint Then
                    'yay!
                    'now let's backtrack to get an itinary:

                    Dim path As String = BackTrack(endPoint)
                    ResetAllPathfindingVariables()
                    Return path
                End If

                'finding which nodes are connected to the Current node
                'then analyzing it
                For Each vertex As MapVertex In currentNode.VertexList
                    Dim nextNode As MapPoint = vertex.GetOtherNode(currentNode)

                    'only work on a node if it's out of the closedList
                    If Not closedList.Contains(nextNode) Then
                        'I'm tracking parents to make an itinary when this is finished
                        nextNode.CalculateH(currentNode, endPoint)
                        nextNode.UpdateFandG(currentNode.g, vertex.AdjustedLength)

                        'if it's not yet on the openList, now's time to add it there!
                        '(I know that a negative check with a 'Else' clause is stupid, but it's easier to read in this context)
                        If Not openList.Contains(nextNode) Then
                            openList.Add(nextNode)
                            nextNode.UpdateFandG(currentNode.g, vertex.AdjustedLength)
                            nextNode.Parent = vertex
                            vertex.Parent = currentNode
                        Else
                            'if this is a known node but we found a faster path to reach it, update it's parent
                            If currentNode.g + vertex.AdjustedLength < nextNode.g Then
                                nextNode.UpdateFandG(currentNode.g, vertex.AdjustedLength)
                                nextNode.Parent = vertex
                                vertex.Parent = currentNode
                            End If
                        End If
                    End If
                Next
            End While

            ResetAllPathfindingVariables()
            Return "No path was found."
        End Function

        Private Sub ResetAllPathfindingVariables()
            For Each p As MapPoint In _points
                p.ResetPathfindingValues()
            Next
        End Sub

        'recursive function to show the path found by the algorithm
        Private Function BackTrack(location As Object, Optional path As String = "") As String
            If path <> "" Then path = " => " & path

            Select Case True
                Case TypeOf location Is MapPoint
                    Dim currentPoint As MapPoint = DirectCast(location, MapPoint)
                    path = currentPoint.Name & path
                    If currentPoint.Parent Is Nothing Then
                        Return path
                    Else
                        Return BackTrack(currentPoint.Parent, path)
                    End If

                Case TypeOf location Is MapVertex
                    Dim currentVertex As MapVertex = DirectCast(location, MapVertex)
                    path = currentVertex.Name & path
                    If currentVertex.Parent Is Nothing Then
                        Return path
                    Else
                        Return BackTrack(currentVertex.Parent, path)
                    End If
            End Select

            Return ""
        End Function
    End Class

    Private Sub TestMyMap()
        _map = New Map()

        Dim path As String = _map.FindTheShortestPath(_map.GetPoint("Home"), _map.GetPoint("The Foo's Bar"))
        Console.WriteLine(path)
    End Sub
#End Region

当然,我会在那里帮助您了解这一部分的内容。很重,我知道。仍然很有趣,尤其是一旦完成,您就可以使用它!