home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 4_2005-2006.ISO / data / Zips / LaVolpe_Pa2061534212007.psc / clsLVpathFinder.cls < prev   
Text File  |  2005-06-18  |  61KB  |  1,402 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "clsLVpathFinder"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15. ' Not easy to follow unless you are very familiar with the A-Star (A*)
  16. ' path finding algorithm and how Window's regions are stored.
  17. ' Making it even more complicated is the sophisticated way I use Linked Lists
  18. ' and arrays to transform window region rectangles into horizontal edges to
  19. ' path points (anchors).
  20.  
  21. ' My goal was to find paths in l00 ms or less. I have not quite accomplished that,
  22. ' but got real close (on average is less than 100 ms). That being said, this is
  23. ' the next step of something I started two years ago. There will be more, but this
  24. ' will be put to bed for awhile to allow me to think about ways to improve this project.
  25. ' Since no others I know of have attempted to path find using region rectangles,
  26. ' I get to make the rules & you get to bend or break them.
  27.  
  28. ' I am always eager to talk with others that are interested in pursuing this unique
  29. ' logic in path finding on non-tiled graphs. If you want to tweak or improve this
  30. ' routine & are unsure of why I did what I did, simply email me with your questions.
  31.  
  32. ' Until final tweaks are made. Some terminology that you may find confusing:
  33. ' Edge: the top edge of a rectangle. Unique & never duplicated
  34. ' Node: can refer to a rectangle or edge
  35. ' Anchor: a point in a path that the path MUST go thru
  36. '   all paths have at least 2 anchors: starting & ending point
  37.  
  38.  
  39. ' Always declare your class like: Private myClass WithEvents As clsLVpathFinder
  40. Public Event PathFound(ByVal isFinal As Boolean, ByRef Continue As Boolean)
  41. ' ^^ The only public event in this class. This event notifies you that a path
  42. ' has been found, whether or not it is the final path, and offers the option
  43. ' for you to abort continuing path finding, if desired
  44.  
  45. ' very few APIs used
  46. Private Declare Function PtInRect Lib "user32.dll" (ByRef lpRect As RECT, ByVal x As Long, ByVal y As Long) As Long
  47. Private Declare Function InflateRect Lib "user32.dll" (ByRef lpRect As RECT, ByVal x As Long, ByVal y As Long) As Long
  48. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
  49. Private Declare Function GetRegionData Lib "gdi32" (ByVal hRgn As Long, ByVal dwCount As Long, lpRgnData As Any) As Long
  50. Private Declare Function IntersectRect Lib "user32.dll" (ByRef lpDestRect As RECT, ByRef lpSrc1Rect As RECT, ByRef lpSrc2Rect As RECT) As Long
  51.  
  52. ' Heavy use of UDTs to help keep arrays managable/readable
  53. Private Type POINTAPI
  54.     x As Long
  55.     y As Long
  56. End Type
  57. Private Type RECT
  58.     Left As Long
  59.     Top As Long
  60.     Right As Long
  61.     Bottom As Long
  62. End Type
  63.  
  64. Private Type LinkData               ' individual links btwn region rectangles
  65.     Ref As Long                     ' Pointer to Rectangle array item
  66.     Edge As Long                    ' Pointer to related Edges array item
  67. End Type
  68. Private Type LinkReference          ' collection of LinkData elements
  69.     k_Links As Long                 ' number of elements
  70.     k_Neighbor() As LinkData        ' the collection
  71. End Type
  72.  
  73. ' Anchors are points in a path that cannot be removed from the path
  74. Private Type AnchorData             ' Anchor Details
  75.     Parent As Long
  76.     x As Long                       ' X coordinate of point
  77.     Edge As Long                    ' Ref to Edges() array which has the Y coord
  78.     LowCost As Single               ' Min cost for this point to the path starting point
  79. End Type
  80. ' SubAnchors are same as anchors but are unremovable points btwn two Anchors
  81. Private Type SubAnchorData
  82.     Count As Long                   ' Number of subanchors
  83.     Anchor() As Long                ' Array of pointers into the Anchors() array
  84. End Type
  85.  
  86. Private Type NodeReference          ' individual nodes
  87.     Parent As Long                  ' node that generated this node
  88.     Cost As Single                  ' Cost involved in exploring this node (filterable value)
  89.     Anchor As Long                  ' Last anchor point for this node
  90.     SubAnchor As Long               ' Ref to Subanchors() array, if any subanchors
  91. End Type
  92. Private Type PendingNodeReference   ' Pending cached path
  93.     Count As Long                   ' Nr of nodes in the path
  94.     Node() As NodeReference         ' Collection of the nodes
  95.     Index As Long                   ' The last node index in the pending path
  96. End Type
  97.     
  98. Private Type EdgeReference          ' individual Edges (horizontal only)
  99.     x As Long                       ' left coordinate of the edge
  100.     Z As Long                       ' right coordinate of the edge
  101.     y As Long                       ' Y coordinate of the edge
  102.     Count As Long                   ' Number of linked edges
  103.     Edge() As Long                  ' Pointers to the links in the Edges array
  104. End Type
  105.  
  106. ' Enumerators only used in local functions/routines
  107. Private Enum HeapActions
  108.     heapPush = 0
  109.     heapPop = 1
  110.     heapModify = 2
  111. End Enum
  112.  
  113. ' As the items in some arrays can potentially be huge if we populated them in
  114. ' advance, several arrays are appended only when needed. This is slower overall
  115. ' but reduces the memory required to find extremely complicated paths on large graphs
  116.  
  117. Private rgnRect() As RECT           ' collection of rectangles in the map region
  118. Private Links() As LinkReference    ' collection of rectangle links
  119. Private Nodes() As NodeReference    ' collection of Nodes considered for paths
  120. Private Edges() As EdgeReference    ' collection of Edge links
  121. Private Anchors() As AnchorData     ' collection of path Anchor points
  122. Private EdgeAnchors() As Long       ' cross-ref of Edges>Anchors
  123. Private subAnchors() As SubAnchorData   ' collection of subAnchors
  124.  
  125. Private openList() As Long          ' binary heap of active nodes
  126. Private pendingList() As Long       ' binary heap of pending nodes
  127. Private nodesPending() As PendingNodeReference   ' Created as needed
  128. ' ^^ Unfortunately, window regions don't allow for simple path finding rules
  129. '    Collection of nodes that need to be revisited
  130. Private pendingIndex() As Long      ' list of reusable pending nodes indexes
  131. ' ^^ The nodesPending() array is a cached history of a path. This has potential
  132. '    of being somewhat large & is often updated. To prevent rediming this array
  133. '    which has subarrays and its sorted binary heap when items are removed from
  134. '    the heap, we will simply keep track of those removed items & reuse them
  135. '    as new nodes are placed on the heap (pendingList() array)
  136.  
  137. Private FinalPath() As Long         ' 2-dim array of key points in a path
  138. Private FinalCost As Single         ' the cost/length of the final path
  139.  
  140. Private anchorPtLoopCount As Long       ' debug/testing use only
  141.  
  142. Public Property Get pathLength() As Single
  143.     pathLength = FinalCost
  144. End Property
  145.  
  146. Public Property Get PathPoints() As Long()
  147.     If FinalCost = 0 Then ReDim FinalPath(0)
  148.     PathPoints = FinalPath()
  149. End Property
  150.  
  151. ' ///////////////////// PATH ALGORITHM ROUTINES \\\\\\\\\\\\\\\\\\\\\
  152.  
  153. Public Sub FindPath(startPtX As Long, startPtY As Long, endPtX As Long, endPtY As Long, graphRgn As Long)
  154. ' startPtX & startPtY are the X,Y coordinates of the path's start position
  155. ' endPtX & endPtY are the X,Y coordinates of the path's end/terminal position
  156. ' graphRgn is a region containing the graph minus all obstacles (free space only)
  157. '   ::use APIs like CombineRgn, CreateRectRgn, etc to create the region
  158.  
  159. anchorPtLoopCount = 0
  160.  
  161. Dim bContinue As Boolean
  162. bContinue = True    ' flag to trigger Event at end of routine
  163. ' reset the cost before we start pathfinding
  164. FinalCost = 0
  165.  
  166. ' no region, no path
  167. If graphRgn = 0 Then GoTo CleanUp
  168.  
  169. ' extract region rectangles from passed region
  170. ExtractRectangles graphRgn
  171.  
  172. If UBound(rgnRect) = 0 Then
  173.     ' the entire graph is free space
  174.     ReDim Nodes(0)
  175.     ReDim Edges(1 To 2)
  176.     ' ensure the points are on the graph
  177.     If LocateTargets(startPtX, startPtY, endPtX, endPtY) = 0 Then GoTo CleanUp
  178.     ' calculate the cost & set the points
  179.     Nodes(0).Cost = Sqr(Abs(startPtX - endPtX) ^ 2 + Abs(startPtY - endPtY) ^ 2)
  180.     SetFinalPath
  181.     GoTo CleanUp
  182. End If
  183.  
  184. ' build edge and rectangle link arrays
  185. BuildLinks
  186. ' resize the base node array & related Anchors array
  187. ReDim Nodes(0 To UBound(Edges) + 2)
  188. ' ^^ Note: Nodes are ref'd as 1-bound. The 0-array item is used as a temporary node
  189. '    common to a few of the routines in this project. No need to pass it to the function
  190.  
  191. ' locate the targets on the graph. If any target is in an obstacle, abort.
  192. Select Case LocateTargets(startPtX, startPtY, endPtX, endPtY)
  193.     Case 0: ' one or both obstacles are off the graph, maybe in an obstacle
  194.         GoTo CleanUp
  195.     Case 2: ' both obstacles are in the same regional rectangle
  196.         Nodes(0).Cost = Sqr(Abs(startPtX - endPtX) ^ 2 + Abs(startPtY - endPtY) ^ 2)
  197.         SetFinalPath
  198.         GoTo CleanUp
  199.     Case Else
  200. End Select
  201.  
  202. ' A* (A Star) algorithm. There are many variations of this algorithm.
  203. ' A* is, in fact, a variation of Dijkstra's algorithm.
  204. ' Where A* requires a known "path traveled so-far" cost and an
  205. '   estimated cost to end of path, I may not know the true value of
  206. '   "path traveled so-far". The core logic of A* modified for this situation
  207.  
  208.  
  209. ReDim Anchors(0 To 1)       ' reset path anchor points. Created as needed
  210. ReDim subAnchors(0)         ' reset path subanchor points. Created as needed
  211.  
  212. ReDim EdgeAnchors(1 To UBound(Nodes) * 2)
  213. ' ^^ max number of possible anchors. Resets cross-reference table
  214.  
  215. ' binary heap & node array flags
  216. Dim nodeState() As Byte             ' state of any node
  217. ReDim nodeState(1 To UBound(Edges))
  218. ReDim openList(1 To 100)            ' binary heap of active nodes
  219. Dim nrHeapItems As Long             ' number of items in the binary heap
  220. Dim closedState As Byte             ' flag to indicate status of any node
  221.  
  222. ReDim nodesPending(1)               ' pending nodes to be revisited (as needed)
  223. ReDim pendingList(1 To 100)         ' binary heap of pending nodes
  224. Dim nrPending As Long               ' number of items in the binary heap
  225. ReDim pendingIndex(0 To 1)          ' reusable nodesPending() array item Indexes
  226.  
  227. Dim nodeID As Long, pNode As Long   ' refrences into the Nodes Array
  228. Dim adjNode As Long, pNodeIndex As Long ' and nodesPending() array
  229.     
  230. Dim bAbort As Boolean               ' flag to indicate continuance of path
  231.  
  232. ' Add the starting node to the open list to be checked
  233. ' Node 1 will always be the starting location
  234. ' Node 2 will always be the ending location
  235. nrHeapItems = 1
  236. openList(1) = 1
  237. ' prime the pending nodes reusable index array
  238. pendingIndex(0) = 1
  239.  
  240. With Anchors(0)         ' create two initial anchor points
  241.     .Edge = 1           ' starting point
  242.     .x = Edges(1).x
  243. End With
  244. With Anchors(1)         ' ending point
  245.     .Edge = 2
  246.     .x = Edges(2).x
  247. End With
  248.  
  249. ' set the value of this flag
  250. closedState = 1
  251.  
  252. Do  ' double loop.
  253.     ' one loop processes all current possible paths
  254.     ' the 2nd loop adds any secondary paths onto the 1st loop after 1st loop is done
  255.  
  256.     Do ' continue until path found or no path can be found
  257.         
  258.         nodeState(openList(1)) = closedState 'update node as closed/explored/done
  259.         ' this will be the parent node for any nodes it explores/opens
  260.         pNode = openList(1)
  261.     
  262.         ' Get next open node from the binary heap
  263.         openList(1) = openList(nrHeapItems)
  264.         nrHeapItems = nrHeapItems - 1
  265.         ' Need to adjust the heap now
  266.         UpdateHeap heapPop, nrHeapItems
  267.         
  268.         ' get the 1st possible adjacent edge/node to our parent node
  269.         adjNode = 0
  270.         nodeID = GetAdjacents(pNode, adjNode)
  271.         
  272.         Do Until nodeID = 0
  273.             ' call function to calculate any anchor/subanchor points and also
  274.             ' determine the cost of moving to those points
  275.             If CalcAnchorPt(nodeID, pNode) Then
  276.             
  277.                 If nodeID = 2 Then  ' we hit the ending point
  278.                     If SetFinalPath() Then
  279.                         bContinue = True
  280.                         If UBound(FinalPath, 2) = 1 Then
  281.                             ' a straight line path test.
  282.                             ' Since the fastest route btwn 2 points is a straight line,
  283.                             ' if we have only 2 points in the path....
  284.                             nrHeapItems = 0
  285.                             nrPending = 0
  286.                         Else
  287.                             ' send user notice that a path was found
  288.                             ' If user aborts pathfinding, we abort here
  289.                             RaiseEvent PathFound(False, bContinue)
  290.                             If Not bContinue Then GoTo CleanUp
  291.                         End If
  292.                     End If
  293.                     ' no need to allow this node to continue
  294.                     bAbort = True
  295.                 Else
  296.                     ' not the ending node, but do we have a path yet?
  297.                     If FinalCost Then
  298.                         ' yep. Is this path longer? If so, abort it
  299.                         bAbort = (Nodes(0).Cost > FinalCost)
  300.                     End If
  301.                 End If
  302.                 If bAbort Then
  303.                     ' we aborted, reset flag
  304.                     bAbort = False
  305.                 Else
  306.                 
  307.                     If nodeState(nodeID) = closedState Then
  308.                         ' if this path hits another existing path, we want to push this
  309.                         ' onto a pending path array to process later. We cannot process
  310.                         ' it now 'cause we do not know where it will next turn or
  311.                         ' terminate at and we can't modify any node's
  312.                         ' parent properties without knowing that information in advance
  313.                         UpdatePendingIndex heapPop, pNodeIndex
  314.                         AddToNodesPending nodeID, pNode, pNodeIndex
  315.                         nrPending = nrPending + 1
  316.                         pendingList(nrPending) = pNodeIndex
  317.                         UpdatePendingHeap heapPush, nrPending
  318.                     Else
  319.                         ' update the node's properties
  320.                         Nodes(nodeID) = Nodes(0)
  321.                         ' increment the heap count
  322.                         nrHeapItems = nrHeapItems + 1
  323.                         ' add the node to the the binary heap
  324.                         openList(nrHeapItems) = nodeID
  325.                         UpdateHeap heapPush, nrHeapItems
  326.                         'Update the node state
  327.                         nodeState(nodeID) = closedState
  328.  
  329.                     End If
  330.                     
  331.                 End If '  done updating current node
  332.             
  333.             End If ' done testing for excessive path length
  334.             
  335.             ' get next adjacent to this parent node
  336.             nodeID = GetAdjacents(pNode, adjNode)
  337.             
  338.         Loop ' adjacent nodes
  339.         
  340.     ' when the heap is empty, we no longer have candidates to explore, yet.
  341.     Loop Until nrHeapItems = 0
  342.  
  343.     If nrPending Then
  344.         ' process pending nodes. Since any edge can only have one parent,
  345.         ' nodes from different paths that come to a common node would require
  346.         ' us to make a decision which path would be that node's parent. But
  347.         ' we don't have all the points in the new path & can't make that decision
  348.         ' Therefore, we added that node to a pending list & will process it now
  349.         
  350.         ' instead of redim'ing the NodeState array, we will simply change the
  351.         ' value of closedState. This allows us to reuse the array
  352.         ' up to 255 times without clearing it out via a Redim each time
  353.         If closedState = 255 Then
  354.             ReDim nodeState(0 To UBound(Nodes))
  355.             closedState = 0
  356.         End If
  357.         closedState = closedState + 1
  358.         
  359.         ' get the last item on the list
  360.         Do
  361.             pNodeIndex = pendingList(1)
  362.             pendingList(1) = pendingList(nrPending)
  363.             nrPending = nrPending - 1
  364.             UpdatePendingHeap heapPop, nrPending
  365.             
  366.             ' we can run a check here first. While the node was pending,
  367.             ' the final path length could have been shortened which could now
  368.             ' nullify this pending path. If so, we don't need to finish it.
  369.             If FinalCost > nodesPending(pNodeIndex).Node(0).Cost Then
  370.             
  371.                 ' path length short enough to allow it to continue
  372.                 ' repopulate the Nodes() array with the cached path
  373.                 With nodesPending(pNodeIndex)
  374.                     For nodeID = 0 To .Count - 2
  375.                         ' redesignate nodes & parent relationships for this path
  376.                         Nodes(.Node(nodeID).Parent) = .Node(nodeID + 1)
  377.                         ' show the node state as closed
  378.                         nodeState(.Node(nodeID).Parent) = closedState
  379.                     Next
  380.                     ' now bring on the node that was added to the pending list
  381.                     Nodes(.Index) = .Node(0)
  382.                     ' change it's state & add it as the 1st item on the heap
  383.                     nodeState(.Index) = closedState
  384.                     openList(1) = .Index
  385.                     nrHeapItems = 1
  386.                 End With
  387.                 UpdatePendingIndex heapPush, pNodeIndex
  388.                 Exit Do
  389.             Else
  390.                 UpdatePendingIndex heapPush, pNodeIndex
  391.             End If
  392.         Loop Until nrPending = 0
  393.     
  394.     End If
  395.  
  396. Loop Until nrHeapItems = 0
  397.  
  398. CleanUp:
  399. ' explicitly clear arrays
  400. Erase nodesPending
  401. Erase Nodes
  402. Erase Edges
  403. Erase Anchors
  404. Erase subAnchors
  405. Erase EdgeAnchors
  406. Erase openList
  407. Erase pendingList
  408. Erase pendingIndex
  409. Erase nodeState
  410.  
  411. ' send final event to user
  412. If bContinue Then RaiseEvent PathFound(True, False)
  413.  
  414. Debug.Print "total calcAnchorPt loops til final path: "; anchorPtLoopCount
  415.  
  416. End Sub
  417.  
  418. Private Function GetAdjacents(nodeID As Long, nextIndex As Long) As Long
  419. ' function returns 1st adjacent edge starting with passd "nextIndex"
  420. ' for the target node/edge (NodeID)
  421.  
  422. ' This function filters possible path segments to prevent backtracking
  423. ' and wasted rerouting
  424.  
  425. Dim x As Long           ' generic loop variable
  426. Dim rtnNode As Long     ' return value
  427. Dim adjNode As Long     ' generic loop variable
  428. Dim bAllow As Boolean   ' filter flag
  429. Dim pDirBase As Byte    ' parent to current node direction
  430. Dim tDirection As Byte  ' current node to target direction
  431. Dim pDirection As Byte  ' parent to current node direction
  432.  
  433. With Edges(nodeID)
  434.  
  435.     ' determine the primary direction between the parent node & the current node
  436.     Select Case .y - Edges(Nodes(nodeID).Parent).y
  437.     Case Is < 0: pDirBase = 2 ' northerly route
  438.     Case Is > 0: pDirBase = 1 ' southerly direction
  439.     End Select  ' otherwise moving horizonally
  440.     
  441.     For x = nextIndex To .Count - 1
  442.         
  443.         ' prevent backtracking/rerouting to save processing time
  444.         ' 1st check. Is next node the current node's parent?
  445.         If Nodes(nodeID).Parent <> .Edge(x) Then
  446.         
  447.             If nodeID = 1 Or .Edge(x) = 2 Then
  448.                 ' if we are on our very 1st node or the last node; then allow connections
  449.                 bAllow = True
  450.             Else
  451.                 ' not the very first node; do some more checks
  452.                 pDirection = pDirBase
  453.                 ' determine primary direction between current node & next node
  454.                 Select Case Edges(.Edge(x)).y - .y
  455.                 Case Is < 0: tDirection = 1 ' northerly route
  456.                 Case Is > 0: tDirection = 2 ' southerly direction
  457.                 End Select  ' otherwise moving horizonally
  458.                 '^^ notice tDirection is opposite value of pDirection if same direction.
  459.                 '   this is done so we can simply use an AND operand in the final check
  460.                 
  461.                 ' 2nd check. Entering/Exiting a bridge
  462.                 If pDirection = 0 Or tDirection = 0 Then
  463.                     ' Don't allow bridge to be entered/exited if bridge is also
  464.                     ' directly adjacent to the parent node/edge
  465.                     For adjNode = 0 To Edges(Nodes(nodeID).Parent).Count - 1
  466.                         If Edges(Nodes(nodeID).Parent).Edge(adjNode) = .Edge(x) Then
  467.                             pDirection = 3
  468.                             tDirection = pDirection 'flag prevents bridge connections
  469.                             Exit For
  470.                         End If
  471.                     Next
  472.                 End If
  473.                 
  474.                 ' 3rd Check. Only allow connection if not wasted reroute
  475.                 bAllow = ((pDirection And tDirection) = 0)
  476.                 
  477.                 If bAllow Then
  478.                     ' 4th check proof positive not backtracking
  479.                     ' start with grandfather since parent was checked at top
  480.                     adjNode = Nodes(Nodes(nodeID).Parent).Parent
  481.                     Do Until adjNode = 1
  482.                         If .Edge(x) = adjNode Then
  483.                             bAllow = False
  484.                             Exit Do
  485.                         End If
  486.                         adjNode = Nodes(adjNode).Parent
  487.                     Loop
  488.                 End If
  489.                 tDirection = 0  ' reset
  490.             End If
  491.             
  492.             ' pass the edge reference if allowable
  493.             If bAllow Then
  494.                 rtnNode = .Edge(x)  ' set return value
  495.                 nextIndex = x + 1   ' set next index value
  496.                 Exit For
  497.             End If
  498.             
  499.         End If  ' test if next node is also the parent node
  500.     
  501.     Next    ' check next adjacent node if necessary
  502.     
  503. End With
  504.  
  505. GetAdjacents = rtnNode
  506.  
  507. End Function
  508.  
  509. Private Sub AddToNodesPending(nodeIndex As Long, newParent As Long, pIndex As Long)
  510.  
  511. Dim nrNodes As Long
  512. Dim pNode As Long
  513.  
  514. ' resize the array as needed; giving a small buffer to prevent resizing more often
  515. If pIndex > UBound(nodesPending) Then
  516.     ReDim Preserve nodesPending(0 To UBound(nodesPending) + 20)
  517. End If
  518.  
  519. ' count the number of edge segments in this current path
  520. pNode = newParent
  521. Do Until pNode = 1
  522.     nrNodes = nrNodes + 1
  523.     pNode = Nodes(pNode).Parent
  524. Loop
  525.  
  526. ' resize the array Path & Anchor elements appropriately
  527. ReDim nodesPending(pIndex).Node(0 To nrNodes)
  528.  
  529. ' populate the array Path & Anchor elements
  530. nrNodes = 1
  531. pNode = newParent
  532. Do Until pNode = 1
  533.     nodesPending(pIndex).Node(nrNodes) = Nodes(pNode)
  534.     nrNodes = nrNodes + 1
  535.     pNode = Nodes(pNode).Parent
  536. Loop
  537. ' fill in the core elements
  538. With nodesPending(pIndex)
  539.     .Count = nrNodes
  540.     .Index = nodeIndex
  541.     .Node(0) = Nodes(0)
  542. End With
  543.  
  544. End Sub
  545.  
  546. Private Function CalcHueristics(fromAnchor As Long, fromNode As Long) As Single
  547.  
  548. ' Still a work in progress, playing with different heuristic calculations
  549. ' This is the MOST important routine. The closer the first path found is to
  550. ' the final path, dramatically reduces the overall time needed to find the best path
  551.  
  552. Dim lngDiff As Long
  553. Dim rtnVal As Single, midX As Single
  554. Dim basePt As POINTAPI, endPt As POINTAPI
  555. Dim bIsNotValid As Boolean, bIsLateral As Boolean
  556.  
  557. With Anchors(Nodes(0).Anchor)
  558.  
  559.     If fromNode = 2 Then
  560.         ' moving to the final node (path ending point). Get exact distance
  561.         rtnVal = Sqr(Abs(.x - Edges(2).x) ^ 2 + Abs(Edges(.Edge).y - Edges(2).y) ^ 2)
  562.     Else
  563.         ' moving from the last anchor point to some other regional edge
  564.         ' This is the guess in distance. But the key is NOT to overestimate
  565.         ' the distance to a value that would be longer than the actual distance.
  566.         ' The trick is to be as close to exact as possible.
  567.         ' Since we don't know the next turn point or even the next rectangle that
  568.         ' will be visited, we have no way of knowing the exact distance!
  569.         
  570.         ' See if the edge being checked is physically between the last anchor
  571.         ' and the ending point
  572.         If Edges(.Edge).y < Edges(2).y Then
  573.             If Edges(fromNode).y < Edges(.Edge).y Then bIsNotValid = True
  574.         Else
  575.             If Edges(fromNode).y > Edges(2).y Then bIsNotValid = True
  576.         End If
  577.         If bIsNotValid Then
  578.             ' if the above is the case, we handle guesswork differently
  579.             If Edges(2).x < Edges(fromNode).x Then
  580.                 midX = Edges(fromNode).x
  581.             ElseIf Edges(2).x > Edges(fromNode).Z Then
  582.                 midX = Edges(fromNode).Z
  583.             Else
  584.                 midX = Edges(2).x
  585.             End If
  586.             rtnVal = Sqr(Abs(midX - Edges(2).x) ^ 2 + Abs(Edges(fromNode).y - Edges(2).y) ^ 2)
  587.             rtnVal = rtnVal + Abs(Edges(fromNode).y - Edges(.Edge).y)
  588.             
  589.         Else
  590.             ' the visited edge is between the last known anchor & the ending point
  591.             ' Draw a diagonal line thru that edge & get an intersection point closest
  592.             ' to that edge. Use that point for measurement
  593.             basePt.x = .x
  594.             basePt.y = Edges(.Edge).y
  595.             endPt.x = Edges(2).x
  596.             endPt.y = Edges(2).y
  597.             midX = LineIntersectCoord(basePt, endPt, Edges(fromNode).y, bIsLateral)
  598.             If bIsLateral Then midX = .x
  599.             If midX < Edges(fromNode).x Then
  600.                 midX = Edges(fromNode).x
  601.             ElseIf midX > Edges(fromNode).Z Then
  602.                 midX = Edges(fromNode).Z
  603.             End If
  604.             ' measure from that point to last known anchor
  605.             lngDiff = Abs(Edges(fromNode).y - Edges(.Edge).y)
  606.             rtnVal = Sqr(Abs(.x - midX) ^ 2 + lngDiff ^ 2)
  607.             ' measure from that pont to the end point
  608.             lngDiff = Abs(Edges(fromNode).y - Edges(2).y)
  609.             rtnVal = rtnVal + Sqr(Abs(Edges(2).x - midX) ^ 2 + lngDiff ^ 2)
  610.         End If
  611.     End If
  612.     
  613.     ' carry over the cost from the parent node (accumulative), sort of.
  614.     rtnVal = rtnVal + .LowCost
  615.         
  616. End With
  617.  
  618. CalcHueristics = rtnVal
  619.  
  620. End Function
  621.  
  622. Private Sub UpdateHeap(Action As HeapActions, heapOffset As Long)
  623. ' BINARY HEAP ROUTINE for active nodes
  624.  
  625. ' Don't mess with the binary heap routines.
  626. ' Binary heaps basically move every other item in an array until the sort order
  627. ' is done; but it is a bit more complicated than that. If unsure what binary heaps
  628. ' are, suggest pulling down a few web pages to get familiar with it. If you foul up
  629. ' the heap routines: at best a failed sorted list will occur,
  630. ' at worst: infinite loop
  631.  
  632. Dim heapPtr1 As Long
  633. Dim heapPtr2 As Long
  634. Dim SwapTemp As Long
  635.  
  636. Select Case Action
  637. Case heapPop
  638.     ' We want to keep the Node with lowest F cost at top of heap
  639.     heapPtr2 = 1
  640.     Do
  641.         heapPtr1 = heapPtr2
  642.         If heapPtr1 * 2 + 1 <= heapOffset Then
  643.         ' see if at least 2 more items on heap exist
  644.             If Nodes(openList(heapPtr1)).Cost >= Nodes(openList(heapPtr1 * 2)).Cost Then heapPtr2 = heapPtr1 * 2
  645.             If Nodes(openList(heapPtr2)).Cost >= Nodes(openList(heapPtr1 * 2 + 1)).Cost Then heapPtr2 = heapPtr1 * 2 + 1
  646.         
  647.         Else    ' if only one more item on heap exists, do that one
  648.             If heapPtr1 * 2 <= heapOffset Then
  649.                 If Nodes(openList(heapPtr1)).Cost >= Nodes(openList(heapPtr1 * 2)).Cost Then heapPtr2 = heapPtr1 * 2
  650.             End If
  651.         End If
  652.         
  653.         If heapPtr1 <> heapPtr2 Then
  654.             ' swap the pointers
  655.             SwapTemp = openList(heapPtr1)
  656.             openList(heapPtr1) = openList(heapPtr2)
  657.             openList(heapPtr2) = SwapTemp
  658.         Else
  659.             Exit Do
  660.         End If
  661.     Loop ' until done
  662. Case heapPush
  663.     ' add a node to the heap
  664.     If heapOffset + 1 > UBound(openList) Then
  665.         ReDim Preserve openList(1 To UBound(openList) + 100)
  666.     End If
  667.     heapPtr1 = heapOffset
  668.     Do While heapPtr1 <> 1
  669.         If Nodes(openList(heapPtr1)).Cost <= Nodes(openList(heapPtr1 / 2)).Cost Then
  670.             SwapTemp = openList(heapPtr1 / 2)
  671.             openList(heapPtr1 / 2) = openList(heapPtr1)
  672.             openList(heapPtr1) = SwapTemp
  673.             heapPtr1 = heapPtr1 / 2
  674.         Else
  675.             Exit Do
  676.         End If
  677.     Loop
  678.    
  679. End Select
  680. End Sub
  681.  
  682. Private Sub UpdatePendingHeap(Action As HeapActions, heapOffset As Long)
  683. ' BINARY HEAP ROUTINE for pending nodes
  684.  
  685. ' Don't mess with the binary heap routines.
  686. ' Binary heaps basically move every other item in an array until the sort order
  687. ' is done; but it is a bit more complicated than that. If unsure what binary heaps
  688. ' are, suggest pulling down a few web pages to get familiar with it. If you foul up
  689. ' the heap routines: at best a failed sorted list will occur,
  690. ' at worst: infinite loop
  691.  
  692. Dim heapPtr1 As Long
  693. Dim heapPtr2 As Long
  694. Dim SwapTemp As Long
  695.  
  696. Select Case Action
  697. Case heapPop
  698.     ' We want to keep the Node with lowest F cost at top of heap
  699.     heapPtr2 = 1
  700.     Do
  701.         heapPtr1 = heapPtr2
  702.         If heapPtr1 * 2 + 1 <= heapOffset Then
  703.         ' see if at least 2 more items on heap exist
  704.             If nodesPending(pendingList(heapPtr1)).Node(0).Cost >= nodesPending(pendingList(heapPtr1 * 2)).Node(0).Cost Then heapPtr2 = heapPtr1 * 2
  705.             If nodesPending(pendingList(heapPtr2)).Node(0).Cost >= nodesPending(pendingList(heapPtr1 * 2 + 1)).Node(0).Cost Then heapPtr2 = heapPtr1 * 2 + 1
  706.         
  707.         Else    ' if only one more item on heap exists, do that one
  708.             If heapPtr1 * 2 <= heapOffset Then
  709.                 If nodesPending(pendingList(heapPtr1)).Node(0).Cost >= nodesPending(pendingList(heapPtr1 * 2)).Node(0).Cost Then heapPtr2 = heapPtr1 * 2
  710.             End If
  711.         End If
  712.         
  713.         If heapPtr1 <> heapPtr2 Then
  714.             ' swap the pointers
  715.             SwapTemp = pendingList(heapPtr1)
  716.             pendingList(heapPtr1) = pendingList(heapPtr2)
  717.             pendingList(heapPtr2) = SwapTemp
  718.         Else
  719.             Exit Do
  720.         End If
  721.     Loop ' until done
  722. Case heapPush
  723.     ' add a node to the heap
  724.     If heapOffset + 1 > UBound(pendingList) Then
  725.         ReDim Preserve pendingList(1 To UBound(pendingList) + 100)
  726.     End If
  727.     heapPtr1 = heapOffset
  728.     Do While heapPtr1 <> 1
  729.         If nodesPending(pendingList(heapPtr1)).Node(0).Cost <= nodesPending(pendingList(heapPtr1 / 2)).Node(0).Cost Then
  730.             SwapTemp = pendingList(heapPtr1 / 2)
  731.             pendingList(heapPtr1 / 2) = pendingList(heapPtr1)
  732.             pendingList(heapPtr1) = SwapTemp
  733.             heapPtr1 = heapPtr1 / 2
  734.         Else
  735.             Exit Do
  736.         End If
  737.     Loop
  738.    
  739. End Select
  740. End Sub
  741.  
  742. Private Sub UpdatePendingIndex(Action As HeapActions, Index As Long)
  743. ' Routine keeps track of which pendingNodes() array items can be reused
  744.  
  745. If Action = heapPop Then ' need next reusable index
  746.     
  747.     If pendingIndex(0) = 0 Then
  748.         ' no resuable indexes available, therefore; set to UBound+1
  749.         Index = UBound(nodesPending) + 1
  750.     Else
  751.         ' (0) is pointer to array item to be used as the Index
  752.         Index = pendingIndex(pendingIndex(0))
  753.         pendingIndex(0) = pendingIndex(0) - 1
  754.     End If
  755.  
  756. Else                    ' adding a reusable index
  757.     
  758.     ' this portion of the nodesPending() array can be large; erase it
  759.     Erase nodesPending(Index).Node
  760.     If pendingIndex(0) = UBound(pendingIndex) Then
  761.         ' resize the array giving a small buffer to prevent resizing more often
  762.         ReDim Preserve pendingIndex(0 To UBound(pendingIndex) + 10)
  763.     End If
  764.     ' (0) is pointer to next Index to use
  765.     pendingIndex(0) = pendingIndex(0) + 1
  766.     ' update the array with the Index
  767.     pendingIndex(pendingIndex(0)) = Index
  768. End If
  769.  
  770. End Sub
  771.  
  772. Private Function LineIntersectCoord(PointA As POINTAPI, PointB As POINTAPI, y As Long, isBridged As Boolean) As Single
  773. ' Basic line intersection routine.
  774.  
  775. ' PointA & PointB comprise the X,Y coords of one of the lines
  776. ' Since one line is always 180*, only Y need to be passed for the second line
  777. ' Additionally, standard intersection routines test to see if the collision is
  778. ' on both lines; not needed here 'cause I want to predict where the collision
  779. ' would occur on the passed PointA/B line.
  780.  
  781. ' Modified to tweak & exclude unneeded calculations.
  782. ' One line will always be 180* horizontal, so there is no need to calculate
  783. ' the slope of that line or the Y coordinate of intersection
  784.  
  785. Dim x As Single
  786. If PointA.x = PointB.x Then ' vertically parallel points
  787.     
  788.     ' therefore a 90* & 180* intersection
  789.     ' simply return the vertical X coordinate
  790.     x = PointA.x
  791.     isBridged = False
  792.  
  793. ElseIf PointA.y = PointB.y Then
  794.  
  795.     'horizontally parallel points
  796.     isBridged = True
  797.  
  798. Else
  799.     ' calculate intersection X coordinate
  800.     Dim M1 As Single, B1 As Single
  801.     M1 = (PointB.y - PointA.y) / (PointB.x - PointA.x)
  802.     B1 = PointA.y - M1 * PointA.x
  803.     x = (y - B1) / M1
  804.     isBridged = False
  805.     
  806. End If
  807.  
  808. ' Return where intersection would occur
  809. LineIntersectCoord = x
  810.  
  811. End Function
  812.  
  813. Private Function CalcAnchorPt(nodeID As Long, parentNode As Long) As Boolean
  814. ' A core routine, 2nd most imporant routine. Detemines if an edge can be reached
  815. ' directly from a given point without obstruction.  If obstructions exists, then the
  816. ' routine creates anchor points / turn points for the path. This routine is the
  817. ' most time consuming portion of this class. We take significant steps to prevent
  818. ' running this routine if possible. Some steps/filters were applied before this
  819. ' routine was called and others are applied during this routine
  820.  
  821. Dim x As Single             ' generic counter
  822. Dim pNode As Long           ' node ref used in a Do Loop
  823. Dim bBridged As Boolean     ' booleans to determine anchor type/aborts
  824. Dim bDone As Boolean        ' flag to terminate the double loop below
  825. Dim bAbort As Boolean       ' flag to terminate this routine
  826.  
  827. Dim midAnchor As Long       ' possible anchor point
  828. Dim basePt As POINTAPI      ' used to determine if path can pass thru
  829. Dim targetPt As POINTAPI    '   an edge without obstruction
  830.  
  831. Dim bHasSubAnchor As Boolean    ' used to identify if subAnchors() array
  832. Dim newAnchor As Long           ' needs to be appended to
  833.  
  834. Dim anchorCost As Single        ' return value of calculating distances
  835.  
  836. ' Following Do:Loop is a filter to reduce number of times this routine is run
  837.  
  838. ' The logic here is... If a node is being processed and some point down
  839. ' its path has been changed (differnt parent assigned), then this node can be
  840. ' aborted. This is because whichever node got to that point faster changed the
  841. ' parent and is still being processed/considered in the path,
  842. ' it wasn't aborted & this one can be.
  843.  
  844. ' Check each anchor and subanchor to see if it changed parent
  845. ' Quick since any path will not have many anchor points in it
  846.  
  847. pNode = parentNode
  848. newAnchor = Nodes(parentNode).Anchor
  849. Do Until newAnchor = 0
  850.     If Nodes(pNode).Anchor <> newAnchor Then
  851.         If Anchors(newAnchor).Parent <> Nodes(pNode).Anchor Then
  852.             bAbort = True
  853.             Exit Do
  854.         End If
  855.         newAnchor = Nodes(pNode).Anchor
  856.     End If
  857.     If Nodes(pNode).SubAnchor Then
  858.         For midAnchor = subAnchors(Nodes(pNode).SubAnchor).Count - 1 To 0 Step -1
  859.             If Anchors(newAnchor).Parent <> subAnchors(Nodes(pNode).SubAnchor).Anchor(midAnchor) Then
  860.                 bAbort = True
  861.                 Exit Do
  862.             End If
  863.             newAnchor = subAnchors(Nodes(pNode).SubAnchor).Anchor(midAnchor)
  864.         Next
  865.     End If
  866.     pNode = Nodes(pNode).Parent
  867. Loop
  868. If bAbort Then Exit Function
  869.  
  870. ' set up the temp Node. Prevents undoing actions if we abort later
  871. Nodes(0) = Nodes(nodeID)
  872. Nodes(nodeID).Parent = parentNode   ' incoming node generally won't have a parent yet
  873. Nodes(0).SubAnchor = 0              ' ensure no subanchors are referenced
  874. ' get the parent nodes last turn point, this is our goal for path segment
  875. Nodes(0).Anchor = Nodes(parentNode).Anchor
  876.  
  877. ' set the base point of our diagonal line to the parent's last turn point
  878. basePt.x = Anchors(Nodes(0).Anchor).x
  879. basePt.y = Edges(Anchors(Nodes(0).Anchor).Edge).y
  880.     
  881. ' see if we can get diagonal from last turn point to our new edge
  882. ' without obstruction.  If not, find the nearest new turn point to
  883. ' the base point
  884.  
  885. Do
  886.     ' set the other end point of the diagonal line to the far edge of the target edge
  887.     ' Note: Setting it to any point on the edge will work with this algorithm
  888.     targetPt.x = Edges(nodeID).Z
  889.     targetPt.y = Edges(nodeID).y
  890.     ' identify 1st node/edge to be checked
  891.     pNode = parentNode
  892.     midAnchor = 0       ' reset
  893.     
  894.     ' continue checking up the path until we get to our basePoint's edge/node
  895.     Do Until pNode = Anchors(Nodes(0).Anchor).Edge
  896.         anchorPtLoopCount = anchorPtLoopCount + 1
  897.         ' see if this edge intersects the diagonal line
  898.         x = LineIntersectCoord(basePt, targetPt, Edges(pNode).y, bBridged)
  899.         
  900.         ' horizontal movements are handled uniquely
  901.         If bBridged Then
  902.             If midAnchor = 0 Then
  903.                 ' bridge to bridge to bridge
  904.                 If Edges(pNode).x > Edges(nodeID).Z Then
  905.                     ' moving east>west
  906.                     anchorCost = SetAnchor(pNode, Edges(pNode).x, Nodes(0).Anchor, newAnchor, nodeID)
  907.                 Else ' west>east
  908.                     anchorCost = SetAnchor(pNode, Edges(pNode).Z, Nodes(0).Anchor, newAnchor, nodeID)
  909.                 End If
  910.             Else ' at least one bridge involved; may be two or more
  911.                 If Anchors(Nodes(0).Anchor).x > Edges(midAnchor).Z Then
  912.                     anchorCost = SetAnchor(midAnchor, Edges(midAnchor).Z, Nodes(0).Anchor, newAnchor, nodeID)
  913.                 Else
  914.                     anchorCost = SetAnchor(midAnchor, Edges(midAnchor).x, Nodes(0).Anchor, newAnchor, nodeID)
  915.                 End If
  916.             End If
  917.             
  918.             If anchorCost Then  ' anchor was set or is now shorter than before
  919.                 ' set up subanchors if needed
  920.                 If bHasSubAnchor Then
  921.                     If Nodes(0).SubAnchor = 0 Then
  922.                         ReDim Preserve subAnchors(0 To UBound(subAnchors) + 1)
  923.                         Nodes(0).SubAnchor = UBound(subAnchors)
  924.                     End If
  925.                     With subAnchors(Nodes(0).SubAnchor)
  926.                         ReDim Preserve .Anchor(0 To .Count)
  927.                         .Anchor(.Count) = Nodes(0).Anchor
  928.                         .Count = .Count + 1
  929.                     End With
  930.                 Else
  931.                     bHasSubAnchor = True
  932.                 End If
  933.                 ' cache node's latest anchor point
  934.                 Nodes(0).Anchor = newAnchor
  935.             Else
  936.                 ' we will not continue. This path segment is longer than one
  937.                 ' that has previously gone thru the anchor
  938.                 
  939.                 If Nodes(0).SubAnchor Then ReDim Preserve subAnchors(0 To Nodes(0).SubAnchor - 1)
  940.                 ' ^^optional. But keep arrays lengths at a minimum if possible
  941.                 bAbort = True
  942.             End If
  943.             midAnchor = 0   ' prevents secondary loop from triggering
  944.             Exit Do
  945.         End If
  946.         ' obstacle at this point. Change angle of diagonal line to
  947.         ' go around this obstacle & try again
  948.         If x < Edges(pNode).x Then
  949.             targetPt.x = Edges(pNode).x
  950.             targetPt.y = Edges(pNode).y
  951.             midAnchor = pNode
  952.         ElseIf x > Edges(pNode).Z Then
  953.             targetPt.x = Edges(pNode).Z
  954.             targetPt.y = Edges(pNode).y
  955.             midAnchor = pNode
  956.         End If
  957.         ' move to the previous parent
  958.         pNode = Nodes(pNode).Parent
  959.     Loop
  960.     
  961.     ' secondary loop....
  962.     ' The 1st loop identified the anchor closest to the previous anchor that
  963.     ' will now be included in the path. However, we may still have other
  964.     ' anchors that need to be calculated. This loop will check from the
  965.     ' new anchor to our current node. If other obstructions occur, then
  966.     ' we will continue the dual loop until no more anchors are needed.
  967.     
  968.     If midAnchor Then   ' obstacle blocking a straight path
  969.         ' See if we can get to the new anchor without obstruction
  970.         pNode = nodeID
  971.         Do Until pNode = midAnchor
  972.             anchorPtLoopCount = anchorPtLoopCount + 1
  973.             x = LineIntersectCoord(basePt, targetPt, Edges(pNode).y, bBridged)
  974.             
  975.             If x < Edges(pNode).x Or x > Edges(pNode).Z Or bBridged = True Then
  976.                 ' obstacle cannot be bypassed
  977.                 anchorCost = SetAnchor(midAnchor, targetPt.x, Nodes(0).Anchor, newAnchor, nodeID)
  978.                 If anchorCost Then  ' anchor was set or is now shorter than before
  979.                     ' set up subAnchors if needed
  980.                     If bHasSubAnchor Then
  981.                         If Nodes(0).SubAnchor = 0 Then
  982.                             ReDim Preserve subAnchors(0 To UBound(subAnchors) + 1)
  983.                             Nodes(0).SubAnchor = UBound(subAnchors)
  984.                         End If
  985.                         With subAnchors(Nodes(0).SubAnchor)
  986.                             ReDim Preserve .Anchor(0 To .Count)
  987.                             .Anchor(.Count) = Nodes(0).Anchor
  988.                             .Count = .Count + 1
  989.                         End With
  990.                     Else
  991.                         bHasSubAnchor = True
  992.                     End If
  993.                     ' cache latest anchor & adjust the diagonal line again
  994.                     Nodes(0).Anchor = newAnchor
  995.                     basePt = targetPt
  996.                 Else
  997.                     ' we will not continue. This path segment is longer than one
  998.                     ' that has previously gone thru the anchor
  999.                     If Nodes(0).SubAnchor Then ReDim Preserve subAnchors(0 To Nodes(0).SubAnchor - 1)
  1000.                     ' ^^optional. But keep arrays lengths at a minimum if possible
  1001.                     midAnchor = pNode ' prevent 1st loop from triggering
  1002.                     bAbort = True
  1003.                 End If
  1004.                 Exit Do
  1005.             End If
  1006.             ' move to previous parent
  1007.             pNode = Nodes(pNode).Parent
  1008.         Loop
  1009.         ' if we got to the turn point, no need to continue the primary loop
  1010.         bDone = (midAnchor = pNode)
  1011.     Else
  1012.         ' we have an unobstructed possible path from last anchor to our edge
  1013.         bDone = True
  1014.     End If
  1015.  
  1016.     ' because there can be multiple turn points needed to move to our
  1017.     ' target edge, we may need to repeat as needed
  1018. Loop Until bDone
  1019.  
  1020.  
  1021. ' replace the node's original parent reference
  1022. Nodes(nodeID).Parent = Nodes(0).Parent
  1023.  
  1024. If Not bAbort Then
  1025.     ' finalize     ' i' replace the node's original pi   a or more
  1026.               ' m       ' obstacle at this pf NoPreserve subAnchTgDone = T  have aneinalng path, we \^Pt, tartswcaus loop so there is aour
  1027.     Bhors)
  1028. .V obstacle , we \^Pt, tartswcaus lf we got to theed out obstr   End bDone
  1029.  
  1030.  
  1031. ' reace apath from l(e aneinalng po Unfit Dor        ' i' rir   itthei      bet has prut obstause the            ' cache node's latestthe
  1032. e have an unobstructen
  1033.     ' n
  1034.     i End  rserve su i Eode's rNo     he        ss   "  Upe's a-aus l bett o EnxXu i Eode's rNo  t bypassed
  1035.   dAnc-
  1036.   dAnode's latestthe
  1037.  we g>to repeat as neevYrrartswcaus lf we goePt.y
  1038.    Anode's latesunt = ancho our en NoNodes(r end point aus lf we got taUpe'sw connection if  with passd "n    r lf we geapOffset  End If
  1039.            p soI!        '(Nodes(nodeID) '(NdAnod ' obtherwise movinnection ifs that need rom y      '(Nodes(n. However, weeeeaus lf we gocse is '&psmidAnd IyiRx'(Nodes(nodeID)         ' cache yiRx'(Nodeour e gocser   target ednVal    tr finaldAnchor Then   ' obstaclr, wstaclr, Done
  1040.  
  1041.  
  1042. ' replace t's lateas pending,
  1043.     ' ensure no t lanal line
  1044.       bstr   E  End If
  1045.      gTyeed out obstr   End bDon        leviortions
  1046.   's &o soI!        '(Nodeatesarget eitthd IyiRxtr   Eat thiyDon   
  1047.        usee, we waodeID)  ginal pi  tttttttttttttttttD)  ginlatione, we wap    tione, we wap    tione, weeasePt = tatesargecfinalize     ' i' replace the node's original pi   a or more
  1048.       e
  1049.    ' i Eoinlati      D         lt edge
  1050.  l)at t Eoinlati      DD).Parenr(0 To .Count)
  1051. cTsobstaiyDoh
  1052. cTsobstae
  1053. ' &Loope tiy     '(Nodeatoh
  1054. cTsobort = True
  1055.    Eoinlati      DD).Parenr(cTsobstaiyDoh5enodesPendises(0).boge
  1056.  lcTsobo  usee, we ng te
  1057. ' path pensobstaiyDoh5enodesiiiienodesii' ensure 
  1058.        poi
  1059.    ' i Eoinlati      D  newAnsee nt)nsobstaiyDoh5enodesiiiieoiA        hores laaps be' inc, newA nsobstaiyDoh5enodesiiiieoiA        hores laaps be' inc, newA nsobstaiyDoh5enodesiiiieoiA      oaionee gocsesii' ensure 
  1060.      
  1061.         2hearentNotewA nsnd wasted uinlati      Dt = anchoXXXXXXXXXXXXXXXXXXXXXXXXXXt thiyDon   
  1062.        usee, is >U gocsetrised
  1063.  
  1064. If Actionons.  If anc*     usee, is >U/
  1065.  l)atuXXt theised
  1066.  
  1067. Ifc    conne       v
  1068.  
  1069. GetAdjactatuXXt see, isubAnchorithout obstruction
  1070.  
  1071. Dim bHasSubanchoXXXrvs0).Anchor '. This qr(Abs(midX ms(0).SubAnchor -toge
  1072.  llC' routinitthd IyiRxtr   Eat thifpath
  1073.         ' Sbe Cdingfodjacthseee node's t = anchoXXXXXXXXnot nA nsndbet has pinitthAnchor(midAncra.5enoe g>to red &LodeIDiiienodesodeIDiiie = tateNodes + 1est F cost at top of heap
  1074.     heapP
  1075. If Not bA  ' Sbe     mruction
  1076. esoodes(0)cate Funct      
  1077. GetAdjactdes(ver, wbHasSu- nsobs.prevne  (usee, wr,  Funct   kn poindAncra, wr,  Funct revne  (um'ne tne  (usee, wr,  's s poindAnc.fe).Anice, is cXXXXXXcate Funce
  1078. GetAdjact  r
  1079. ' pd sed
  1080. c is > red gFuncEdges(pNode). the p<
  1081.       bAnchor Then ReDim Preserve subAnchors(0 To Nodes  > red gFuncEdges(tAdjactEnUUUUUUUUcoo that edge. Us    ti(nlati    dDone     ieo      p soI!   erve subAnchor'ateocsesii' ensure 
  1082.      e. Us    ti(nlas set      , isinal pi   a or mde).Anchor
  1083.  nnectioDo
  1084.    &h5enm inco,i   aes(0) = ment is2 poindA   ' cacget er1 / 2es(r enZ+edjactEnUUcthseee o,i   aes(0) = ment is2  Us    ti(nlati    dDone s origs(r enZ+edjactaps >U/
  1085.  l)asnco,i   aes
  1086. l, we \^ctais longer than oniagonau  ' nol, w   a or  see, isubAnchorithout obsE)longer t    Actionsiu(r ebe mu mae  (u+edjactEnUUc  'ss              mid)m  ' n
  1087.  ntersects nger than thseee o,i   aem  'ss              mid)m  ' n
  1088.  ntersects nger than thseee o,i   aem  'ss              mid)m  ' n
  1089.  ntersects nger than thseee o,i   aem  'ss          timmmmmmmm nodeID nt As Loer than thseee o,i   asent Pathalng(n onpndAnc.fe).Anice, is m Pr lines; not np    em  'ss   2as pinia  Us few wn thsssssem 5enodesPendises(0).boge
  1090.   ' n ionsiu(rlhalng(Pathalng(n onpndAnc.fe)./ r linest node; then allow mid)m   til pNode = Anc' will nownc.fe).b  an
  1091. ' remidA(wbHasow mnt is2   routinitthd IyiRxtr   Eat thifpath
  1092.         ' Sbe Cdthseee o,i -Rxtr   Eater than       '1qei  tilu
  1093. ' remidc Cdthseee o,i -Rxdesgment
  1094. df'r enZ+edjacsoI!   pthifpathn  nt
  1095. df'r enZ+dund(t set    o Then
  1096.          w.ce,oedjactEn  em  'ss  Long
  1097. l             thalng    Funct revne  (um'ne tnactEnUUcibstaiyDoh5enctEnUUc  ss  Q    x = Li' move 
  1098.  ntersecaaaaaaaaaeID5enctEnUUc  ss  Q    motnactEnUUcibstaiyDoh5enctersecaaaalng   nt)UUcibstahseeA(wbH'PendiseLong
  1099. l 'r eer  If Anchg
  1100.       (wbH'PenenctEnUUat least 2 morV at a mi newAnchor, nodeID)xapP This qr(tdesIf Anchg
  1101.       (w       End IM    (wnsn
  1102.      9continue ors End UUat le)des(nodeID).Pare ment is2  Us   f'r enZ+dund(tare meanchoX=e
  1103. ' patatbit e tnactEnUUcibstaiyDoh5enctEnses(0).b    9conZ+dund(tarf(hor nodeID)xapP  o,i -Rxdesgment
  1104. efe).Anice, is cXXXXXXcate  e gocser   targersecaaaalng   As  leaschTgDone = T  have aneinalng psneinalng psneinalng psne le)des(nodeID).Pare menu.Parace
  1105.            ge).y)e).y s ss  Q  Nodes(pNode).  'sse).Pa  ' XXXXX           ge).y)e).yiaiyDoh5enctEnUUc  ss  Q    x = L End If
  1106.   anol,iess  r noewAnsee nt)nsobstaiyDoh5enodrIf nodesPen
  1107. '  yfiyDoh5enbAnchors(NodenIf b pent yfiyDoc  s5en= mea      gDon ifs thaEnd nodsssssTlng Ot)
  1108. cT,i -Rxtr2           e).  'sse).Pa  ' XXXXX           ge).y)h5enctEnses(0).b ) = L Enendt
  1109. df'rfiyDohtdes(verEnd If).Anice, i  ' 0fl noet(n1XXX&u  oaiodes(no)e)tEns  ' 0fl no     bstr   E  End If
  1110.   ^t)nsobstaiyDoh5oh5etoaiodes(no,lc(Pathalng(n onpn,   As  revne   ss  Q  Nodes(pNoEnd (nodeID).P<:    motnactEnUUcibstaiyDoh5enctersec5(no,lc(Pathalng(n oa   = (midA    i
  1111.             r
  1112.  revtr2 ont thssssoops til finaf ensu = (  9conZ+wn thsssssem 5enodesPendises(0).boge
  1113.   ' n ionsiu(rlhalng(Pathalng(n onpndAnc.fe)./ r linest node; then allow mid)m   til pNode = Anc' will nownc.fe).b  an
  1114. ' remidA(wbHasow mnt is2   routinitthd IyiRxtr   Eat thifpath
  1115.         ' Sbe Cdthseee o,i -Rxtr   Efew/aaa5Doh5enbAnchors(s  Q  )r ment ir
  1116. ' pd s
  1117.    su F  routinirstaiyDofodjacwoedjactEn  em  'nchorithout obstruction
  1118.  
  1119. Dim bHasSubanchoXXXrvs0).Anchor '. XXnoaI.le)des(t oes; ors    ReDimthal End If
  1120.     p    l pNsu F  routrgersecawe g>to repeat as neevn
  1121. ' remion
  1122.  
  1123. Dhout remiondRxtr  /aaa5Dohy If
  1124. wr ' nnnnnnnnn).boge
  1125.   ' njoreRxtr   mifdEode's npn,   A==halnfew/aaa5Ds til fide'sy
  1126.    An     peaoithou)m  mde).Anchor
  1127.  nnectioDo
  1128.    &h5enm incjoreR5enm intestthe'chor
  1129.  nye Fuithou)m Anchornt = .CousnectioDoone of tttil Endhe'chornt = .C
  1130.   ' If
  1131. .yndAnrs il Endhe'chornt = .C
  1132.   ' If
  1133. .yndAnrs il Endhe'chornt = .C
  1134.   ' Ifc)ors(s  Q  )r methou)m Anchornt = .Cousnec poind.le)bstaiyDoh5eno , = .ee gocs tttil (1 We t later
  1135. nyDofodjacwoedjactlss  i gFuncEd.le)bstaiyDo
  1136. End With
  1137.  
  1138. eapActions, Index amid)m  ' n
  1139.  waiyDofy If
  1140. wr ' nnnnnInefsses(0).boge
  1141.   ' n ions(  erfffffhen&
  1142.  waiyDofy If
  1143. wr ' n. Edgesfen
  1144.    f Endhcs tttil (1 We t latey If
  1145. ole)dovn
  1146. ' remion(Nodeour e go       ' set= .C
  1147.   ' If
  1148. .yndAnrs il
  1149. nc' nnnnnInefsses(0       tateNn
  1150. asSu.x, Nodes(0).Anchornectio.ince we don't knnInefsses(0 )
  1151.    Nn
  1152. n ionsi D    f
  1153. o.ince we     Vloithotarg (nodeID).P<)m Eoinlati      n. Edgesfen
  1154.  ionnchoY
  1155.    ringre -t o EnxXu 1dAnrs ilt o Eno).Sue X,Y ccingre -t           If      onnchoY
  1156.   eapActions, Index amioh5ee t rs ilt o Eno).Sue X,Y ccingre -t           ss  Q   nterse qcAncnt
  1157. dfteNnonZ+wn thsssssem 5enodes     subAnchorensure 
  1158.   ttttttD)  ginlremiondRxtr  /aaa5Dotions, Indruction
  1159. sssem 5es(0).bogoY
  1160.    ringre -t o EnxXu 1dAr
  1161. ' pre -t           IM es  > red gFuncss  Q  3ag (nodeID.aiyD  n.naf ensu = (  9copath' set=uncss move to eapPop
  1162.     ' Ws il
  1163. ncsA         I
  1164. o.i
  1165.    = UBoun    Exitvery otherncnt
  1166.  if theSi ilt
  1167.  nInefsses(0 )
  1168.    Nn
  1169. n s track of whiclt o pss  t = .Cousnecvery otn
  1170. asSO
  1171.  
  1172. ' replace t'V (nodeID.andRxtr  /aeI
  1173. o.i   e       des(0).Afs if the what odeID.i(nle trto.i r  = sses(0 )   ' Ws il
  1174. ncsA         I
  1175. o.i   xdeo)isu = (  9cseee node's t = anchoXat thi0 )
  1176.    e
  1177. nc1enZ+edja (wbng(pe,Anc.       e  (are menu.Pa,Anc.  ginlrenc.    yDoh5engesfen
  1178.   ve.   
  1179. nc1enZ+e0)
  1180.   = .CtbndRxasSO
  1181.  
  1182. ' s, Index atrack of whicen
  1183.  io t'V t odess  Q  3ags(0).Endex
  1184. >e X,abuty .
  1185. asSO
  1186. lati enctEnUUc  ss  Q    xr   Parace
  1187.    whi
  1188. asSOeto rtoaiotIM    (wg).C   til pfve ae
  1189.         End If
  1190.   de's t = anchoXa  If AD)x whi
  1191. asSOeto t        bDIf AD UUaNUUcbon.  If obstrucbuty .
  1192. asSs(0).b    9conZ+dund(tarf(h UUaNUUcbon.  aiotIM choXa  If AD)x whi
  1193. asSOeto whicl,r    tinia  If AEndex
  1194. >e  If ges(mino,lc(Pathalng(n     tinia  If de'se)des(t oes; or    Ss(0).b  tiniaiiiiiiiiirA(n onpn,r        (wg).C   til'se)des(t oes; or    Ss(0).b  tin    i,(t osssssem 5enodesP(  9c,r    tinia  If AEndex
  1195. >e  c,r e
  1196.    dual loour e gge
  1197.   ' n ionsiu(rlhalng(Pa  tin    i,(tTinia  If AEndex
  1198. Nrs il Endheng) de'se)chorensure 
  1199.   ttttdl nowAnchor Tt1Nnsobnol, Q  3ag (node  i,(tTinia  ReDimthou)  3ag (node  i,o t'V Anchor Then ReDapTemmidA(wbion m 5 bet hasjssesdl nowAnchor Tt1Nlhalng(Pa  tin    i,(em 5enode)chot node; then alle latep1h' set=uncss move to eapPop
  1200.   ns  ' 0fl no     bstr   E  End If
  1201.   ^tss move targetPt.r than thseeea
  1202.   rbstr   E fferrIor)
  1203.    i,(iueng) debnol, Q  3atiniaibttttdl mmidA(wbionfferrIor)
  1204.    itep:css move to eadA(wbHas(n. Hof
  1205.   ^tss o. Edgesfen, ^tss o. Edgesfeendin   bttttdl mmidA(wbionfferrIor)
  1206.   lin
  1207. ' .o. E     pendck emmidA(wbionfferrIor)
  1208. fob Q  3atiniaibttttdl mmidA(watesttbttttdl ack of wi. E rQ  3ag rbstr   trucbuty .
  1209. I!  poss Hofack ttbtttnfferrIor)
  1210. fob Q  3altrucbuty .
  1211. I!As Long
  1212. Dst = SetAnchor point on' If
  1213. .yndAnrs        "s Lo(watesttbtt.b    9conZ+        "s Lofort    "s  Hofack"s Lofort    "s s.s(nodeI   E fferrIor)
  1214.   n)
  1215.   n)'  an
  1216. ' re
  1217.   n)'   midXor)
  1218. fob Q Ss(0).b  tiniaiiiil Aaiiiil Aaiiiis(2)   bttnffev
  1219. ' Follaltrucbuty .
  1220. d2oI   Euiii  tinia  If AEndex
  1221. >e  c .boge>r l5eno s pendb   bttny .
  1222. d2oesee, wr,r
  1223. ' F  midXod2o
  1224.   lineI  .)'2fobisbuty .
  1225. I!As Long
  1226. Dst = SetAnchor point on' If
  1227. .yndAnrs  fferfferrIor)ineI  .)'2fobidin 
  1228. I!As Lon e gge
  1229.  Po    ')' b  tinia If
  1230.   o whi
  1231. asSOeto r
  1232. Dst = SetAnchor point onebogerey b  If
  1233.     ).y - Ed     0).bdAnrs  fferfferrIor)ineI  .)Then RdaryEnd If
  1234.     f
  1235.              c .bogM
  1236.          ggeiil pendingr .bogM
  1237.      i(nlas set      Mfobidin 
  1238.  ' 0fl nno st he   f
  1239.  gM
  1240.  
  1241.   dAnc-
  1242.   dAss movem 5 bet hasjssetys  fferfp obstrucbu.y - Ed     ucbuty NotewAoaeu+edjactEnUUct u+edjactEnUUc   i(nys  faas,etAnctin bu.y - Ein bs,etAnbionfferrIorneed .mioh5'   pNode = Nodes(pNodC   .Anchor(.Coffer NodesUc  d  )Iornee bu.y - Efobidinder No or c, Inde Inde Inde Inde        f(h UUaNUUcbon.  aiotIM chd  )Iornr   s alr finaldAogM
  1243.          r NodesUc te
  1244.  gM
  1245.  
  1246. tionE rQ  3ag100)
  1247. nchoXXXXXXt thiyDon   
  1248.       riliem  (n. HodjactE
  1249. nc.y - Einyve todjactE
  1250. nc.y - EinyvlRxtr  /aaa5Dy - Ed aaa(obstrucbodesUc  d  )ib   Ss(0).b  tin    i,XXXXX   y -    RxtE
  1251. nc.y,wcaax
  1252. >e  thsee2ibttttdlser  - ee  tp y -   Must = S=.y - Ed     H.io .Ae/tttttttturif usnecv      de).t ir
  1253. ' pd s
  1254.    su F  routinirstaiyDofodjacwoedjactEn  em  'nchooa
  1255. nc.y - Einyvlg Mustede'sy
  1256.  doedjactEn  em    n. n   s alr finaEinyvlRxtrdesalng(n onpndAnc.fe)./ r lineet
  1257.    lng(n on1t refermineet
  1258.  De
  1259.    whi
  1260. asSOe,etAnbL tiniaps s
  1261.    su F  rlRxtrdesaln    rif 
  1262.  
  1263. Else
  1264.     io  AaiiilCoff   n. V lf EinyvlRxtrdee t'V (cten
  1265.      su F salng(n - EfobidindyEnd dex aO
  1266. 'eet
  1267.   de'sstaiyDo
  1268. .y,wcaaxctExtrdesa0g(n - E.ys  faas,etAn
  1269. asSu.x,        lt"ernStr  /aaa5Dy - Ed aaa(obstr ttbtttnfferrIafn onpn- Ed aaa(obstr ttbtttnfferrIaf tExtrd
  1270.  MsctEnUUct u+edjactEnUUc   i(nys  faas,etAnctin bu.y - Ein bs,etAnbionfferrIorneed .mioh5'   pNo  faaN   pNo  faaN trd
  1271.  Msw(Z+dund(tarrefered .,faaw
  1272. x ctEnUUc   i('y  faaN?tpNodpT  lt"ernStr  he  ID.ieetinyMstaiyDofoNo  Uc  ,oobstructen
  1273.     ' km<faas,etAnneed .mioh5' ery otn
  1274. asSOd km<fa=nStr  N trd
  1275.  Msw(Z+duPallow  exact as possib  aes(0) = n
  1276. aY
  1277.    (0) = n
  1278. aY.
  1279. asSs(0)   sse
  1280. '                 ld wha
  1281. asS<  rbstr.Anchor(.Coffer NodesUc  d  )Iornee bu.y   TheneaN trd
  1282. erenStr  N trd
  1283.  Msw(Z+duPaasS<  rbstr.Anchor(.Coffer NodetcfaaN?tpNoy - EdtcAnchorPt(nodeIDuZ+duPaasS<  rbsLoopCount + 1
  1284.        bstrucbodAnyIictin b minee bu.y   ThenxrasS<ID)
  1285.                 Else ' west>east
  1286.              int            ld wha
  1287. achor(.Coast
  1288.     ' n
  1289. ount + 1gM
  1290.  
  1291. tionE rQ  3ag100)
  1292. nchoXXXXXahou)   tin     ill nownc t = aEinyvlRx ccingre -t    onE rQ  3ag10-
  1293. f nger thf faaN?tpNoPaase -t in  titd .miofoAnd IyiRx'(Nodes(node f
  1294. o.iT(node  tr fina<fa=nStr  N uZ+duPaasS<  rbstr.Anchor(.lr   Para r l, IndNodatif
  1295. o.iT(node thseelRx c  tr finnger thf faaN?tu.boge>-
  1296. ere   tr fina<fa=nStr  N uZ+duPahseelRx c  t  ' n
  1297. ount huet= .C
  1298. wXXahou)   tinnor)
  1299. df'r enZ De
  1300.    whi
  1301.             y0nt huet= .C
  1302. wXXahx' n
  1303. k    e. Us    ti(nlas set      , isinal pi 
  1304.     io  AaaxctExtaStcfaaNe gocs toAncxtaStcfaaNe gocccccrEin bs,et&a r l,Ein de's origin          yPahsT frodtd . longer bu.y   ThenxrasS<IUs  tcfaatt = aDIf AD)' b  tinia If
  1305.   y - D)' b  tiniUs    ti(nlas -VT - Edges(.Edge).y)
  1306.   yPahso
  1307. nyDof Thenr.Anchorl -VTlace nlas -VT -VTlace nlas -V
  1308. nyDof Thenr.&cv eZAigin   D.ieetinyMstaiyDofoNo  Uc - Edges(.Edge).y)= nlas yofoNo  Un   mf(.Edge).y)
  1309.   yPahsp         b yfiyDoh5enbAnchoet eitthd IyiRxtr   ahsp =     iID).P<:bge
  1310.   mAnrs  fNo  faaN trd
  1311.  Msw(Zove everdsioh5'  rs  fNo' b  tiniU ti(nlas -VT - Edges(.pendieed .mp =2).y p=No  Uces(.penng(n onpnh
  1312.  
  1313. C on1t ro Edthseee o,nlas -Vhen
  1314.  v              on1t r,ini  d  )Iornee bor  A su ,bu.yi  d  )Iornee bonchor closesDon   
  1315.    End Ip inc, newA nsobstaiyDoh5enodesif wA nsobsi  nchorh  d  faDon   
  1316.    End Ip inc, newA nsobstaiyDoh5enodesif wA nsrh tturif usnecv      de).t ir
  1317. p   s
  1318.   n(   S SetAnchoPgemp = oag100)
  1319. es(.pe.
  1320. ' Binary heaps basically move every other  y moveBridged As Boolearnee bkthd e  (um'ne tnactEnUUcibst bkt o,i -Rxdesgmenee bu.y 
  1321.   ' enee t'V (cten
  1322.   enZ arnee b
  1323. wXXahxPahsp . As Boolealbionfferr3S+duPonpnxtrdee tpnxtnchorh  d  faDon   
  1324.    End Ip inc, aDon   
  1325.    End 
  1326.    p incmidAnDst = SetAnal End Ide).t '   ee bA neral)
  1327.             If bIsLat - E    td no   ti Wo1 * 2)).Cost TheIde).t '   nt ThIUs ng itduPoidAnDsLatAnrs  nat . + 1  Enner thao1 * 2)).Coc.boge.lost coc.bogerNo  o
  1328. laty)
  1329.   yP bu.y - Ein nd Ide).t '   ee5Do,v.Parentis(2)   btu(s Boolealbionffer r Nodetnchorh  d= (midA IUs  tcfaatt = aDIf AD)' b  tyio  AaaxctExtau(s b  tyiautewArx/t(nZ+ptewAr hea 
  1330.    p incmidAnDentis(2tiniUs    ti(n rbstr.Anc.h  d  faDon   
  1331.    En Wo1 * 2)).Costnt
  1332. Don aatt = aDIf AD)V
  1333.    Eas,etAnct
  1334.  HasonIf b pntr r Nodetny - EintewArv  tcfaatt = aDIf AD)' b  tyti Wo1 * 2)).Cosde) tcfaatt = b  tyti Wo1 * 2 TheId True
  1335.             Exit Do
  1336.  yfiyDoh5IUs  tcfaatt =sitduPoisobsiyDoh5IU  tcxf)ealbionffer r Nct
  1337.  ib pns AD)' b fffffffGrwnng(n onchoX=e  tcfaata )
  1338.   yP bu.y - Eio1 (s b  tu'd Iflngys  fferfp obs,bn
  1339. asSu.x&or = pN
  1340.  
  1341. Dim bHa
  1342. laty)
  1343.  T    yerfp obsmlnDentis(2tiniUmT  fferfp oIornee bu.y - E
  1344.             Exit Do o. ct
  1345.  Ha
  1346. e menfobidwiona   Lc t =  gM
  1347.  
  1348. tionE r ntionsi D    fD)x wheID).Pare menuAn inc, aDon   
  1349.    End ee bu.y - ).Sui b- E
  1350.     io  AaiiilCfaDon   uAn inc, a        ' ^^opti
  1351. latrfp obe 
  1352.   tt+iixf)ealbionffer r Nct
  1353.  ib pns AD)' b flCfaDon gDs(Sa      ntis(> ' ^^o   ntis(> aDon  bttttd b  tyti WAD)' b fffff r Nct
  1354. oAD)' b fecfaatt =(s b  tyflCfaAD)' lh  yerfp obe 
  1355.  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXN fecfa.Pn lrrnee bu.y - Ea(par.y = Edges(pNodDoh5enca(par.yXXXXXXXX2Y^opti
  1356. latrfp oaAr
  1357. '^opti
  1358. lo rbstr.Anc.h  d  XXXXoh5e0iiiiiinc.h  d  XXXas,etAnc  .)'n2Gc.h^opti
  1359. l  fatis(> aDon  btttts d  u.fl nno syDoh
  1360. cTsobstae
  1361. ' &Loope t )- Esobstae
  1362. 'pti
  1363. luiuno   ti  nsobsu d  uleaN.fl Pn lrrnee bpti
  1364. lua tiniU ti(nlas -VT - EdgesXahou)  uno   ti  ulea  ti(nlasm  ' End    >o o. ct
  1365.   ' st tonlatXXXas      XXXXXXXX1.Pare menuAn innnnnnnnnorn H.io .AsmlnDentis(nc.h  di  ulea  XXXXXXXXLong
  1366. Dst = SetAnchor point Long
  1367. Dst = SetAnchor p di  ulhFcalbionff plserve pendingList(1 T plserf nodesPen
  1368. '  yfiyDoh5enbnZ+dund(tarf(h UUaNUUcbon tExtrd
  1369.  MsctEnUUct u+edjac  di  ulea  XXXXXZ+       bu.y - d.i;    entdAnrs  ffe+        di   Lc t =  gM
  1370.  
  1371. tionE r  nnnnnShifpathn  toDent     bu.y - d+       bu.              Ifmti
  1372. lat100)ot>   XXXXXZ+    1of 2 TheId True
  1373.  XXXXXXXXXXXXXu+edjac  de. TheneXZ+       bu.y - d).  'sse).Pve evero.Z+dund(tarfYlng(PaXXXXXXXXXXXXMcfaatt   rnnnnno=  gM
  1374.  
  1375. tionE r  nP
  1376.     o .Ae/ttttttttuyXXXXXXXXXXnat . + 1  Endi  pnP
  1377.  +       bu.    gM
  1378.  
  1379. tionEduPaasS<   nP
  1380. ve e
  1381.  
  1382.  
  1383. tionv - d        a   
  1384.  
  1385. CePt only Y need to be passetg- EsobstaeasseaDon   uAn  Esocontinue the du) = LineIrac  di  ulea  XXXis ebu.y -  m  ' EnXXXXXXXXE nodt - E ..y -  m  ' Ef)eaT - )fas      XXXXXXXX= SetA- d.i;   c        .or = .y as  tXXoh5e tyti WAD      - )fas tttD)  ginlrelStr  inlrenc.    yDebu.y)' yed.i;   c      XXXXXXXbu.yns,(no-nmenee bto be   .or =Lfaat   XXXXXXXbu   s path segmentXXXbu   s     y - d).  'sse).Pve evero.Z+dund(tarfYlng(PaXXXXXXXXXXXXMcnd Itr.Anchor(s pathnl(h UUaNUUcbony
  1386. ve .y - io,i -Rxdbidwiona   L  tcidf'rf
  1387. '  yfiyDoA  tru.ynchor(s pathnl(ve .y - aXXXXXXXXXXXXMcnd Itr.ace t'V (nodeID.andRxtr  /aeI
  1388. occccco be passetg-n
  1389. l1alculate
  1390. ' the r^fiyDoA oblrLc t =  gM
  1391.  
  1392. ti o EnxXu =  gM
  1393.  
  1394. socontinue tocontinand With
  1395.  
  1396. eapsrh tturif usnecv      de).t ir
  1397. p a5Dy - Ed aaa(obstrucbodesUc  d  )ib   Ss(0).b  tin    i,00)o - aXXXXXXXXXXXand Icont.p t later
  1398. ny(h UUaNUUcbon tExtrd
  1399.  Msc +     XXohs  uAn  Esocontinue thuyx
  1400.     xn2dRif usnecv s bDy - XXXXAndRxtr t'   vne   ss  Q   tin    i0).br)
  1401.   nen
  1402.  v *n rbstr.Anc.h  dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif u dif