|
|
Title | Draw a spline "by hand" in VB .NET |
Description | This example shows how to draw a spline "by hand" in VB .NET. |
Keywords | smooth curve, spline, cardinal spline, Bezier curve, tension, VB .NET |
Categories | Graphics |
|
|
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.
|
|
|
|
|
|