EDN Admin
Well-known member
I actually found this amazing code online and I tweaked it a little the way I wanted it to look and function. But now I am having trouble getting the main function to work, which is to get the bubbles to pop. I have created a bubble pop game, in which the bubbles move randomly when the form loads. I want to create an event handler that serves the function of when the mouse left button is clicked, the bubble pops, sound is produced of bubble popping, and the bubble splashes, and then two more bubbles are produced each time a bubble is popped. I was thinking about getting the mouse position first and if that position matched that of the bubble, then the bubble would simply be removed. But I dont know how to do that.
I am doing my best to be as clear as I can. I am a newbee
Here is the code I tweaked a littleartial Public Class Form1
Inherits Form
the control to draw on
Private panel1 As New MyPanel()
Private _rnd As New Random()
Public Sub New()
InitializeComponent()
Init
Me.StartPosition = FormStartPosition.Manual
Me.Location = New Point(12, 12)
Me.ClientSize = New Size(1024, 768)
keep some area for the speed-o-meter gadget on the desktop...
this.ClientSize = new Size(Screen.GetWorkingArea(this).Width - 200, Screen.GetWorkingArea(this).Height);
this.Location = new Point(0, 0);
panel1.Location = New Point(20, 20)
panel1.Size = New Size(Me.ClientSize.Width - 40, Me.ClientSize.Height - 40)
panel1.Anchor = AnchorStyles.Bottom Or AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top
AddHandler panel1.Balls.EnergyStatus, AddressOf _balls_EnergyStatus
Me.Controls.Add(panel1)
AddBalls()
panel1.StartTimer()
End Sub
Private Sub AddBalls()
panel1.Balls.AddOneBall();
panel1.Balls.AddOneBall(New Size(100, 100), Color.Green, Color.Blue, New PointF(200, 200), _rnd.[Next](4))
panel1.Balls.AddOneBall(New Size(100, 100), Color.DarkMagenta, Color.LightPink, New PointF(600, 200), _rnd.[Next](9))
panel1.Balls.AddOneBall(New Size(200, 200), Color.Orchid, Color.Beige, New PointF(200, 200), _rnd.[Next](4))
panel1.Balls.AddOneBall(New Size(150, 150), Color.Green, Color.Blue, New PointF(400, 200), _rnd.[Next](4), _rnd.[Next](9))
panel1.Balls.AddOneBall(New Size(150, 150), Color.DarkGoldenrod, Color.DarkRed, New PointF(400, 400), _rnd.[Next](4), _rnd.[Next](9))
panel1.Balls.AddOneBall(New Size(50, 50), Color.Green, Color.Blue, New PointF(200, 500), _rnd.[Next](4))
panel1.Balls.AddOneBall(new Size(50, 50), Color.DarkGoldenrod, Color.DarkRed, new PointF(400, 500), _rnd.Next(9));
Dim j As Integer = 0
For i As Integer = 0 To 0
panel1.Balls.AddOneBall(New Size(50, 50), Color.Green, Color.Blue, New PointF((i Mod 10) * 100 + 1, (j Mod 10) * 100 + 1), 4)
panel1.Balls.AddOneBall(New Size(50, 50), Color.DarkGoldenrod, Color.DarkRed, New PointF((i Mod 10) * 100 + 1, ((j + 2) Mod 10) * 100 + 1), 7)
If i Mod 10 = 0 Then
j += 1
End If
Next
End Sub
Private Sub _balls_EnergyStatus(sender As Object, e As EnergyEventArgs)
Me.Text = "EnergyOld: " + e.EnergyOld.ToString() + " - EnergyNew: " + e.EnergyNew.ToString()
End Sub
End Class
Public Class MyPanel
Inherits Panel
Private _backGround As Bitmap
Public ReadOnly Property Balls() As BallsList
Get
Return _balls
End Get
End Property
Private _balls As New BallsList()
Private Sub Form1_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles MyClass.MouseDown
If e.X = _balls.location Then
End If
End Sub
Public Property Use3D() As Boolean
Get
Return m_Use3D
End Get
Set(value As Boolean)
m_Use3D = Value
End Set
End Property
Private m_Use3D As Boolean
Public Property RedrawAll() As Boolean
Get
Return m_RedrawAll
End Get
Set(value As Boolean)
m_RedrawAll = Value
End Set
End Property
Private m_RedrawAll As Boolean
Private Delegate Sub RefreshDelegate()
Private _controlThread As System.Threading.Thread = Nothing
Private _running As Boolean
Private _calc As Boolean = True
Private _f As Integer = 0
Private _sleep As Integer = 15
Private _closing As Boolean
Public Sub New()
Me.BackColor = Color.SteelBlue
avoid flicker
Me.DoubleBuffered = True
End Sub
Public Sub SetInterval(milliSecs As Integer)
_sleep = milliSecs
End Sub
Public Sub StartTimer()
If _controlThread IsNot Nothing AndAlso _controlThread.IsAlive Then
_controlThread.Abort()
End If
_controlThread = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf UpdateGraphicsInvoker))
_controlThread.IsBackground = True
_running = True
_controlThread.Start()
End Sub
Public Sub SetBackGround(bmp As Bitmap)
Dim bOld As Bitmap = _backGround
If bmp IsNot Nothing Then
_backGround = DirectCast(bmp.Clone(), Bitmap)
Else
_backGround = Nothing
End If
If bOld IsNot Nothing Then
bOld.Dispose()
End If
End Sub
cleanup
Protected Overrides Sub OnHandleDestroyed(e As EventArgs)
_running = False
_closing = True
If _controlThread IsNot Nothing AndAlso _controlThread.IsAlive Then
_controlThread.Abort()
End If
If _balls IsNot Nothing Then
_balls.Dispose()
End If
If _backGround IsNot Nothing Then
_backGround.Dispose()
End If
End Sub
Private Sub UpdateGraphicsInvoker()
While _running
If _f > 0 Then
System.Threading.Thread.Sleep(Math.Max(_f + 5, _sleep))
Else
System.Threading.Thread.Sleep(_sleep)
End If
Me.Invoke(New RefreshDelegate(AddressOf UpdateUI))
End While
End Sub
Private Sub UpdateUI()
Dim f As Integer = 0
If _calc Then
f = Environment.TickCount
End If
If _balls IsNot Nothing AndAlso _balls.Count > 0 Then
If Not RedrawAll Then
&& (this.ClientSize.Width > 800 || this.ClientSize.Height > 600)
Using gP As System.Drawing.Drawing2D.GraphicsPath = GetPath()
calculate Location for each ball
If _balls IsNot Nothing Then
_balls.IncrementLocationsForAll(Me.ClientSize)
End If
If gP IsNot Nothing AndAlso Not _closing Then
Using gP2 As System.Drawing.Drawing2D.GraphicsPath = GetPath()
gP.AddPath(gP2, False)
End Using
Using reg As New Region(gP)
Me.Invalidate(reg)
End Using
End If
End Using
Else
If _balls IsNot Nothing Then
_balls.IncrementLocationsForAll(Me.ClientSize)
End If
Me.Invalidate()
End If
End If
If _calc Then
Me._f = Environment.TickCount - f
End If
End Sub
Private Function GetPath() As System.Drawing.Drawing2D.GraphicsPath
Dim gP As System.Drawing.Drawing2D.GraphicsPath = Nothing
If _balls IsNot Nothing Then
gP = New System.Drawing.Drawing2D.GraphicsPath()
gP.FillMode = System.Drawing.Drawing2D.FillMode.Winding
For i As Integer = 0 To _balls.Count - 1
Dim r As New RectangleF(_balls(i).Location, _balls(i).Size)
r.Inflate(16, 16)
gP.AddEllipse(r)
Next
End If
Return gP
End Function
Protected Overrides Sub OnPaint(e As PaintEventArgs)
Dim y As Integer = 0
If _backGround IsNot Nothing Then
While y < Me.ClientSize.Height
Dim x As Integer = 0
While x < Me.ClientSize.Width
e.Graphics.DrawImageUnscaled(_backGround, x, y)
x += _backGround.Width
End While
y += _backGround.Height
End While
End If
If _balls IsNot Nothing Then
better smooothing
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
_balls.RenderAll(e.Graphics)
End If
End Sub
End Class
custom eventargs
Public Class EnergyEventArgs
Public Property EnergyOld() As Double
Get
Return m_EnergyOld
End Get
Set(value As Double)
m_EnergyOld = Value
End Set
End Property
Private m_EnergyOld As Double
Public Property EnergyNew() As Double
Get
Return m_EnergyNew
End Get
Set(value As Double)
m_EnergyNew = Value
End Set
End Property
Private m_EnergyNew As Double
End Class
Public Class BallsList
Implements IDisposable
the List of balls
Private _balls As New List(Of Ball)()
Private _i As Integer
Private _dOLd As Double
Public Property BitmapMode() As Boolean
Get
Return m_BitmapMode
End Get
Set(value As Boolean)
m_BitmapMode = Value
End Set
End Property
Private m_BitmapMode As Boolean
Public Property Bmp() As Bitmap
Get
Return m_Bmp
End Get
Set(value As Bitmap)
m_Bmp = Value
End Set
End Property
Private m_Bmp As Bitmap
Property location As Single
event
Public Delegate Sub EnergyEventHandler(sender As Object, e As EnergyEventArgs)
Public Event EnergyStatus As EnergyEventHandler
Default Public Property Item(index As Integer) As Ball
null-check usually needed
Get
Return _balls(index)
End Get
Set(value As Ball)
_balls(index) = value
End Set
End Property
Public ReadOnly Property Count() As Integer
Get
If _balls IsNot Nothing AndAlso _balls.Count > -1 Then
Return _balls.Count
Else
If _balls Is Nothing Then
_balls = New List(Of Ball)()
End If
Return -1
End If
End Get
End Property
Public Sub AddOneBall(size As System.Drawing.Size, color As Color, color_2 As Color, pt As PointF, vel As Single)
Dim b As New GradientBall(size, color, color_2, vel, pt)
b.RotationAdd = 1
Me._balls.Add(b)
End Sub
Public Sub AddOneBall(size As System.Drawing.Size, color As Color, color_2 As Color, pt As PointF, velX As Single, velY As Single)
Dim b As New GradientBall(size, color, color_2, velX, velY, pt)
b.RotationAdd = 1
Me._balls.Add(b)
End Sub
Public Sub AddOneBall()
Dim b As New GradientBall()
Me._balls.Add(b)
End Sub
Public Sub Remove(ball As Ball)
If Me._balls IsNot Nothing AndAlso Me._balls.Contains(ball) Then
Me._balls.Remove(ball)
ball.Dispose()
End If
End Sub
Public Sub Clear()
If Me._balls IsNot Nothing AndAlso Me._balls.Count > 0 Then
Dim i As Integer = Me._balls.Count - 1
While i >= 0
Dim ball As Ball = Me._balls(i)
Remove(ball)
i += -1
End While
Me._balls.Clear()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Bmp IsNot Nothing Then
Bmp.Dispose()
End If
If _balls IsNot Nothing Then
For i As Integer = _balls.Count - 1 To 0 Step -1
_balls(i).Dispose()
Next
_balls.Clear()
End If
End Sub
Public Sub CheckCollisions(i As Integer)
we only need one loop here, since the "outer loop" is processed in the calling method
For j As Integer = i + 1 To _balls.Count - 1
Try
if we have a collision and no lastcollison of the same balls, compute the new values
If _balls(i).Collides(_balls(j)) Then
Dim b1 As Ball = _balls(i)
Dim b2 As Ball = _balls(j)
center points
Dim c1 As New PointF(b1.Location.X + b1.Size.Width / 2.0F, b1.Location.Y + b1.Size.Height / 2.0F)
Dim c2 As New PointF(b2.Location.X + b2.Size.Width / 2.0F, b2.Location.Y + b2.Size.Height / 2.0F)
b1
Math.Cos(theta) = ball-vel.v * normvectorcenter / |ball-vel.v| * |normvectorcenter|
1st norm the centervektor
2nd getAngle between velVektor and normCenterVektor
3d get resulting CenterVector by multiplying the normCenterVector with the (projection of the) velVector and the Cosine of the angle
Dim vX As Double = c2.X - c1.X
Dim vY As Double = c2.Y - c1.Y
Dim velAmount As Double = Math.Sqrt(b1.IncrementX * b1.IncrementX + b1.IncrementY * b1.IncrementY)
Dim centerAmount As Double = Math.Sqrt(vX * vX + vY * vY)
Dim centerNormX As Double = 0.0
Dim centerNormY As Double = 0.0
If centerAmount <> 0 Then
centerNormX = vX / centerAmount
centerNormY = vY / centerAmount
End If
Dim spX As Double = b1.IncrementX * centerNormX + b1.IncrementY * centerNormY
Dim thetaCos As Double = 0.0
If velAmount <> 0 Then
thetaCos = spX / velAmount
End If
Dim centerNormToVel As Double = velAmount * thetaCos
Dim xC As Double = centerNormX * centerNormToVel
Dim yC As Double = centerNormY * centerNormToVel
Dim xT As Double = b1.IncrementX - xC
Dim yT As Double = b1.IncrementY - yC
b2
Dim vX2 As Double = c1.X - c2.X
Dim vY2 As Double = c1.Y - c2.Y
Dim velAmount2 As Double = Math.Sqrt(b2.IncrementX * b2.IncrementX + b2.IncrementY * b2.IncrementY)
Dim centerAmount2 As Double = Math.Sqrt(vX2 * vX2 + vY2 * vY2)
Dim centerNormX2 As Double = 0.0
Dim centerNormY2 As Double = 0.0
If centerAmount2 <> 0 Then
centerNormX2 = vX2 / centerAmount2
centerNormY2 = vY2 / centerAmount2
End If
Dim spX2 As Double = b2.IncrementX * centerNormX2 + b2.IncrementY * centerNormY2
Dim thetaCos2 As Double = 0.0
If velAmount2 <> 0 Then
thetaCos2 = spX2 / velAmount2
End If
Dim centerNormToVel2 As Double = velAmount2 * thetaCos2
Dim xC2 As Double = centerNormX2 * centerNormToVel2
Dim yC2 As Double = centerNormY2 * centerNormToVel2
Dim xT2 As Double = b2.IncrementX - xC2
Dim yT2 As Double = b2.IncrementY - yC2
Determine, if this ball was in a previous collision and still intersects the other balls region
We assume that in a collision the centers of the balls are moving towards each other, if so a collision is present,
if not, the balls in a previous collision state.
Dim prevColl As Boolean = False
Dim distCenterX As Double = (c2.X + xC2) - (c1.X + xC)
Dim distCenterY As Double = (c2.Y + yC2) - (c1.Y + yC)
Dim distCenter As Double = Math.Sqrt(distCenterX * distCenterX + distCenterY * distCenterY)
Dim distCenterX2 As Double = (c2.X - xC2) - (c1.X - xC)
Dim distCenterY2 As Double = (c2.Y - yC2) - (c1.Y - yC)
Dim distCenter2 As Double = Math.Sqrt(distCenterX2 * distCenterX2 + distCenterY2 * distCenterY2)
If distCenter > distCenter2 Then
prevColl = True
End If
If Not prevColl Then
I used the math from http://web.physik.rwth-aachen.de/~fluegge/Vorlesung/PhysIpub/Exscript/4Kapitel/IV6Kapitel.html
because this computes values with directions (complete vectors) - so you do not have to do any contorted maneuvers to
get the direction when only a numeric amount for velocity is calculated. You also do nnot need to do trigonometric
operations which could slow down performance when done intensivly.
Dim v1resX As Double = ((b1.Mass - b2.Mass) / (b1.Mass + b2.Mass) * xC) + (((2 * b2.Mass) / (b1.Mass + b2.Mass)) * xC2)
Dim v1resY As Double = ((b1.Mass - b2.Mass) / (b1.Mass + b2.Mass) * yC) + (((2 * b2.Mass) / (b1.Mass + b2.Mass)) * yC2)
Dim v2resX As Double = ((b2.Mass - b1.Mass) / (b1.Mass + b2.Mass) * xC2) + (2 * b1.Mass / (b1.Mass + b2.Mass) * xC)
Dim v2resY As Double = ((b2.Mass - b1.Mass) / (b1.Mass + b2.Mass) * yC2) + (2 * b1.Mass / (b1.Mass + b2.Mass) * yC)
add central and tangential vectors
b1.IncrementX = CSng(v1resX + xT)
b1.IncrementY = CSng(yT + v1resY)
b2.IncrementX = CSng(xT2 + v2resX)
b2.IncrementY = CSng(v2resY + yT2)
If _i >= 10 Then
Dim d As Double = GetOverallEnrg()
OnUpdateText(_dOLd, d)
_dOLd = d
_i = 0
End If
_i += 1
b1.RotationAdd *= -1
b2.RotationAdd *= -1
End If
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
Next
End Sub
raiseevent
Protected Overridable Sub OnUpdateText(enrgOld As Double, enrg As Double)
RaiseEvent EnergyStatus(Me, New EnergyEventArgs() With { _
.EnergyOld = enrgOld, _
.EnergyNew = enrg _
})
End Sub
Private Function GetOverallEnrg() As Double
Dim res As Double = 0
If _balls IsNot Nothing AndAlso _balls.Count > 0 Then
For i As Integer = 0 To _balls.Count - 1
res += _balls(i).Mass + ((_balls(i).IncrementX * _balls(i).IncrementX + _balls(i).IncrementY * _balls(i).IncrementY) / 2.0)
Next
End If
Return res
End Function
Public Sub IncrementLocationsForAll(clientSize As SizeF)
For i As Integer = 0 To _balls.Count - 1
CheckCollisions(i)
Dim x As Single = _balls(i).Location.X + Me._balls(i).IncrementX
Dim y As Single = _balls(i).Location.Y + Me._balls(i).IncrementY
_balls(i).Location = New PointF(x, y)
reverse direction needed?
If x >= clientSize.Width - _balls(i).Size.Width Then
If Me._balls(i).IncrementX > 0 Then
Me._balls(i).IncrementX *= -1
End If
End If
If x <= 0 Then
If Me._balls(i).IncrementX < 0 Then
Me._balls(i).IncrementX *= -1
End If
End If
If y >= clientSize.Height - _balls(i).Size.Height Then
If Me._balls(i).IncrementY > 0 Then
Me._balls(i).IncrementY *= -1
End If
End If
If y <= 0 Then
If Me._balls(i).IncrementY < 0 Then
Me._balls(i).IncrementY *= -1
End If
End If
Next
End Sub
Public Sub RenderAll(g As Graphics)
draw each ball
For i As Integer = 0 To _balls.Count - 1
_balls(i).Render(g)
Next
End Sub
End Class
Public MustInherit Class Ball
Implements IDisposable
Public Property Location() As PointF
Get
Return m_Location
End Get
Set(value As PointF)
m_Location = Value
End Set
End Property
Private m_Location As PointF
we only use balls, so a single float would be enough for the diameter or radius...
Public Property Size() As SizeF
Get
Return m_Size
End Get
Set(value As SizeF)
m_Size = Value
End Set
End Property
Private m_Size As SizeF
Public Property IncrementX() As Single
Get
Return m_IncrementX
End Get
Set(value As Single)
m_IncrementX = Value
End Set
End Property
Private m_IncrementX As Single
Public Property IncrementY() As Single
Get
Return m_IncrementY
End Get
Set(value As Single)
m_IncrementY = Value
End Set
End Property
Private m_IncrementY As Single
Public Property RotationAdd() As Single
Get
Return m_RotationAdd
End Get
Set(value As Single)
m_RotationAdd = Value
End Set
End Property
Private m_RotationAdd As Single
current rotation angle
Protected Property R() As Single
Get
Return m_R
End Get
Set(value As Single)
m_R = Value
End Set
End Property
Private m_R As Single
Public Sub ComputeMass()
since we use balls, we calculate a comparable value - her - the volume
Mass = 4.0 / 3.0 * Math.PI * Math.Pow(Size.Width / 2.0, 3.0)
End Sub
Public MustOverride Sub Render(g As Graphics)
Public Function Collides(ball As Ball) As Boolean
we are using a Region to determine collisions
we first set up two GraphicsPaths with the ellipses and create a Region from one
then we intersect the Region with the second GraphicsPath and look, if
we have intersections by simnply checking the amount of RegionScans (RectanlgeFs) in the result.
Using gP1 As New System.Drawing.Drawing2D.GraphicsPath()
gP1.AddEllipse(New RectangleF(Me.Location, Me.Size))
Using gP2 As New System.Drawing.Drawing2D.GraphicsPath()
gP2.AddEllipse(New RectangleF(ball.Location, ball.Size))
Using reg As New Region(gP1)
reg.Intersect(gP2)
If reg.GetRegionScans(New System.Drawing.Drawing2D.Matrix(1, 0, 0, 1, 0, 0)).Length > 0 Then
Return True
End If
End Using
End Using
End Using
Return False
End Function
Protected Function GetBrush() As System.Drawing.Drawing2D.PathGradientBrush
Dim p As System.Drawing.Drawing2D.PathGradientBrush = Nothing
Using gP As New System.Drawing.Drawing2D.GraphicsPath()
gP.AddEllipse(New RectangleF(New PointF(0, 0), Me.Size))
p = New System.Drawing.Drawing2D.PathGradientBrush(gP)
p.CenterColor = Color.FromArgb(147, 255, 255, 255)
Dim w As Single = Me.Size.Width / 4.0F
Dim h As Single = Me.Size.Height / 4.0F
p.CenterPoint = New PointF(w, h)
p.SurroundColors = New Color() {Color.Transparent}
End Using
Return p
End Function
Protected Function GetBrush(alpha As Integer) As System.Drawing.Drawing2D.PathGradientBrush
Dim p As System.Drawing.Drawing2D.PathGradientBrush = Nothing
Using gP As New System.Drawing.Drawing2D.GraphicsPath()
gP.AddEllipse(New RectangleF(New PointF(0, 0), Me.Size))
p = New System.Drawing.Drawing2D.PathGradientBrush(gP)
p.CenterColor = Color.FromArgb(147, 255, 255, 255)
Dim w As Single = Me.Size.Width / 4.0F
Dim h As Single = Me.Size.Height / 4.0F
p.CenterPoint = New PointF(w, h)
p.SurroundColors = New Color() {Color.FromArgb(alpha, 0, 0, 0)}
End Using
Return p
End Function
Public Property Mass() As Double
Get
Return m_Mass
End Get
Set(value As Double)
m_Mass = Value
End Set
End Property
Private m_Mass As Double
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Public Class GradientBall
Inherits Ball
Implements IDisposable
Private brush2 As System.Drawing.Drawing2D.PathGradientBrush
Private brush As System.Drawing.Drawing2D.LinearGradientBrush
Public Sub New()
Me.IncrementX = InlineAssignHelper(Me.IncrementY, 1)
Me.RotationAdd = 2
Me.Size = New SizeF(100, 100)
Me.brush = New System.Drawing.Drawing2D.LinearGradientBrush(New RectangleF(0, 0, Size.Width, Size.Height), Color.DarkRed, Color.Yellow, 0.0F)
Me.brush.WrapMode = System.Drawing.Drawing2D.WrapMode.TileFlipXY
Me.brush2 = GetBrush()
ComputeMass()
End Sub
Public Sub New(size__1 As SizeF, c1 As Color, c2 As Color, increment As Single, startLocation As PointF)
Me.IncrementX = InlineAssignHelper(Me.IncrementY, increment)
Me.RotationAdd = 2
Me.Size = size__1
Me.brush = New System.Drawing.Drawing2D.LinearGradientBrush(New RectangleF(0, 0, Size.Width, Size.Height), c1, c2, 0.0F)
Me.brush.WrapMode = System.Drawing.Drawing2D.WrapMode.TileFlipXY
Me.brush2 = GetBrush()
Me.Location = startLocation
ComputeMass()
End Sub
Public Sub New(size__1 As SizeF, c1 As Color, c2 As Color, incrementX As Single, incrementY As Single, startLocation As PointF)
Me.IncrementX = incrementX
Me.IncrementY = incrementY
Me.RotationAdd = 2
Me.Size = size__1
Me.brush = New System.Drawing.Drawing2D.LinearGradientBrush(New RectangleF(0, 0, Size.Width, Size.Height), c1, c2, 0.0F)
Me.brush.WrapMode = System.Drawing.Drawing2D.WrapMode.TileFlipXY
Me.brush2 = GetBrush()
Me.Location = startLocation
ComputeMass()
End Sub
Public Overrides Sub Dispose()
cleanup
If brush IsNot Nothing Then
brush.Dispose()
End If
If brush2 IsNot Nothing Then
brush2.Dispose()
End If
MyBase.Dispose()
End Sub
Public Overrides Sub Render(g As Graphics)
rotate
R += RotationAdd
translate to center, rotate and translate back
brush.TranslateTransform(-brush.Rectangle.Width / 2.0F, -brush.Rectangle.Height / 2.0F)
brush.RotateTransform(R, System.Drawing.Drawing2D.MatrixOrder.Append)
brush.TranslateTransform(brush.Rectangle.Width / 2.0F, brush.Rectangle.Height / 2.0F, System.Drawing.Drawing2D.MatrixOrder.Append)
translate graphics object and draw at *(0, 0)* !
g.TranslateTransform(Location.X, Location.Y)
g.FillEllipse(brush, New RectangleF(New Point(0, 0), Size))
g.FillEllipse(brush2, New RectangleF(New Point(0, 0), Size))
reset the transforms
brush.ResetTransform()
g.ResetTransform()
End Sub
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Class
View the full article
I am doing my best to be as clear as I can. I am a newbee
Here is the code I tweaked a littleartial Public Class Form1
Inherits Form
the control to draw on
Private panel1 As New MyPanel()
Private _rnd As New Random()
Public Sub New()
InitializeComponent()
Init
Me.StartPosition = FormStartPosition.Manual
Me.Location = New Point(12, 12)
Me.ClientSize = New Size(1024, 768)
keep some area for the speed-o-meter gadget on the desktop...
this.ClientSize = new Size(Screen.GetWorkingArea(this).Width - 200, Screen.GetWorkingArea(this).Height);
this.Location = new Point(0, 0);
panel1.Location = New Point(20, 20)
panel1.Size = New Size(Me.ClientSize.Width - 40, Me.ClientSize.Height - 40)
panel1.Anchor = AnchorStyles.Bottom Or AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top
AddHandler panel1.Balls.EnergyStatus, AddressOf _balls_EnergyStatus
Me.Controls.Add(panel1)
AddBalls()
panel1.StartTimer()
End Sub
Private Sub AddBalls()
panel1.Balls.AddOneBall();
panel1.Balls.AddOneBall(New Size(100, 100), Color.Green, Color.Blue, New PointF(200, 200), _rnd.[Next](4))
panel1.Balls.AddOneBall(New Size(100, 100), Color.DarkMagenta, Color.LightPink, New PointF(600, 200), _rnd.[Next](9))
panel1.Balls.AddOneBall(New Size(200, 200), Color.Orchid, Color.Beige, New PointF(200, 200), _rnd.[Next](4))
panel1.Balls.AddOneBall(New Size(150, 150), Color.Green, Color.Blue, New PointF(400, 200), _rnd.[Next](4), _rnd.[Next](9))
panel1.Balls.AddOneBall(New Size(150, 150), Color.DarkGoldenrod, Color.DarkRed, New PointF(400, 400), _rnd.[Next](4), _rnd.[Next](9))
panel1.Balls.AddOneBall(New Size(50, 50), Color.Green, Color.Blue, New PointF(200, 500), _rnd.[Next](4))
panel1.Balls.AddOneBall(new Size(50, 50), Color.DarkGoldenrod, Color.DarkRed, new PointF(400, 500), _rnd.Next(9));
Dim j As Integer = 0
For i As Integer = 0 To 0
panel1.Balls.AddOneBall(New Size(50, 50), Color.Green, Color.Blue, New PointF((i Mod 10) * 100 + 1, (j Mod 10) * 100 + 1), 4)
panel1.Balls.AddOneBall(New Size(50, 50), Color.DarkGoldenrod, Color.DarkRed, New PointF((i Mod 10) * 100 + 1, ((j + 2) Mod 10) * 100 + 1), 7)
If i Mod 10 = 0 Then
j += 1
End If
Next
End Sub
Private Sub _balls_EnergyStatus(sender As Object, e As EnergyEventArgs)
Me.Text = "EnergyOld: " + e.EnergyOld.ToString() + " - EnergyNew: " + e.EnergyNew.ToString()
End Sub
End Class
Public Class MyPanel
Inherits Panel
Private _backGround As Bitmap
Public ReadOnly Property Balls() As BallsList
Get
Return _balls
End Get
End Property
Private _balls As New BallsList()
Private Sub Form1_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles MyClass.MouseDown
If e.X = _balls.location Then
End If
End Sub
Public Property Use3D() As Boolean
Get
Return m_Use3D
End Get
Set(value As Boolean)
m_Use3D = Value
End Set
End Property
Private m_Use3D As Boolean
Public Property RedrawAll() As Boolean
Get
Return m_RedrawAll
End Get
Set(value As Boolean)
m_RedrawAll = Value
End Set
End Property
Private m_RedrawAll As Boolean
Private Delegate Sub RefreshDelegate()
Private _controlThread As System.Threading.Thread = Nothing
Private _running As Boolean
Private _calc As Boolean = True
Private _f As Integer = 0
Private _sleep As Integer = 15
Private _closing As Boolean
Public Sub New()
Me.BackColor = Color.SteelBlue
avoid flicker
Me.DoubleBuffered = True
End Sub
Public Sub SetInterval(milliSecs As Integer)
_sleep = milliSecs
End Sub
Public Sub StartTimer()
If _controlThread IsNot Nothing AndAlso _controlThread.IsAlive Then
_controlThread.Abort()
End If
_controlThread = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf UpdateGraphicsInvoker))
_controlThread.IsBackground = True
_running = True
_controlThread.Start()
End Sub
Public Sub SetBackGround(bmp As Bitmap)
Dim bOld As Bitmap = _backGround
If bmp IsNot Nothing Then
_backGround = DirectCast(bmp.Clone(), Bitmap)
Else
_backGround = Nothing
End If
If bOld IsNot Nothing Then
bOld.Dispose()
End If
End Sub
cleanup
Protected Overrides Sub OnHandleDestroyed(e As EventArgs)
_running = False
_closing = True
If _controlThread IsNot Nothing AndAlso _controlThread.IsAlive Then
_controlThread.Abort()
End If
If _balls IsNot Nothing Then
_balls.Dispose()
End If
If _backGround IsNot Nothing Then
_backGround.Dispose()
End If
End Sub
Private Sub UpdateGraphicsInvoker()
While _running
If _f > 0 Then
System.Threading.Thread.Sleep(Math.Max(_f + 5, _sleep))
Else
System.Threading.Thread.Sleep(_sleep)
End If
Me.Invoke(New RefreshDelegate(AddressOf UpdateUI))
End While
End Sub
Private Sub UpdateUI()
Dim f As Integer = 0
If _calc Then
f = Environment.TickCount
End If
If _balls IsNot Nothing AndAlso _balls.Count > 0 Then
If Not RedrawAll Then
&& (this.ClientSize.Width > 800 || this.ClientSize.Height > 600)
Using gP As System.Drawing.Drawing2D.GraphicsPath = GetPath()
calculate Location for each ball
If _balls IsNot Nothing Then
_balls.IncrementLocationsForAll(Me.ClientSize)
End If
If gP IsNot Nothing AndAlso Not _closing Then
Using gP2 As System.Drawing.Drawing2D.GraphicsPath = GetPath()
gP.AddPath(gP2, False)
End Using
Using reg As New Region(gP)
Me.Invalidate(reg)
End Using
End If
End Using
Else
If _balls IsNot Nothing Then
_balls.IncrementLocationsForAll(Me.ClientSize)
End If
Me.Invalidate()
End If
End If
If _calc Then
Me._f = Environment.TickCount - f
End If
End Sub
Private Function GetPath() As System.Drawing.Drawing2D.GraphicsPath
Dim gP As System.Drawing.Drawing2D.GraphicsPath = Nothing
If _balls IsNot Nothing Then
gP = New System.Drawing.Drawing2D.GraphicsPath()
gP.FillMode = System.Drawing.Drawing2D.FillMode.Winding
For i As Integer = 0 To _balls.Count - 1
Dim r As New RectangleF(_balls(i).Location, _balls(i).Size)
r.Inflate(16, 16)
gP.AddEllipse(r)
Next
End If
Return gP
End Function
Protected Overrides Sub OnPaint(e As PaintEventArgs)
Dim y As Integer = 0
If _backGround IsNot Nothing Then
While y < Me.ClientSize.Height
Dim x As Integer = 0
While x < Me.ClientSize.Width
e.Graphics.DrawImageUnscaled(_backGround, x, y)
x += _backGround.Width
End While
y += _backGround.Height
End While
End If
If _balls IsNot Nothing Then
better smooothing
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
_balls.RenderAll(e.Graphics)
End If
End Sub
End Class
custom eventargs
Public Class EnergyEventArgs
Public Property EnergyOld() As Double
Get
Return m_EnergyOld
End Get
Set(value As Double)
m_EnergyOld = Value
End Set
End Property
Private m_EnergyOld As Double
Public Property EnergyNew() As Double
Get
Return m_EnergyNew
End Get
Set(value As Double)
m_EnergyNew = Value
End Set
End Property
Private m_EnergyNew As Double
End Class
Public Class BallsList
Implements IDisposable
the List of balls
Private _balls As New List(Of Ball)()
Private _i As Integer
Private _dOLd As Double
Public Property BitmapMode() As Boolean
Get
Return m_BitmapMode
End Get
Set(value As Boolean)
m_BitmapMode = Value
End Set
End Property
Private m_BitmapMode As Boolean
Public Property Bmp() As Bitmap
Get
Return m_Bmp
End Get
Set(value As Bitmap)
m_Bmp = Value
End Set
End Property
Private m_Bmp As Bitmap
Property location As Single
event
Public Delegate Sub EnergyEventHandler(sender As Object, e As EnergyEventArgs)
Public Event EnergyStatus As EnergyEventHandler
Default Public Property Item(index As Integer) As Ball
null-check usually needed
Get
Return _balls(index)
End Get
Set(value As Ball)
_balls(index) = value
End Set
End Property
Public ReadOnly Property Count() As Integer
Get
If _balls IsNot Nothing AndAlso _balls.Count > -1 Then
Return _balls.Count
Else
If _balls Is Nothing Then
_balls = New List(Of Ball)()
End If
Return -1
End If
End Get
End Property
Public Sub AddOneBall(size As System.Drawing.Size, color As Color, color_2 As Color, pt As PointF, vel As Single)
Dim b As New GradientBall(size, color, color_2, vel, pt)
b.RotationAdd = 1
Me._balls.Add(b)
End Sub
Public Sub AddOneBall(size As System.Drawing.Size, color As Color, color_2 As Color, pt As PointF, velX As Single, velY As Single)
Dim b As New GradientBall(size, color, color_2, velX, velY, pt)
b.RotationAdd = 1
Me._balls.Add(b)
End Sub
Public Sub AddOneBall()
Dim b As New GradientBall()
Me._balls.Add(b)
End Sub
Public Sub Remove(ball As Ball)
If Me._balls IsNot Nothing AndAlso Me._balls.Contains(ball) Then
Me._balls.Remove(ball)
ball.Dispose()
End If
End Sub
Public Sub Clear()
If Me._balls IsNot Nothing AndAlso Me._balls.Count > 0 Then
Dim i As Integer = Me._balls.Count - 1
While i >= 0
Dim ball As Ball = Me._balls(i)
Remove(ball)
i += -1
End While
Me._balls.Clear()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Bmp IsNot Nothing Then
Bmp.Dispose()
End If
If _balls IsNot Nothing Then
For i As Integer = _balls.Count - 1 To 0 Step -1
_balls(i).Dispose()
Next
_balls.Clear()
End If
End Sub
Public Sub CheckCollisions(i As Integer)
we only need one loop here, since the "outer loop" is processed in the calling method
For j As Integer = i + 1 To _balls.Count - 1
Try
if we have a collision and no lastcollison of the same balls, compute the new values
If _balls(i).Collides(_balls(j)) Then
Dim b1 As Ball = _balls(i)
Dim b2 As Ball = _balls(j)
center points
Dim c1 As New PointF(b1.Location.X + b1.Size.Width / 2.0F, b1.Location.Y + b1.Size.Height / 2.0F)
Dim c2 As New PointF(b2.Location.X + b2.Size.Width / 2.0F, b2.Location.Y + b2.Size.Height / 2.0F)
b1
Math.Cos(theta) = ball-vel.v * normvectorcenter / |ball-vel.v| * |normvectorcenter|
1st norm the centervektor
2nd getAngle between velVektor and normCenterVektor
3d get resulting CenterVector by multiplying the normCenterVector with the (projection of the) velVector and the Cosine of the angle
Dim vX As Double = c2.X - c1.X
Dim vY As Double = c2.Y - c1.Y
Dim velAmount As Double = Math.Sqrt(b1.IncrementX * b1.IncrementX + b1.IncrementY * b1.IncrementY)
Dim centerAmount As Double = Math.Sqrt(vX * vX + vY * vY)
Dim centerNormX As Double = 0.0
Dim centerNormY As Double = 0.0
If centerAmount <> 0 Then
centerNormX = vX / centerAmount
centerNormY = vY / centerAmount
End If
Dim spX As Double = b1.IncrementX * centerNormX + b1.IncrementY * centerNormY
Dim thetaCos As Double = 0.0
If velAmount <> 0 Then
thetaCos = spX / velAmount
End If
Dim centerNormToVel As Double = velAmount * thetaCos
Dim xC As Double = centerNormX * centerNormToVel
Dim yC As Double = centerNormY * centerNormToVel
Dim xT As Double = b1.IncrementX - xC
Dim yT As Double = b1.IncrementY - yC
b2
Dim vX2 As Double = c1.X - c2.X
Dim vY2 As Double = c1.Y - c2.Y
Dim velAmount2 As Double = Math.Sqrt(b2.IncrementX * b2.IncrementX + b2.IncrementY * b2.IncrementY)
Dim centerAmount2 As Double = Math.Sqrt(vX2 * vX2 + vY2 * vY2)
Dim centerNormX2 As Double = 0.0
Dim centerNormY2 As Double = 0.0
If centerAmount2 <> 0 Then
centerNormX2 = vX2 / centerAmount2
centerNormY2 = vY2 / centerAmount2
End If
Dim spX2 As Double = b2.IncrementX * centerNormX2 + b2.IncrementY * centerNormY2
Dim thetaCos2 As Double = 0.0
If velAmount2 <> 0 Then
thetaCos2 = spX2 / velAmount2
End If
Dim centerNormToVel2 As Double = velAmount2 * thetaCos2
Dim xC2 As Double = centerNormX2 * centerNormToVel2
Dim yC2 As Double = centerNormY2 * centerNormToVel2
Dim xT2 As Double = b2.IncrementX - xC2
Dim yT2 As Double = b2.IncrementY - yC2
Determine, if this ball was in a previous collision and still intersects the other balls region
We assume that in a collision the centers of the balls are moving towards each other, if so a collision is present,
if not, the balls in a previous collision state.
Dim prevColl As Boolean = False
Dim distCenterX As Double = (c2.X + xC2) - (c1.X + xC)
Dim distCenterY As Double = (c2.Y + yC2) - (c1.Y + yC)
Dim distCenter As Double = Math.Sqrt(distCenterX * distCenterX + distCenterY * distCenterY)
Dim distCenterX2 As Double = (c2.X - xC2) - (c1.X - xC)
Dim distCenterY2 As Double = (c2.Y - yC2) - (c1.Y - yC)
Dim distCenter2 As Double = Math.Sqrt(distCenterX2 * distCenterX2 + distCenterY2 * distCenterY2)
If distCenter > distCenter2 Then
prevColl = True
End If
If Not prevColl Then
I used the math from http://web.physik.rwth-aachen.de/~fluegge/Vorlesung/PhysIpub/Exscript/4Kapitel/IV6Kapitel.html
because this computes values with directions (complete vectors) - so you do not have to do any contorted maneuvers to
get the direction when only a numeric amount for velocity is calculated. You also do nnot need to do trigonometric
operations which could slow down performance when done intensivly.
Dim v1resX As Double = ((b1.Mass - b2.Mass) / (b1.Mass + b2.Mass) * xC) + (((2 * b2.Mass) / (b1.Mass + b2.Mass)) * xC2)
Dim v1resY As Double = ((b1.Mass - b2.Mass) / (b1.Mass + b2.Mass) * yC) + (((2 * b2.Mass) / (b1.Mass + b2.Mass)) * yC2)
Dim v2resX As Double = ((b2.Mass - b1.Mass) / (b1.Mass + b2.Mass) * xC2) + (2 * b1.Mass / (b1.Mass + b2.Mass) * xC)
Dim v2resY As Double = ((b2.Mass - b1.Mass) / (b1.Mass + b2.Mass) * yC2) + (2 * b1.Mass / (b1.Mass + b2.Mass) * yC)
add central and tangential vectors
b1.IncrementX = CSng(v1resX + xT)
b1.IncrementY = CSng(yT + v1resY)
b2.IncrementX = CSng(xT2 + v2resX)
b2.IncrementY = CSng(v2resY + yT2)
If _i >= 10 Then
Dim d As Double = GetOverallEnrg()
OnUpdateText(_dOLd, d)
_dOLd = d
_i = 0
End If
_i += 1
b1.RotationAdd *= -1
b2.RotationAdd *= -1
End If
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
Next
End Sub
raiseevent
Protected Overridable Sub OnUpdateText(enrgOld As Double, enrg As Double)
RaiseEvent EnergyStatus(Me, New EnergyEventArgs() With { _
.EnergyOld = enrgOld, _
.EnergyNew = enrg _
})
End Sub
Private Function GetOverallEnrg() As Double
Dim res As Double = 0
If _balls IsNot Nothing AndAlso _balls.Count > 0 Then
For i As Integer = 0 To _balls.Count - 1
res += _balls(i).Mass + ((_balls(i).IncrementX * _balls(i).IncrementX + _balls(i).IncrementY * _balls(i).IncrementY) / 2.0)
Next
End If
Return res
End Function
Public Sub IncrementLocationsForAll(clientSize As SizeF)
For i As Integer = 0 To _balls.Count - 1
CheckCollisions(i)
Dim x As Single = _balls(i).Location.X + Me._balls(i).IncrementX
Dim y As Single = _balls(i).Location.Y + Me._balls(i).IncrementY
_balls(i).Location = New PointF(x, y)
reverse direction needed?
If x >= clientSize.Width - _balls(i).Size.Width Then
If Me._balls(i).IncrementX > 0 Then
Me._balls(i).IncrementX *= -1
End If
End If
If x <= 0 Then
If Me._balls(i).IncrementX < 0 Then
Me._balls(i).IncrementX *= -1
End If
End If
If y >= clientSize.Height - _balls(i).Size.Height Then
If Me._balls(i).IncrementY > 0 Then
Me._balls(i).IncrementY *= -1
End If
End If
If y <= 0 Then
If Me._balls(i).IncrementY < 0 Then
Me._balls(i).IncrementY *= -1
End If
End If
Next
End Sub
Public Sub RenderAll(g As Graphics)
draw each ball
For i As Integer = 0 To _balls.Count - 1
_balls(i).Render(g)
Next
End Sub
End Class
Public MustInherit Class Ball
Implements IDisposable
Public Property Location() As PointF
Get
Return m_Location
End Get
Set(value As PointF)
m_Location = Value
End Set
End Property
Private m_Location As PointF
we only use balls, so a single float would be enough for the diameter or radius...
Public Property Size() As SizeF
Get
Return m_Size
End Get
Set(value As SizeF)
m_Size = Value
End Set
End Property
Private m_Size As SizeF
Public Property IncrementX() As Single
Get
Return m_IncrementX
End Get
Set(value As Single)
m_IncrementX = Value
End Set
End Property
Private m_IncrementX As Single
Public Property IncrementY() As Single
Get
Return m_IncrementY
End Get
Set(value As Single)
m_IncrementY = Value
End Set
End Property
Private m_IncrementY As Single
Public Property RotationAdd() As Single
Get
Return m_RotationAdd
End Get
Set(value As Single)
m_RotationAdd = Value
End Set
End Property
Private m_RotationAdd As Single
current rotation angle
Protected Property R() As Single
Get
Return m_R
End Get
Set(value As Single)
m_R = Value
End Set
End Property
Private m_R As Single
Public Sub ComputeMass()
since we use balls, we calculate a comparable value - her - the volume
Mass = 4.0 / 3.0 * Math.PI * Math.Pow(Size.Width / 2.0, 3.0)
End Sub
Public MustOverride Sub Render(g As Graphics)
Public Function Collides(ball As Ball) As Boolean
we are using a Region to determine collisions
we first set up two GraphicsPaths with the ellipses and create a Region from one
then we intersect the Region with the second GraphicsPath and look, if
we have intersections by simnply checking the amount of RegionScans (RectanlgeFs) in the result.
Using gP1 As New System.Drawing.Drawing2D.GraphicsPath()
gP1.AddEllipse(New RectangleF(Me.Location, Me.Size))
Using gP2 As New System.Drawing.Drawing2D.GraphicsPath()
gP2.AddEllipse(New RectangleF(ball.Location, ball.Size))
Using reg As New Region(gP1)
reg.Intersect(gP2)
If reg.GetRegionScans(New System.Drawing.Drawing2D.Matrix(1, 0, 0, 1, 0, 0)).Length > 0 Then
Return True
End If
End Using
End Using
End Using
Return False
End Function
Protected Function GetBrush() As System.Drawing.Drawing2D.PathGradientBrush
Dim p As System.Drawing.Drawing2D.PathGradientBrush = Nothing
Using gP As New System.Drawing.Drawing2D.GraphicsPath()
gP.AddEllipse(New RectangleF(New PointF(0, 0), Me.Size))
p = New System.Drawing.Drawing2D.PathGradientBrush(gP)
p.CenterColor = Color.FromArgb(147, 255, 255, 255)
Dim w As Single = Me.Size.Width / 4.0F
Dim h As Single = Me.Size.Height / 4.0F
p.CenterPoint = New PointF(w, h)
p.SurroundColors = New Color() {Color.Transparent}
End Using
Return p
End Function
Protected Function GetBrush(alpha As Integer) As System.Drawing.Drawing2D.PathGradientBrush
Dim p As System.Drawing.Drawing2D.PathGradientBrush = Nothing
Using gP As New System.Drawing.Drawing2D.GraphicsPath()
gP.AddEllipse(New RectangleF(New PointF(0, 0), Me.Size))
p = New System.Drawing.Drawing2D.PathGradientBrush(gP)
p.CenterColor = Color.FromArgb(147, 255, 255, 255)
Dim w As Single = Me.Size.Width / 4.0F
Dim h As Single = Me.Size.Height / 4.0F
p.CenterPoint = New PointF(w, h)
p.SurroundColors = New Color() {Color.FromArgb(alpha, 0, 0, 0)}
End Using
Return p
End Function
Public Property Mass() As Double
Get
Return m_Mass
End Get
Set(value As Double)
m_Mass = Value
End Set
End Property
Private m_Mass As Double
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Public Class GradientBall
Inherits Ball
Implements IDisposable
Private brush2 As System.Drawing.Drawing2D.PathGradientBrush
Private brush As System.Drawing.Drawing2D.LinearGradientBrush
Public Sub New()
Me.IncrementX = InlineAssignHelper(Me.IncrementY, 1)
Me.RotationAdd = 2
Me.Size = New SizeF(100, 100)
Me.brush = New System.Drawing.Drawing2D.LinearGradientBrush(New RectangleF(0, 0, Size.Width, Size.Height), Color.DarkRed, Color.Yellow, 0.0F)
Me.brush.WrapMode = System.Drawing.Drawing2D.WrapMode.TileFlipXY
Me.brush2 = GetBrush()
ComputeMass()
End Sub
Public Sub New(size__1 As SizeF, c1 As Color, c2 As Color, increment As Single, startLocation As PointF)
Me.IncrementX = InlineAssignHelper(Me.IncrementY, increment)
Me.RotationAdd = 2
Me.Size = size__1
Me.brush = New System.Drawing.Drawing2D.LinearGradientBrush(New RectangleF(0, 0, Size.Width, Size.Height), c1, c2, 0.0F)
Me.brush.WrapMode = System.Drawing.Drawing2D.WrapMode.TileFlipXY
Me.brush2 = GetBrush()
Me.Location = startLocation
ComputeMass()
End Sub
Public Sub New(size__1 As SizeF, c1 As Color, c2 As Color, incrementX As Single, incrementY As Single, startLocation As PointF)
Me.IncrementX = incrementX
Me.IncrementY = incrementY
Me.RotationAdd = 2
Me.Size = size__1
Me.brush = New System.Drawing.Drawing2D.LinearGradientBrush(New RectangleF(0, 0, Size.Width, Size.Height), c1, c2, 0.0F)
Me.brush.WrapMode = System.Drawing.Drawing2D.WrapMode.TileFlipXY
Me.brush2 = GetBrush()
Me.Location = startLocation
ComputeMass()
End Sub
Public Overrides Sub Dispose()
cleanup
If brush IsNot Nothing Then
brush.Dispose()
End If
If brush2 IsNot Nothing Then
brush2.Dispose()
End If
MyBase.Dispose()
End Sub
Public Overrides Sub Render(g As Graphics)
rotate
R += RotationAdd
translate to center, rotate and translate back
brush.TranslateTransform(-brush.Rectangle.Width / 2.0F, -brush.Rectangle.Height / 2.0F)
brush.RotateTransform(R, System.Drawing.Drawing2D.MatrixOrder.Append)
brush.TranslateTransform(brush.Rectangle.Width / 2.0F, brush.Rectangle.Height / 2.0F, System.Drawing.Drawing2D.MatrixOrder.Append)
translate graphics object and draw at *(0, 0)* !
g.TranslateTransform(Location.X, Location.Y)
g.FillEllipse(brush, New RectangleF(New Point(0, 0), Size))
g.FillEllipse(brush2, New RectangleF(New Point(0, 0), Size))
reset the transforms
brush.ResetTransform()
g.ResetTransform()
End Sub
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Class
View the full article