Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Sine wave drawing 1

Status
Not open for further replies.

Lennieboy

Programmer
Jul 13, 2006
18
GB
Hi Gurus.

I need to draw a graph of a sine wave in a picturebox demonstrating how its frequence changes. I have searched everywhere for samplecode and found nothing. This will obviously use the math.sine() functionality somewhere I suppose.

There are a few VB6 and ASP examples, but they are of no use in VB.net. Can anyone point me in the right direction, or better even, throw a few lines of sample code to me?

Gratefully yours



Lennieboy -
Life is Good and God is Good
B-)
 
Have you tried to use the VB6 code putting it through the upgrade wizard?
djj
 
Just an idea, I've never done it. But could you plot some points using the Sine function and then pass them as an array into something that will draw a shape? Good Luck!

Have a great day!

j2consulting@yahoo.com
 
I looked at the VB6 code, but the VB6 code cannot be converted to VB.net due to serious changes to the libraries. I tried it already. The sine function returns a decimal, not a point. I am playing with the idea of the DrawBeziers function, but it requires an array of points (x,y)'s.

I can not find ANY sourcecode anywhere on the web! Can this be? Has nobody in the world ever tried to do this? There has to be an example of this somewhere in the world...



 
This will draw a sine wave in a picturebox. It's not very pretty - the line is dashed - but it is recognizable as a sine wave. I'll leave it to you to play with the numbers to change the frequency and/or amplitude, and to get the line unbroken and smooth:

Dim x As Double
Dim y As Double
Dim blackPen As New Pen(Color.Black, 3)
Dim g As Graphics

g = PictureBox1.CreateGraphics

For r As Double = 0 To 400
y = Math.Sin(r / 50 * 2 * Math.PI) * 100 + 100
x = r

g.DrawLine(blackPen, CType(x, Single), CType(y, Single), CType(x, Single) + 1, CType(y, Single))

Next

'draw a center line in the wave
g.DrawLine(blackPen, 0, 100, PictureBox1.Width, 100)

I used to rock and roll every night and party every day. Then it was every other day. Now I'm lucky if I can find 30 minutes a week in which to get funky. - Homer Simpson

Arrrr, mateys! Ye needs ta be preparin' yerselves fer Talk Like a Pirate Day! Ye has a choice: talk like a pira
 
Thanks jebenson!

I appreciate this.

I also worked on it this weekend and came up with a very good solution using the g.graphicpath function. It produces a neat smooth line and fits perfectly into the picturebox. I will post the solution here soon so all can benefit from it.

Kind regards

Lennieboy



Lennieboy -
Life is Good and God is Good
B-)
 
I have a picturebox called pbWave, a Scrollbar called HSFreq, a commandbutton called cmdGo.

I wanted to draw a sine wave inside the picbox and used the graphics path "drawpath" method.

Here goes:



Private Sub DrawLine(ByRef g As Graphics)

Dim iStart, iEnd, iY_Axis As Int16

Dim myPen1 As New Pen(Color.Blue, 2)
Dim myPen2 As New Pen(Color.Black, 1)
Dim myPen3 As New Pen(Color.Red, 1)
Dim i, maxX, SineValue As Int32
Dim gPath As New System.Drawing.Drawing2D.GraphicsPath

Try
iStart = CInt(Me.pbWave.Left - 50)
iEnd = Me.pbWave.Width
iY_Axis = Me.pbWave.Height / 2
'Clear the image from prev waves
Me.pbWave.CreateGraphics.Clear(Color.White)
'Draw the x-axis
Me.pbWave.CreateGraphics.DrawLine(myPen1, New Point(iStart, iY_Axis), New Point(iEnd, iY_Axis))

'Draw the wave===

For i = iStart To iEnd
gPath.AddLine(New Point(i, SinePoint(i, iY_Axis, iEnd)), New Point(i + 1, SinePoint(i + 1, iY_Axis, iEnd)))
Me.pbWave.CreateGraphics.DrawPath(myPen3, gPath)
Next

