Home
Search
 
What's New
Index
Books
Links
Q & A
Newsletter
Banners
 
Feedback
Tip Jar
 
C# Helper...
 
XML RSS Feed
Follow VBHelper on Twitter
 
 
 
MSDN Visual Basic Community
 
 
 
 
 
TitleDraw a spline "by hand" in VB .NET
DescriptionThis example shows how to draw a spline "by hand" in VB .NET.
Keywordssmooth curve, spline, cardinal spline, Bezier curve, tension, VB .NET
CategoriesGraphics
 
This approach connects a series of points with Bezier curves. The interior control points are chosen so the curves meet smoothly. These control points lie along a line parallel to the line connecting the points' neighbors. For example, suppose point p2 lies between p1 and p3. When drawing the Bezier curve between points p2 and p3, the control point after p2 lies along a line starting at p2 and going in the same direction as the line between p1 and p3. The distance along this line that the point lies is determined by a "tension" factor. See the code for details.

A special case occurs at the first and last points, which have only one neighbor. At these points, the control points lie along the line between the end point and its only neighbor.

The PictureBox's Paint event handler loops through the pairs of adjacent control points, defines two extra control points between them, and calls subroutine DrawBezier to draw that section of curve.

 
Private Sub picCanvas_Paint(ByVal sender As Object, ByVal e _
    As System.Windows.Forms.PaintEventArgs) Handles _
    picCanvas.Paint
    e.Graphics.Clear(Me.BackColor)
    If m_NumPoints < 1 Then Exit Sub

    ' Draw a spline the easy way.
    If m_NumPoints > 1 Then
        Dim thick_pen As New Pen(Color.White, 5)
        e.Graphics.DrawCurve(thick_pen, m_Pts)
        thick_pen.Dispose()
    End If

    Dim A As Single = CSng(Tension / 0.5 * 0.175)
    Dim pt, pt_before, pt_after, pt_after2, Di, DiPlus1 As _
        PointF
    Dim p1, p2, p3, p4 As PointF
    For i As Integer = 0 To m_Pts.GetUpperBound(0) - 1
        pt_before = m_Pts(Math.Max(i - 1, 0))
        pt = m_Pts(i)
        pt_after = m_Pts(i + 1)
        pt_after2 = m_Pts(Math.Min(i + 2, _
            m_Pts.GetUpperBound(0)))

        p1 = m_Pts(i)
        p4 = m_Pts(i + 1)

        Di.X = pt_after.X - pt_before.X
        Di.Y = pt_after.Y - pt_before.Y
        p2.X = pt.X + A * Di.X
        p2.Y = pt.Y + A * Di.Y

        DiPlus1.X = pt_after2.X - m_Pts(i).X
        DiPlus1.Y = pt_after2.Y - m_Pts(i).Y
        p3.X = pt_after.X - A * DiPlus1.X
        p3.Y = pt_after.Y - A * DiPlus1.Y

        DrawBezier(e.Graphics, p1, p2, p3, p4)
    Next i
End Sub
 
Subroutine DrawBezier draws the control points for a section of curve and then calls DrawCurve to draw the actual curve.
 
' Prepare to draw the Bezier curve.
Private Sub DrawBezier(ByVal gr As Graphics, ByVal pt0 As _
    PointF, ByVal pt1 As PointF, ByVal pt2 As PointF, ByVal _
    pt3 As PointF)
    ' Draw the control lines.
    Dim dashed_pen As New Pen(Color.Black)
    dashed_pen.DashStyle = Drawing2D.DashStyle.Custom
    dashed_pen.DashPattern = New Single() {4, 4}
    gr.DrawLine(dashed_pen, pt0.X, pt0.Y, pt1.X, pt1.Y)
    gr.DrawLine(dashed_pen, pt2.X, pt2.Y, pt3.X, pt3.Y)
    dashed_pen.Dispose()

    ' Draw the control points.
    gr.FillRectangle(Brushes.White, pt0.X - 3, pt0.Y - 3, _
        6, 6)
    gr.DrawRectangle(Pens.Black, pt0.X - 3, pt0.Y - 3, 6, 6)
    gr.FillRectangle(Brushes.LightGray, pt1.X - 3, pt1.Y - _
        3, 6, 6)
    gr.DrawRectangle(Pens.Gray, pt1.X - 3, pt1.Y - 3, 6, 6)
    gr.FillRectangle(Brushes.LightGray, pt2.X - 3, pt2.Y - _
        3, 6, 6)
    gr.DrawRectangle(Pens.Gray, pt2.X - 3, pt2.Y - 3, 6, 6)
    gr.FillRectangle(Brushes.White, pt3.X - 3, pt3.Y - 3, _
        6, 6)
    gr.DrawRectangle(Pens.Black, pt3.X - 3, pt3.Y - 3, 6, 6)

    ' Draw the curve.
    DrawCurve(gr, 0, 1, 0.01, pt0, pt1, pt2, pt3)
End Sub
 
Subroutine DrawCurve makes a parameter t loop from 0 to 1. It calls functions X and Y to get points on the curve and connects them.
 
' Draw the curve on the indicated picture box.
Private Sub DrawCurve(ByVal gr As Graphics, ByVal start_t _
    As Single, ByVal stop_t As Single, ByVal dt As Single, _
    ByVal pt0 As PointF, ByVal pt1 As PointF, ByVal pt2 As _
    PointF, ByVal pt3 As PointF)
    Dim t, x0, y0, x1, y1 As Single

    t = start_t
    x1 = X(t, pt0.X, pt1.X, pt2.X, pt3.X)
    y1 = Y(t, pt0.Y, pt1.Y, pt2.Y, pt3.Y)
    t += dt
    Do While t < stop_t
        x0 = x1
        y0 = y1
        x1 = X(t, pt0.X, pt1.X, pt2.X, pt3.X)
        y1 = Y(t, pt0.Y, pt1.Y, pt2.Y, pt3.Y)
        gr.DrawLine(Pens.Black, x0, y0, x1, y1)
        t += dt
    Loop
End Sub

' The parametric function X(t).
Private Function X(ByVal t As Single, ByVal x0 As Single, _
    ByVal x1 As Single, ByVal x2 As Single, ByVal x3 As _
    Single) As Single
    X = CSng( _
        x0 * (1 - t) ^ 3 + _
        x1 * 3 * t * (1 - t) ^ 2 + _
        x2 * 3 * t ^ 2 * (1 - t) + _
        x3 * t ^ 3 _
        )
End Function

' The parametric function Y(t).
Private Function Y(ByVal t As Single, ByVal y0 As Single, _
    ByVal y1 As Single, ByVal y2 As Single, ByVal y3 As _
    Single) As Single
    Y = CSng( _
        y0 * (1 - t) ^ 3 + _
        y1 * 3 * t * (1 - t) ^ 2 + _
        y2 * 3 * t ^ 2 * (1 - t) + _
        y3 * t ^ 3 _
        )
End Function
 
See Draw a Bezier curve for information on drawing Bezier curves.

Also see Draw a smooth closed curve for information on drawing closed curves.

For more information on graphics programming in Visual Basic 6, see my book Visual Basic Graphics Programming.

 
 
Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.
  Updated