VB.NET: Using Trigonometry to draw graphic curves (part 2/2)
USING TRIG TO DRAW A SIN WAVE
Since my last article on the Unit Circle was such a hit I decided to continue. In this example we move from using sin and cos to calculate points on the perimeter of a circle to making a "sin" wave.
Introduction
If you watch the values of sin as the angle moves around the circle, you may spot that the values are changing rapidly at 0 and slowly at 90 degrees. That is because at Delta = 0 you are near the X axis where the value of Y is changing rapidly as the angle delta moves at a uniform rate. At Delta = 90 you are on the top of the circle where it is flat and the y value, the sin, changes slowly until it reaches the maximum at 1.
Unit Circle showing flat and steep areas.
CODING A SIN WAVE GRAPH
Lets make a graph that plots the value of the sin in the Y direction. In the X direction we will plot the angle delta. Can you guess what the graph will look like? Its going to be steep in the area around Delta = 0 and flat around Delta = 90.
You can see in this next example 2 we use the sin function to get the value of y at the angle delta. We simply plot delta along the x axis: (x, y) = (delta, sin(delta)) where x is delta and y is sin(delta). See the red sin wave? Note we adjusted the x values so they fit the circle. Any scale could have been used for the x axis.
Public
Class
Form4
'Using Trigonometry to draw graphic curves in VB.NET part 2, Example 2
'draws sin wave on unit circle
Private WithEvents timer1 As New Windows.Forms.Timer With {.Interval = 500}
Private Delta As Single
Private DeltaStep As Single = 15
Private Sub Form4_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
Dim Scale As Single = 4 'create a scale of 2 units across the width of the form
Dim ScaleRatio As Single = Me.ClientRectangle.Width / Scale 'pixels/unit
Dim r As Single = 1 'set radius to 1 for the "Unit Circle"
Dim fntheight As Single = Scale / 25
Dim x, y, x1, y1 As Single
With e.Graphics
Using p As New Pen(Color.LightBlue, Scale / 1000)
Using br As New SolidBrush(Color.Black)
Using f As New Font("Arial", fntheight)
.Clear(Color.White)
.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
'scale the window
.ScaleTransform(ScaleRatio, ScaleRatio)
'move the 0,0 coordinate to the center of the window
.TranslateTransform(Scale / 2, Scale / 2)
'draw the axes
For x = -Scale To Scale
.DrawLine(p, x, -Scale / 2, x, Scale / 2)
.DrawString(x.ToString, f, br, x, -Scale / 2)
.DrawLine(p, -Scale / 2, x, Scale / 2, x)
.DrawString(x.ToString, f, br, -Scale / 2, x)
Next
'draw the unit circle perimeter
p.Width = Scale / 200
p.Color = Color.Blue
Dim rectf As RectangleF = New RectangleF(-r, -r, 2 * r, 2 * r)
.DrawArc(p, rectf, 0, 360)
'draw the sine wave to the current delta
p.Color = Color.Red
p.Width = Scale / 150
p.EndCap = Drawing2D.LineCap.DiamondAnchor
x1 = -r
y1 = 0
For d = 0 To Delta Step DeltaStep
'calculate the y coordinate on the circle at the angle delta.
'Convert degress to radians for sin function
'use delta for the x value
x = (0.4 * d / ScaleRatio) - 1
y = r * Math.Sin(d / 57.3)
.DrawLine(p, x, y, x1, y1)
x1 = x
y1 = y
Next
'draw the radius line at the current angle set in the timer
p.Color = Color.Blue
p.Width = 0.05
x = r * Math.Cos(Delta / 57.3)
y = r * Math.Sin(Delta / 57.3)
.DrawLine(p, 0, 0, x, y)
'draw the values
x = -0.3 * Scale
y = -0.4 * Scale
.DrawString("Delta: " & Delta.ToString, f, br, x, y)
Dim t As String = "Cos: " & Math.Cos(Delta / 57.3).ToString("f3") & " Sin: " & Math.Sin(Delta / 57.3).ToString("f3")
.DrawString(t, f, br, x, y + (2 * fntheight))
End Using
End Using
End Using
End With
End Sub
Private Sub timer1_Tick(sender As Object, e As EventArgs) Handles timer1.Tick
Delta += DeltaStep
If Delta > 360 Then Delta -= 360
Me.Invalidate()
End Sub
Private Sub Form4_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.DoubleBuffered = True
timer1.Start()
End Sub
Private Sub Form4_Click(sender As Object, e As EventArgs) Handles Me.Click
timer1.Enabled = Not timer1.Enabled
End Sub
Private Sub Form4_Resize(sender As Object, e As EventArgs) Handles Me.Resize
Me.Invalidate()
End Sub
End Class
**
**
Coding a Fancy SIN Wave and Unit Circle
Now in example 3 we will get fancy and put the last two examples to work. The code produces a marquee effect with text by placing the text either around a circle (similar to example 1) or along a sin wave (similar to example 2).
In the sin wave example we just place the text at the y values calculated with the sin function y = sin(Delta). The X axis of the graph shows the Delta angle.
However, in the circle marquee we add the step of rotating the text around the circle. In this case, to rotate the text we simply use the value of the delta angle for the text rotation angle. Then we calculate the (x, y) point on the circle where the text is placed using our favorite sin and cos functions.
Example 3: drawing text as Circle or Wave (cick for animation).
Public Class DrawTextMarquee
'Using Trigonometry to draw graphic curves in VB.NET part 2, Example 3
'draws marquee style text animation - wave, circle
Private WithEvents Timer1 As New System.Windows.Forms.Timer With {.Interval = 30}
Private Sub DrawTextMarquee_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.BackgroundImageLayout = ImageLayout.None
Me.DoubleBuffered = True
RadioButton1.Checked = True
Timer1.Start()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim theText As String = "Fancy Marquee"
Dim y2, x2 As Single
Static x1 As Integer = -(theText.Length * 15 + 20)
x1 += 3
If x1 > Me.ClientSize.Width Then x1 = -(theText.Length * 15 + 20)
Static delta As Single
delta += 2
If delta > 359 Then delta = 0
'draw the scene on a the memory bitmap and copy to the form background
Using bmp As Bitmap = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
Using g As Graphics = Graphics.FromImage(bmp)
With g
.Clear(Color.Black)
.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
For i = 1 To theText.Length
If RadioButton1.Checked Then
'wave
x2 = x1 + (i * 24)
y2 = 65 + (20 * Math.Cos(x2 / 26))
g.DrawString(Mid(theText, i, 1), New Font("Arial", 18), Brushes.Red, x2, y2)
Else
'circle
Dim r As Single = 100
Dim d As Single = delta + (i * 16)
x2 = (r * Math.Cos(d / 57.3)) + (Me.ClientSize.Width / 2)
y2 = (r * Math.Sin(d / 57.3)) + (Me.ClientSize.Height / 2)
.ResetTransform()
.TranslateTransform(x2, y2)
.RotateTransform(d + 100)
.TranslateTransform(-x2, -y2)
.DrawString(Mid(theText, i, 1), New Font("Arial", 18), Brushes.Red, x2, y2)
End If
Next
Me.BackgroundImage = bmp.Clone
Me.Invalidate()
End With
End Using
End Using
End Sub
End Class