Catch ex As Exception
MessageBox.Show(ex.Message, "DrawAxis", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Finally
myPen1 = Nothing
myPen3 = Nothing
myPen2 = Nothing
g.Dispose()
g = Nothing
End Try

End Sub

Private Function SinePoint(ByVal x As Int16, ByVal iHalfY As Int16, ByVal iXMax As Int16) As Int32
Dim iFreq As Int16 = Me.HSFreq.Value
Dim iPIVal As Decimal = iFreq * Math.PI
Dim dFactor As Decimal = iPIVal / iXMax

Try

SinePoint = Math.Sin(x * dFactor) * iHalfY + iHalfY

Catch ex As Exception
MessageBox.Show(ex.Message, "DrawWave", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Try
End Function
Private Sub cmdGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGo.Click
miFreq = HSFreq.Value

DrawLine(Me.CreateGraphics)

End Sub


 
It suffers from the #1 Problem on Bob's GDI FAQ:

- the drawing disapears when the picturebox.paint method is called. This will happen if you drag your form off screen and back on, or if another form moves across your form. It happens because the areas of the control that were obscured need to be repainted, and as a part of that the background is painted - on top of your drawing...

The fix is to either draw your image onto a bitmap and set the picturebox's image property to that bitmap, or do all your painting in the paint event of the control (so work with the events, not fight against them). (Or you could draw to a bitmap, and draw that onto the control in the paint event - but then you have to worry about resizing the bitmap).

CreateGraphics is very resource hungry, and you call it everytime you draw something (the code doesn't actually use the graphics that is passed in to the sub DrawLine).

You don't need the precision of the Decimal data type - especially as you are returning an int. Using a single is good enough, and will give better performance.

And graphicsPaths aren't very fast either, an array of points is better here, you can then use Graphics.DrawLines to join them up.

Here's an alternative.
Code:
Option Strict On
Option Explicit On

Public Class Form1

    Private uxSineWaveControl As SineWaveControl
    Private WithEvents uxFreq As TrackBar

    Sub New()
        InitializeComponent()
        Me.Size = New Size(800, 600)
        uxSineWaveControl = New SineWaveControl
        uxSineWaveControl.Width = 760
        uxSineWaveControl.Height = 400
        uxSineWaveControl.Location = New Point(20, 20)
        uxSineWaveControl.Anchor = AnchorStyles.Bottom Or AnchorStyles.Left Or _
         AnchorStyles.Right Or AnchorStyles.Top
        Me.Controls.Add(uxSineWaveControl)
        uxFreq = New TrackBar
        uxFreq.Width = 300
        uxFreq.Location = New Point(20, uxSineWaveControl.Bottom + 20)
        uxFreq.Minimum = 1
        uxFreq.Maximum = 10000 ' we'll divide by 100...
        uxFreq.TickFrequency = 500
        uxFreq.Anchor = AnchorStyles.Bottom Or AnchorStyles.Left
        Me.Controls.Add(uxFreq)
    End Sub

    Private Sub uxFreq_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles uxFreq.ValueChanged
        uxSineWaveControl.Frequency = CType(uxFreq.Value / 100, Single)
        Me.Text = "Frequency: " & uxSineWaveControl.Frequency.ToString
        ' We need to ask the control to repaint itself.
        uxSineWaveControl.Invalidate()
    End Sub
End Class

' Custom control reponsible for drawing itself.
Public Class SineWaveControl
    Inherits Control

    Private m_Freq As Single
    ' Math.pi is too precise (double), as a tweak we'll make a less precise version (single).
    Private Const pi As Single = CSng(Math.PI)

    Sub New()
        ' Double buffer to reduce flicker.
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or _
         ControlStyles.UserPaint Or ControlStyles.ResizeRedraw, True)
        Me.UpdateStyles()
    End Sub

    Public Property Frequency() As Single
        Get
            Return m_Freq
        End Get
        Set(ByVal value As Single)
            m_Freq = value
        End Set
    End Property

    ' why override? see: [URL unfurl="true"]http://www.bobpowell.net/overrideorhandle.htm[/URL]
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        ' No need to CreateGraphics, we get one in the event args.
        ' Smoothing, I don't like it for graphs:
        ' e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
        ' The clientsize property is the usable size, it does not include borders/titlebars and whatnot
        Dim halfHeight As Single = Convert.ToSingle(Me.ClientSize.Height / 2)
        e.Graphics.Clear(Color.White)
        'The pen will be disposed of immediately after use when it is declared in a function call.
        e.Graphics.DrawLine(New Pen(Color.Blue, 2), New PointF(0, halfHeight), New PointF(Me.ClientSize.Width, halfHeight))
        Dim points(Me.ClientSize.Width * 2) As PointF
        ' we'll get it smoother by drawing 2 points as we move 1 pixel across (though this is wasteful when freq is large)
        For i As Integer = 0 To Me.ClientSize.Width * 2
            ' Multiply by halfheight - 1 as the bottom was getting clipped. The rest is the function stripped down a bit.
            ' And we need to CType as Option Strict is On.
            points(i) = New PointF(CType(i / 2, Single), halfHeight + _
             CType(Math.Sin(i * m_Freq * pi / Me.ClientSize.Width) * (halfHeight - 1), Single))
        Next
        e.Graphics.DrawLines(Pens.Red, points)
        MyBase.OnPaint(e)
        ' Don't dispose of e.graphics - windows is in charge of it.
    End Sub

End Class
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top