home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / vbboid / boid.bas < prev    next >
Encoding:
BASIC Source File  |  1999-09-16  |  32.9 KB  |  937 lines

  1. Attribute VB_Name = "Module1"
  2. Option Explicit
  3. Declare Function LineTo Lib "gdi32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long) As Long
  4. Declare Function MoveToEx Lib "gdi32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, ByVal lpPoint As Long) As Long
  5.  
  6. Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
  7. Public Const SRCCOPY = &HCC0020         ' (DWORD) dest = source
  8. Public Const SRCINVERT = &H660046       ' (DWORD) dest = source XOR dest
  9. Public Const SRCPAINT = &HEE0086        ' (DWORD) dest = source OR dest
  10. Public Const SRCAND = &H8800C6          ' (DWORD) dest = source AND dest
  11.  
  12. Public Const PI = 3.1415926
  13. Public Const PI2 = 3.1415926 * 2
  14.  
  15. Public flock As New Collection
  16. Public objects As New Collection
  17.  
  18. Public Sub AddBoid(flock As Collection, X As Integer, Y As Integer, ByVal Dir As Integer, Bcol As Long)
  19. 'helper function to add Boid to the specified collection (flock)
  20. Dim Colour As Integer
  21. Dim boid As BoidClass
  22. Set boid = New BoidClass
  23.  
  24.     boid.X = X
  25.     boid.Y = Y
  26.     
  27.     boid.Colour = Bcol
  28.     boid.id = flock.Count
  29.     
  30.     boid.direction = Dir
  31.     boid.speed = 10
  32.     
  33.     flock.Add boid
  34.     Set boid = Nothing
  35. End Sub
  36.  
  37. Public Sub AddObstacle(objects As Collection, X As Integer, Y As Integer, ByVal Radius As Integer)
  38. Dim obs As ObstacleClass
  39. Set obs = New ObstacleClass
  40.  
  41.     obs.X = X
  42.     obs.Y = Y
  43.     
  44.     obs.id = objects.Count
  45.     
  46.     obs.Radius = Radius
  47.     
  48.     objects.Add obs
  49.     Set obs = Nothing
  50.     
  51. End Sub
  52.  
  53. Sub DrawBoid(flock As Collection, Canvas As PictureBox, ShowColours As Boolean, ShowArrow As Boolean, ShowCircle As Boolean)
  54. Dim boid As BoidClass
  55. Dim d As Integer
  56. Dim u%
  57. Dim NewX As Integer
  58. Dim NewY As Integer
  59.  
  60. Dim XDist As Integer
  61. Dim YDist As Integer
  62.  
  63. Dim AHx As Integer
  64. Dim AHy As Integer
  65. Dim Theta As Integer
  66. Dim Bcol As Long
  67.  
  68.     For Each boid In flock
  69.  
  70.         Theta = boid.direction
  71.         
  72.         If ShowColours = True Then
  73.             Bcol = boid.Colour
  74.         Else
  75.             Bcol = vbBlack
  76.         End If
  77.         
  78.         boid.NewY = boid.Y + (10 * Sin(boid.direction))
  79.         boid.NewX = boid.X + (10 * Cos(boid.direction))
  80.         
  81.         Canvas.Line (boid.X, boid.Y)-(boid.NewX, boid.NewY), Bcol
  82.         
  83.         If ShowCircle Then
  84.             Canvas.Circle (boid.X, boid.Y), 5, Bcol
  85.         End If
  86.         
  87.     'arrow head
  88.         If ShowArrow Then
  89.             AHx = 5 * Cos((Theta + 45))
  90.             AHy = 5 * Sin((Theta + 45))
  91.             Canvas.Line (boid.NewX, boid.NewY)-(boid.NewX - AHx, boid.NewY - AHy), Bcol
  92.             AHx = 5 * Cos((Theta - 45))
  93.             AHy = 5 * Sin((Theta - 45))
  94.             Canvas.Line (boid.NewX, boid.NewY)-(boid.NewX - AHx, boid.NewY - AHy), Bcol
  95.         End If
  96.         
  97.     Next
  98.     Set boid = Nothing
  99.  
  100.  
  101. End Sub
  102.  
  103. Sub DrawObjects(objects As Collection, Canvas As PictureBox)
  104. Dim obs As ObstacleClass
  105.     
  106.     For Each obs In objects
  107.  
  108.         Canvas.Circle (obs.X, obs.Y), obs.Radius
  109.         
  110.     Next
  111.     Set obs = Nothing
  112.  
  113.  
  114. End Sub
  115.  
  116. Sub DrawForces(flock As Collection, Canvas As PictureBox, SensorDist As Integer, ViewTheta As Single, ShowCentre As Boolean, ShowSep As Boolean, ShowAlign As Boolean, ShowSensor As Boolean, ShowBox As Boolean)
  117. Dim boid As BoidClass
  118. Dim d As Integer
  119. Dim u%
  120.  
  121. Dim tmpX1 As Integer
  122. Dim tmpY1 As Integer
  123. Dim tmpX2 As Integer
  124. Dim tmpY2 As Integer
  125. Dim tmpX3 As Integer
  126. Dim tmpY3 As Integer
  127. Dim tmpX4 As Integer
  128. Dim tmpY4 As Integer
  129.  
  130. Dim tmpStart As Single
  131. Dim tmpEnd As Single
  132. Dim HalfTheta As Single
  133.  
  134.     HalfTheta = ViewTheta / 2
  135.     For Each boid In flock
  136.  
  137.             If ShowSensor Then
  138.             
  139.                 tmpX1 = boid.X + (SensorDist * Cos(boid.direction + HalfTheta))
  140.                 tmpY1 = boid.Y + (SensorDist * Sin(boid.direction + HalfTheta))
  141.                 tmpX2 = boid.X + (SensorDist * Cos(boid.direction - HalfTheta))
  142.                 tmpY2 = boid.Y + (SensorDist * Sin(boid.direction - HalfTheta))
  143.                 
  144.                 tmpStart = PI2 - (boid.direction + HalfTheta)
  145.                 tmpEnd = PI2 - (boid.direction - HalfTheta)
  146.                    
  147.                 'Debug.Print tmpStart, tmpEnd
  148.                 
  149.                 If tmpStart > PI2 Then
  150.                     tmpStart = tmpStart - PI2
  151.                 End If
  152.                 If tmpStart < 0 Then
  153.                     tmpStart = tmpStart + PI2
  154.                 End If
  155.                 
  156.                 If tmpEnd > PI2 Then
  157.                     tmpEnd = tmpEnd - PI2
  158.                 End If
  159.                 If tmpEnd < 0 Then
  160.                     tmpEnd = tmpEnd + PI2
  161.                 End If
  162.             
  163.                 Canvas.Circle (boid.X, boid.Y), SensorDist, vbBlack, tmpStart, tmpEnd
  164.                 Canvas.Line (boid.X, boid.Y)-(tmpX1, tmpY1), vbBlack
  165.                 Canvas.Line (boid.X, boid.Y)-(tmpX2, tmpY2), vbBlack
  166.                 
  167.             End If
  168.  
  169.             If ShowCentre Then
  170.                 Canvas.Line (boid.X, boid.Y)-(boid.X + boid.DesireCentreX * 10, boid.Y + boid.DesireCentreY * 10), vbGreen
  171.             End If
  172.             
  173.             If ShowAlign Then
  174.                 Canvas.Line (boid.X, boid.Y)-(boid.X + boid.DesireAlignX * 10, boid.Y + boid.DesireAlignY * 10), vbMagenta
  175.             End If
  176.             
  177.             If ShowSep Then
  178.                 Canvas.Line (boid.X, boid.Y)-(boid.X + boid.DesireSeparateX * 10, boid.Y + boid.DesireSeparateY * 10), vbBlue
  179.             End If
  180.  
  181.  
  182. 'show box used for collision detection
  183.  
  184.             If ShowBox Then
  185.             'box to the right
  186.                 tmpX1 = boid.X + (5 * Cos(boid.direction + PI / 2))
  187.                 tmpY1 = boid.Y + (5 * Sin(boid.direction + PI / 2))
  188.                 tmpX2 = tmpX1 + (SensorDist * Cos(boid.direction))
  189.                 tmpY2 = tmpY1 + (SensorDist * Sin(boid.direction))
  190.     
  191.                 If boid.DesireAvoidRight = False Then
  192.                     Canvas.Line (boid.X, boid.Y)-(tmpX1, tmpY1), vbScrollBars
  193.                     Canvas.Line (tmpX1, tmpY1)-(tmpX2, tmpY2), vbScrollBars
  194.                 Else
  195.                     Canvas.Line (boid.X, boid.Y)-(tmpX1, tmpY1), vbRed
  196.                     Canvas.Line (tmpX1, tmpY1)-(tmpX2, tmpY2), vbRed
  197.                 End If
  198.                 
  199.             'box to the left
  200.                 tmpX3 = boid.X - (5 * Cos(boid.direction + PI / 2))
  201.                 tmpY3 = boid.Y - (5 * Sin(boid.direction + PI / 2))
  202.                 tmpX4 = tmpX3 + (SensorDist * Cos(boid.direction))
  203.                 tmpY4 = tmpY3 + (SensorDist * Sin(boid.direction))
  204.  
  205.                 If boid.DesireAvoidLeft = False Then
  206.                     Canvas.Line (boid.X, boid.Y)-(tmpX3, tmpY3), vbScrollBars
  207.                     Canvas.Line (tmpX3, tmpY3)-(tmpX4, tmpY4), vbScrollBars
  208.                 Else
  209.                     Canvas.Line (boid.X, boid.Y)-(tmpX3, tmpY3), vbRed
  210.                     Canvas.Line (tmpX3, tmpY3)-(tmpX4, tmpY4), vbRed
  211.                 End If
  212.                 
  213.             'complete box
  214.                 Canvas.Line (tmpX2, tmpY2)-(tmpX4, tmpY4), vbScrollBars
  215.             End If
  216.             
  217.     Next
  218.     Set boid = Nothing
  219.  
  220. End Sub
  221.  
  222. Public Sub CalcForces(flock As Collection, CentMult As Integer, SepMult As Integer, AliMult As Integer, SensorDist As Integer, ViewTheta As Single)
  223.     
  224.     Dim distance As Integer
  225.     Dim i%
  226.     Dim AveDir As Single
  227.     Dim AveX As Integer
  228.     Dim AveY As Integer
  229.     Dim AveSpeed As Single
  230.     
  231.     Dim boid As BoidClass
  232.     Dim obs As ObstacleClass
  233.     
  234.     Dim otherBoid As BoidClass
  235.         
  236.     Dim ClosestBoid As BoidClass
  237.     
  238.     Dim iLeaderX As Integer
  239.     Dim iLeaderY As Integer
  240.     
  241.     Dim CloseBoidCount As Integer
  242.     Dim GroupCount As Integer
  243.     'Dim CloseBoidCount As Integer
  244.     
  245.     Dim AllDirChange As Single
  246. '    Dim SensorDist As Integer
  247.     
  248.     Dim ClosestDist As Single
  249.     Dim TmpDist As Single
  250.     Dim TooClose As Boolean
  251.     
  252.     Dim TmpWeight As Double
  253.     
  254.     Dim AngDiff As Single
  255.     Dim Angle As Single
  256.     Dim HalfTheta As Single
  257.     
  258.     Dim blnResult As Boolean
  259.     
  260.     Dim X1 As Integer
  261.     Dim Y1 As Integer
  262.     Dim X2 As Integer
  263.     Dim Y2 As Integer
  264.     
  265.     Dim LeftDist As Single
  266.     Dim RightDist As Single
  267.     
  268. '================================================================================
  269. '================================================================================
  270. '================================================================================
  271.     
  272.     'SensorDist = 50
  273.     
  274.     HalfTheta = ViewTheta / 2
  275.     
  276.     AllDirChange = 0
  277.     For Each boid In flock
  278.  
  279.         ClosestDist = SensorDist * 2
  280.         boid.ClosestDist = 0
  281.         TmpDist = 1000
  282.  
  283.         boid.AveX = boid.X
  284.         boid.AveY = boid.Y
  285.         boid.AveDir = boid.direction
  286.         boid.AveSpeed = boid.speed
  287.         boid.CentreDist = ClosestDist
  288.         boid.DesireAvoidX = 0
  289.         boid.DesireAvoidY = 0
  290.         boid.DesireAvoidWeight = 0
  291.         boid.DesireAvoidRight = False
  292.         boid.DesireAvoidLeft = False
  293.         CloseBoidCount = 1
  294.  
  295.         'Get Average information from flockmates in sensor range
  296.         For Each otherBoid In flock
  297.  
  298.             If boid.id <> otherBoid.id Then 'as long as it's not itself
  299.  
  300.                 distance = Abs(1 + Sqr((boid.X - otherBoid.X) ^ 2 + (boid.Y - otherBoid.Y) ^ 2))
  301.                 
  302.                 If (boid.X - otherBoid.X) <> 0 Then
  303.                     Angle = Abs(Atn((boid.Y - otherBoid.Y) / (boid.X - otherBoid.X)))
  304.                 Else
  305.                     Angle = Abs(Atn(90))
  306.                 End If
  307.                 
  308.                 If boid.X > otherBoid.X Then
  309.                     If boid.Y > otherBoid.Y Then
  310.                         Angle = Angle + PI
  311.                     Else
  312.                         Angle = PI - Angle
  313.                     End If
  314.                 Else
  315.                     If boid.Y > otherBoid.Y Then
  316.                         Angle = (PI2) - Angle
  317.                     Else
  318.                         Angle = Angle
  319.                     End If
  320.                 End If
  321.                 
  322.                 If (distance < SensorDist) Then
  323.                 
  324.                     AngDiff = Abs(boid.direction - Angle)
  325.                     
  326.                     If AngDiff > PI Then
  327.                         AngDiff = AngDiff - PI
  328.                     End If
  329.                     
  330.                     If AngDiff < HalfTheta Then
  331.  
  332.                         'see if it is the closest
  333.                         If distance < TmpDist Then
  334.                             ClosestDist = distance
  335.                             TmpDist = distance
  336.                             Set ClosestBoid = otherBoid
  337.                         End If
  338.     
  339.                         CloseBoidCount = CloseBoidCount + 1
  340.     
  341.                         boid.AveDir = boid.AveDir + otherBoid.direction
  342.                         boid.AveX = boid.AveX + otherBoid.X
  343.                         boid.AveY = boid.AveY + otherBoid.Y
  344.                         boid.AveSpeed = boid.AveSpeed + otherBoid.speed
  345.                         
  346.                     End If
  347.     
  348.                 End If
  349.  
  350.             End If
  351.             TmpDist = SensorDist
  352.         Next
  353.  
  354.         'Debug.Print
  355.  
  356. 'averages
  357.         boid.AveDir = boid.AveDir / CloseBoidCount
  358.         boid.AveX = boid.AveX / CloseBoidCount
  359.         boid.AveY = boid.AveY / CloseBoidCount
  360.         boid.AveSpeed = boid.AveSpeed / CloseBoidCount
  361.  
  362.         If CloseBoidCount > 1 Then
  363. 'align
  364.                 boid.CentreDist = Abs(Sqr((boid.X - boid.AveX) ^ 2 + (boid.Y - boid.AveY) ^ 2))
  365.                 If boid.CentreDist = 0 Then boid.CentreDist = 1
  366.                 boid.DesireAlignTurn = boid.AveDir
  367.                 boid.DesireAlignWeight = 5
  368.                 
  369. 'centre
  370.                 boid.CentreDist = (Sqr((boid.X - boid.AveX) ^ 2 + (boid.Y - boid.AveY) ^ 2))
  371.                 If boid.CentreDist = 0 Then boid.CentreDist = 1
  372.                 
  373.                 If boid.X <> boid.AveX Then
  374.                     boid.DesireCentreTurn = Abs(Atn((boid.Y - boid.AveY) / (boid.X - boid.AveX)))
  375.                 Else
  376.                     boid.DesireCentreTurn = Atn(boid.Y - boid.AveY)
  377.                 End If
  378.                 
  379.                 'boid.DesireCentreWeight = (CSng(boid.CentreDist) * CSng(boid.CentreDist) * CSng(boid.CentreDist)) / 200
  380.                 boid.DesireCentreWeight = Log((CSng(boid.CentreDist) * CSng(boid.CentreDist) * CSng(boid.CentreDist)) / 200) + 5
  381.                 
  382.                 
  383.                 If boid.X > boid.AveX Then
  384.                     If boid.Y > boid.AveY Then
  385.                         boid.DesireCentreTurn = boid.DesireCentreTurn + PI
  386.                     Else
  387.                         boid.DesireCentreTurn = PI - boid.DesireCentreTurn
  388.                     End If
  389.                 Else
  390.                     If boid.Y > boid.AveY Then
  391.                         boid.DesireCentreTurn = (PI2) - boid.DesireCentreTurn
  392.                     Else
  393.                         boid.DesireCentreTurn = boid.DesireCentreTurn
  394.                     End If
  395.                 End If
  396. 'Separate
  397.                 boid.ClosestX = ClosestBoid.X
  398.                 boid.ClosestY = ClosestBoid.Y
  399.                 boid.ClosestDist = Abs(1 + Sqr((boid.X - ClosestBoid.X) ^ 2 + (boid.Y - ClosestBoid.Y) ^ 2))
  400.     
  401.                 If boid.X <> ClosestBoid.X Then
  402.                     boid.DesireSeparateTurn = Abs(Atn((boid.Y - ClosestBoid.Y) / (boid.X - ClosestBoid.X)))
  403.                 Else
  404.                     boid.DesireSeparateTurn = Abs(Atn((boid.Y - ClosestBoid.Y)))
  405.                 End If
  406.     
  407.                 If boid.X > boid.ClosestX Then
  408.                     If boid.Y > boid.ClosestY Then
  409.                         boid.DesireSeparateTurn = boid.DesireSeparateTurn + PI
  410.                     Else
  411.                         boid.DesireSeparateTurn = PI - boid.DesireSeparateTurn
  412.                     End If
  413.                 Else
  414.                     If boid.Y > boid.ClosestY Then
  415.                         boid.DesireSeparateTurn = (PI2) - boid.DesireSeparateTurn
  416.                     Else
  417.                         boid.DesireSeparateTurn = boid.DesireSeparateTurn
  418.                     End If
  419.                 End If
  420.                 boid.DesireSeparateTurn = boid.DesireSeparateTurn + PI
  421.     
  422.                 If boid.DesireSeparateTurn > (PI2) Then boid.DesireSeparateTurn = boid.DesireSeparateTurn - (PI2)
  423.     
  424.                 'boid.DesireSeparateWeight = (2000 / (CSng(boid.ClosestDist) * CSng(boid.ClosestDist) * CSng(boid.ClosestDist)))
  425.                 boid.DesireSeparateWeight = Log((2000 / (CSng(boid.ClosestDist) * CSng(boid.ClosestDist) * CSng(boid.ClosestDist)))) + 10
  426.                 
  427.                                 
  428.             
  429. 'normalise
  430.  
  431.             TmpWeight = Abs(boid.DesireAlignWeight) + Abs(boid.DesireCentreWeight) + Abs(boid.DesireSeparateWeight)
  432.  
  433.             If TmpWeight > 0 Then
  434.  
  435.                 boid.DesireCentreWeight = Abs(boid.DesireCentreWeight / TmpWeight) * CentMult
  436.                 boid.DesireSeparateWeight = Abs(boid.DesireSeparateWeight / TmpWeight) * SepMult
  437.                 boid.DesireAlignWeight = Abs(boid.DesireAlignWeight / TmpWeight) * AliMult
  438.  
  439.  
  440.             End If
  441.  
  442.             boid.DesireCentreX = (boid.DesireCentreWeight * Cos(boid.DesireCentreTurn) * CentMult)
  443.             boid.DesireCentreY = (boid.DesireCentreWeight * Sin(boid.DesireCentreTurn) * CentMult)
  444.  
  445.             boid.DesireSeparateX = (boid.DesireSeparateWeight * Cos(boid.DesireSeparateTurn) * SepMult)
  446.             boid.DesireSeparateY = (boid.DesireSeparateWeight * Sin(boid.DesireSeparateTurn) * SepMult)
  447.  
  448.             boid.DesireAlignX = (boid.DesireAlignWeight * Cos(boid.DesireAlignTurn) * AliMult)
  449.             boid.DesireAlignY = (boid.DesireAlignWeight * Sin(boid.DesireAlignTurn) * AliMult)
  450.  
  451.             
  452.         Else
  453.  
  454.             boid.DesireAlignTurn = 0
  455.             boid.DesireAlignWeight = 0
  456.             boid.DesireAlignX = 0
  457.             boid.DesireAlignY = 0
  458.  
  459.             boid.DesireCentreTurn = 0
  460.             boid.DesireCentreWeight = 0
  461.             boid.DesireCentreX = 0
  462.             boid.DesireCentreY = 0
  463.  
  464.             boid.ClosestX = 0
  465.             boid.ClosestY = 0
  466.             boid.ClosestDist = 0
  467.  
  468.             boid.DesireSeparateTurn = 0
  469.             boid.DesireSeparateWeight = 0
  470.             boid.DesireSeparateX = 0
  471.             boid.DesireSeparateY = 0
  472.  
  473.             boid.ClosestDist = SensorDist * 2
  474.         End If
  475.  
  476. '================================================================================
  477. '================================================================================
  478. '================================================================================
  479.  
  480.  'obstacle avoidance
  481.  
  482.         distance = 0
  483.         TmpDist = 2 * SensorDist
  484.         
  485.         For Each obs In objects
  486.         
  487.             distance = Abs(1 + Sqr((boid.X - obs.X) ^ 2 + (boid.Y - obs.Y) ^ 2))
  488.             
  489.             If distance < obs.Radius Then
  490.                 'Debug.Print boid.id, distance
  491.                 'Stop
  492.             End If
  493.             
  494.             If distance < SensorDist + obs.Radius Then
  495.                 
  496.                 'box to the right
  497.                 X1 = boid.X + (5 * Cos(boid.direction + PI / 2))
  498.                 Y1 = boid.Y + (5 * Sin(boid.direction + PI / 2))
  499.                 X2 = X1 + (SensorDist * Cos(boid.direction))
  500.                 Y2 = Y1 + (SensorDist * Sin(boid.direction))
  501.                 
  502.                 RightDist = SegCirInt(X1, Y1, X2, Y2, obs.X, obs.Y, obs.Radius)
  503.                 'Debug.Print X1, Y1, X2, Y2, obs.X, obs.Y, obs.Radius, blnResult
  504.                 
  505.                 'box to the left
  506.                 X1 = boid.X - (5 * Cos(boid.direction + PI / 2))
  507.                 Y1 = boid.Y - (5 * Sin(boid.direction + PI / 2))
  508.                 X2 = X1 + (SensorDist * Cos(boid.direction))
  509.                 Y2 = Y1 + (SensorDist * Sin(boid.direction))
  510.             
  511.                 LeftDist = SegCirInt(X1, Y1, X2, Y2, obs.X, obs.Y, obs.Radius)
  512.                 'Debug.Print X1, Y1, X2, Y2, obs.X, obs.Y, obs.Radius, blnResult
  513.             
  514.                 If LeftDist <> 0 Or RightDist <> 0 Then
  515.                     
  516.                     If LeftDist <> 0 And RightDist <> 0 Then
  517.                         Select Case LeftDist - RightDist
  518.                         Case Is < 0
  519.                             'Debug.Print LeftDist, RightDist, "TURN RIGHT"
  520.                             'Need to move right
  521.                             boid.DesireAvoidTurn = boid.direction + PI / 4
  522.                             boid.DesireAvoidLeft = True
  523.                             
  524.                             boid.Colour = vbRed
  525.                         Case Is > 0
  526.                             'Debug.Print LeftDist, RightDist, "TURN LEFT"
  527.                             'Need to move left
  528.                             boid.DesireAvoidTurn = boid.direction - PI / 4
  529.                             boid.DesireAvoidRight = True
  530.     
  531.                             boid.Colour = vbBlue
  532.                         Case Else
  533.                             'Debug.Print LeftDist, RightDist, "RANDOM TURN"
  534.                             i% = Int(2 * Rnd)
  535.                             If i% = 0 Then
  536.                                 boid.DesireAvoidTurn = boid.direction - PI / 4
  537.                                 boid.Colour = vbYellow
  538.                             Else
  539.                                 boid.DesireAvoidTurn = boid.direction + PI / 4
  540.                                 boid.Colour = vbRed
  541.                             End If
  542.                             
  543.                             boid.DesireAvoidRight = True
  544.                             boid.DesireAvoidLeft = True
  545.                         End Select
  546.                         
  547.                     Else
  548.                         If LeftDist <> 0 Then
  549.                             boid.DesireAvoidTurn = boid.direction + PI / 4
  550.                             boid.DesireAvoidLeft = True
  551.                             
  552.                             boid.Colour = vbMagenta
  553.                         End If
  554.                         If RightDist <> 0 Then
  555.                             boid.DesireAvoidTurn = boid.direction - PI / 4
  556.                             boid.DesireAvoidRight = True
  557.     
  558.                             boid.Colour = vbCyan
  559.                         End If
  560.                     End If
  561.                     
  562.                 If boid.DesireAvoidTurn > PI2 Then
  563.                     boid.DesireAvoidTurn = boid.DesireAvoidTurn - PI2
  564.                 End If
  565.                 If boid.DesireAvoidTurn < 0 Then
  566.                     boid.DesireAvoidTurn = boid.DesireAvoidTurn + PI2
  567.                 End If
  568.                 
  569.                 boid.DesireAvoidX = 10 * Cos(boid.DesireAvoidTurn)
  570.                 boid.DesireAvoidY = 10 * Sin(boid.DesireAvoidTurn)
  571.                 boid.DesireAvoidWeight = 5
  572.  
  573.                 End If
  574.             End If
  575.         
  576.         Next
  577.     Next
  578.  
  579. '================================================================================
  580. '================================================================================
  581. '================================================================================
  582.  
  583.     Set boid = Nothing
  584.     Set obs = Nothing
  585.     Set ClosestBoid = Nothing
  586.     Set otherBoid = Nothing
  587.     
  588. End Sub
  589.  
  590. Sub MoveBoid(flock As Collection, MaxTurn As Single, iHeight As Integer, iWidth As Integer, SensorDist As Integer, Enclosed As Boolean)
  591.  
  592.     Dim boid As BoidClass
  593.     Dim NewX As Integer
  594.     Dim NewY As Integer
  595.     
  596. '================================================================================
  597. '================================================================================
  598. '================================================================================
  599. Dim NewDir As Single
  600.  Dim Diff As Single
  601.  
  602.     For Each boid In flock
  603.  
  604. 'store starting pos
  605.             boid.Y = boid.Y + (boid.speed * Sin(boid.direction))
  606.             boid.X = boid.X + (boid.speed * Cos(boid.direction))
  607.             
  608. 'select action
  609.             If boid.DesireAvoidWeight = 0 Then
  610.                 Select Case boid.ClosestDist
  611.                 Case 0 To 5
  612.                     boid.DesireSeparateY = boid.DesireSeparateY + Int(Rnd(1) * 10)
  613.                     boid.DesireSeparateX = boid.DesireSeparateX + Int(Rnd(1) * 10)
  614.                 Case 6 To 20
  615.                     boid.DesireAlignWeight = 0
  616.                     boid.DesireCentreWeight = 0
  617.                     boid.DesireSeparateY = boid.DesireSeparateY * 5
  618.                     boid.DesireSeparateX = boid.DesireSeparateX * 5
  619.                 Case Is > SensorDist
  620.     '                Stop
  621.                 Case Else
  622.                 End Select
  623.     
  624.     'start from last finish point
  625.                 NewY = boid.Y
  626.                 NewX = boid.X
  627.     'add forces
  628.                 NewY = NewY + boid.DesireAlignY
  629.                 NewX = NewX + boid.DesireAlignX
  630.                 
  631.                 NewY = NewY + boid.DesireCentreY
  632.                 NewX = NewX + boid.DesireCentreX
  633.                 
  634.                 NewY = NewY + boid.DesireSeparateY
  635.                 NewX = NewX + boid.DesireSeparateX
  636.             Else
  637.             
  638.                 NewY = boid.Y
  639.                 NewX = boid.X
  640.                 
  641.                 NewY = NewY + boid.DesireAvoidY * 10
  642.                 NewX = NewX + boid.DesireAvoidX * 10
  643.               
  644.             End If
  645.  
  646.             If NewX = boid.X And NewY = boid.Y Then
  647.                 'Stop
  648.             Else
  649.             
  650. 'add direction
  651.                 NewY = NewY + (Sin((boid.direction)) * boid.speed)
  652.                 NewX = NewX + (Cos((boid.direction)) * boid.speed)
  653.     
  654. 'get new dir
  655.                 'If boid.Y - NewY = 0 Then NewY = NewY + 1
  656.                 
  657.                 If (boid.X - NewX) <> 0 Then
  658.                     NewDir = Abs(Atn((boid.Y - NewY) / (boid.X - NewX)))
  659.                 Else
  660.                     NewDir = Atn(90)
  661.                 End If
  662.     
  663. 'add static turn
  664.                 If boid.X > NewX Then
  665.                     If boid.Y > NewY Then
  666.                         NewDir = Abs(NewDir) + PI
  667.                     Else
  668.                         NewDir = PI - Abs(NewDir)
  669.                     End If
  670.                 Else
  671.                     If boid.Y > NewY Then
  672.                         NewDir = (PI2) - Abs(NewDir)
  673.                     Else
  674.                         NewDir = Abs(NewDir)
  675.                     End If
  676.                 End If
  677.      
  678. 'check for max allowed turn
  679.                 If boid.direction > NewDir Then
  680.                 
  681.                     If (boid.direction - NewDir) > PI Then
  682.                         Diff = ((PI2) - (boid.direction - NewDir))
  683.                         If Diff > MaxTurn Then
  684.                             Diff = MaxTurn
  685.                         End If
  686.                         boid.direction = boid.direction + Diff
  687.                     Else
  688.                         Diff = (boid.direction - NewDir)
  689.                         If Diff > MaxTurn Then
  690.                             Diff = MaxTurn
  691.                         End If
  692.                         boid.direction = boid.direction - Diff
  693.                     End If
  694.                     
  695.                 Else
  696.                 
  697.                     If (NewDir - boid.direction) > PI Then
  698.                     
  699.                         Diff = ((PI2) - (NewDir - boid.direction))
  700.                         If Diff > MaxTurn Then
  701.                             Diff = MaxTurn
  702.                         End If
  703.                         boid.direction = boid.direction - Diff
  704.                         
  705.                     Else
  706.                         Diff = (NewDir - boid.direction)
  707.                         If Diff > MaxTurn Then
  708.                             Diff = MaxTurn
  709.                         End If
  710.                         boid.direction = boid.direction + Diff
  711.                     End If
  712.                 End If
  713.                 
  714.                 
  715.                 If boid.direction > (PI2) Then
  716.                     boid.direction = boid.direction - (PI2)
  717.                 End If
  718.                 If boid.direction < 0 Then
  719.                     boid.direction = boid.direction + (PI2)
  720.                 End If
  721.                             
  722.             End If
  723.             
  724.             boid.NewY = boid.Y + boid.speed * Sin(boid.direction)
  725.             boid.NewX = boid.X + boid.speed * Cos(boid.direction)
  726.  
  727.  
  728.  
  729.  
  730. '================================================================================
  731. '================================================================================
  732. '================================================================================
  733. '    If boid.CentreDist = 0 Then Stop
  734.  
  735.         Select Case boid.CentreDist
  736.         Case Is < SensorDist
  737.             If Abs(boid.DesireCentreTurn - boid.direction) > PI Then
  738.                 boid.speed = boid.speed - 0.1
  739.                 boid.Colour = vbRed
  740.             Else
  741.                 boid.speed = boid.speed + 0.1
  742.                 boid.Colour = vbGreen
  743.             End If
  744.         Case Is > SensorDist
  745.                 boid.speed = boid.speed + 0.1
  746.                 boid.Colour = vbGreen
  747.         End Select
  748.  
  749. '================================================================================
  750. '================================================================================
  751. '================================================================================
  752.  
  753.         Do
  754.             If boid.direction > (PI2) Then boid.direction = boid.direction - (PI2)
  755.             If boid.direction < 0 Then boid.direction = boid.direction + (PI2)
  756.             
  757.             If boid.speed > 10 Then
  758.                 boid.speed = 10
  759.                 boid.Colour = &HC000&
  760.             End If
  761.             
  762.             If boid.speed < 5 Then
  763.                 boid.speed = 5
  764.                 boid.Colour = &H40C0&
  765.             End If
  766.             
  767.         Loop While (boid.direction > (PI2)) Or (boid.direction < 0)
  768.  
  769. '================================================================================
  770. '================================================================================
  771. '================================================================================
  772.  
  773.         If Enclosed = False Then
  774.         
  775.             If boid.X < 0 Then boid.X = iWidth
  776.             If boid.X > iWidth Then boid.X = 0
  777.             
  778.             If boid.Y < 0 Then boid.Y = iHeight
  779.             If boid.Y > iHeight Then boid.Y = 0
  780.         
  781.         Else
  782.         
  783.             If boid.X > iWidth Then
  784.                 boid.X = iWidth
  785.                 Select Case boid.direction
  786.                 Case 0 To PI / 2
  787.                     boid.direction = PI - boid.direction
  788.                 Case PI + PI / 2 To PI2
  789.                     boid.direction = PI + (PI2 - boid.direction)
  790.                 Case Else
  791.                     'Stop
  792.                 End Select
  793.             End If
  794.                 
  795.             If boid.X < 0 Then
  796.                 boid.X = 0
  797.                 Select Case boid.direction
  798.                 Case PI / 2 To PI
  799.                     boid.direction = PI - boid.direction
  800.                 Case PI To PI + PI / 2
  801.                     boid.direction = PI2 - (boid.direction - PI)
  802.                 Case Else
  803.                     'Stop
  804.                 End Select
  805.             End If
  806.                 
  807.             If boid.Y > iHeight Then
  808.                 boid.Y = iHeight
  809.                 Select Case boid.direction
  810.                 Case PI / 2 To PI
  811.                     boid.direction = PI + (PI - boid.direction)
  812.                 Case 0 To PI
  813.                     boid.direction = PI2 - boid.direction
  814.                 Case Else
  815.                     'Stop
  816.                 End Select
  817.             End If
  818.                 
  819.             If boid.Y < 0 Then
  820.                 boid.Y = 0
  821.                 Select Case boid.direction
  822.                 Case PI To PI + PI / 2
  823.                     boid.direction = PI - (boid.direction - PI)
  824.                 Case PI + PI / 2 To PI2
  825.                     boid.direction = PI2 - boid.direction
  826.                 Case Else
  827.                     'Stop
  828.                 End Select
  829.             End If
  830.         End If
  831.         
  832. '================================================================================
  833. '================================================================================
  834. '================================================================================
  835.  
  836. Next
  837.     
  838. End Sub
  839.  
  840. Sub RandDir(flock As Collection)
  841. Dim boid As BoidClass
  842.  
  843.     For Each boid In flock
  844.         boid.direction = Rnd * 360
  845.     Next
  846.     
  847. End Sub
  848.  
  849. Function SegCirInt(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer, X3 As Integer, Y3 As Integer, r As Integer) As Single
  850. Dim a As Double
  851. Dim b As Double
  852. Dim intClosestPointX As Double
  853. Dim intClosestPointY As Double
  854. Dim cpDist As Single
  855. Dim result As Double
  856. Dim lngX1 As Long
  857. Dim lngX2 As Long
  858. Dim lngX3 As Long
  859. Dim lngY1 As Long
  860. Dim lngY2 As Long
  861. Dim lngY3 As Long
  862.  
  863.  
  864.     lngX1 = X1
  865.     lngX2 = X2
  866.     lngX3 = X3
  867.     lngY1 = Y1
  868.     lngY2 = Y2
  869.     lngY3 = Y3
  870.     
  871. 'check if closest point of line to centre of the object is less than the radius
  872. 'of the object
  873.  
  874.     a = (lngX3 - lngX1) * (lngX2 - lngX1) + (lngY3 - lngY1) * (lngY2 - lngY1)
  875.     b = (lngX2 - lngX1) * (lngX2 - lngX1) + (lngY2 - lngY1) * (lngY2 - lngY1)
  876.     
  877.     result = a / b
  878.     
  879.     If (result > 0) And (result < 1) Then
  880.     'closest point is between the start and end points of the line
  881.     'so now check the distance to the centre of the object
  882.     
  883.         intClosestPointX = X1 + (X2 - X1) * result
  884.         intClosestPointY = Y1 + (Y2 - Y1) * result
  885.         
  886.         cpDist = Sqr(((intClosestPointX - X3) * (intClosestPointX - X3)) + ((intClosestPointY - Y3) * (intClosestPointY - Y3)))
  887.         If cpDist < r Then
  888.             SegCirInt = cpDist
  889.         Else
  890.             SegCirInt = 0
  891.         End If
  892.     Else
  893.     'closest point is outside the start and end points
  894.     'Now check if either end of the line is inside the circle
  895.     
  896.         'check end of line
  897.         cpDist = Sqr((lngX2 - lngX3) * (lngX2 - lngX3) + (lngY2 - lngY3) * (lngY2 - lngY3))
  898.         If cpDist < r Then
  899.             SegCirInt = cpDist
  900.         Else
  901.             'check  start of line
  902.             cpDist = Sqr((lngX1 - lngX3) * (lngX1 - lngX3) + (lngY1 - lngY3) * (lngY1 - lngY3))
  903.             If cpDist < r Then
  904.                 SegCirInt = cpDist
  905.             Else
  906.                 SegCirInt = 0
  907.             End If
  908.         End If
  909.         
  910.     End If
  911.     
  912.     
  913. End Function
  914.  
  915. Function LineCirInt(X1, Y1, X2, Y2, X3, Y3, r) As Boolean
  916. Dim a As Long
  917. Dim b As Long
  918. Dim c As Double
  919. Dim result As Long
  920.  
  921.     a = ((X2 - X1) * (X2 - X1)) + ((Y2 - Y1) * (Y2 - Y1))
  922.     b = 2 * ((X2 - X1) * (X1 - X3) + (Y2 - Y1) * (Y1 - Y3))
  923.     c = (X3 * X3) + (Y3 * Y3) + (X1 * X1) + (Y1 * Y1) - 2 * (X3 * X1 + Y3 * Y1) - (r * r)
  924.     
  925.     result = (b * b) - 4 * a * c
  926.     
  927.     If result > 0 Then
  928.         LineCirInt = True
  929.     Else
  930.         LineCirInt = False
  931.     End If
  932.     
  933.     
  934. End Function
  935.  
  936.  
  937.