Share via


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