You need a curve that goes smoothly through two or more points.
Sample code folder: Chapter 09\CardinalSplines
A Cardinal spline plots a curve through two or more points. Unlike the Bezier spline, the Cardinal spline intersects every point and does not use external control points.
The mathematical description of the way the Cardinal spline works is beyond the scope of this book. For a more in-depth discussion and explanation of the math involved, see the links in the "See Also" section at the end of this recipe.
The following code demonstrates the Cardinal spline by collecting points as they are clicked on the face of the form. A list of the points is built up, and with each added point, the Cardinal spline is drawn anew. A button at the top of the form lets you erase all the points to start over, and a TrackBar control lets you set the tension parameter for the spline. The tension is a number ranging from 0 to 1 that is passed to the DrawCurve() method to determine the smoothness of the curve as it passes through each point. The easiest way to understand the effect of this parameter is to slide the trackBar and watch the curve change shape.
Here's the code that lets the form monitor for mouse clicks, builds the set of points, and refreshes the form to activate its Paint event:
' ----- Keep track of the mouse positions. Private BendPoints As New Generic.List(Of Point) Private Sub Form1_MouseClick(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles Me.MouseClick ' ----- Add a mouse position. BendPoints.Add(New Point(e.X, e.Y)) ' ----- Update the display. Me.Refresh() End Sub
The form's Paint event is where the drawing of the selected points and the spline connecting them takes place. The event fires when the form is refreshed, which is caused by calling the Refresh() method when the mouse is clicked or the trackbar is adjusted.
This code draws each plotted point in red as the user clicks it. Then, if there are two or more accumulated points, it draws the Cardinal spline using the DrawCurve() method:
Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) _ Handles Me.Paint ' ----- Draw the spline points and line. Dim tension As Single Dim canvas As Graphics Dim scanPoint As Point Const PointSize As Integer = 7 ' ----- Determine the tension. tension = TensionLevel.Value / TensionLevel.Maximum LabelTension.Text = "Tension: " & tension.ToString ' ----- Draw the points on the surface. canvas = e.Graphics For Each scanPoint In BendPoints canvas.FillEllipse(Brushes.Red, _ scanPoint.X - PointSize, _ scanPoint.Y - PointSize, _ PointSize * 2, PointSize * 2) Next scanPoint ' ----- Draw the Cardinal spline. If (BendPoints.Count > 1) Then canvas.DrawCurve(Pens.Black, _ BendPoints.ToArray, tension) End If End Sub
When the trackbar's slider is adjusted, the form's Refresh() method is called to trigger a repaint:
Private Sub TensionLevel_ValueChanged( _ ByVal sender As Object, ByVal e As System.EventArgs) _ Handles TensionLevel.ValueChanged ' ----- Update the tension and display. Me.Refresh() End Sub
When the Reset button is clicked, the set of points is emptied, and the form is repainted to erase the points and the curve:
Private Sub ActReset_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles ActReset.Click ' ----- Clear all points. BendPoints.Clear() Me.Refresh() End Sub
Figure 9-23 shows a typical spline curve through six points with the tension set to 0.6. A lower tension results in sharp angles at the bend points, while higher tension gives a smoother curve.
Figure 9-23. Cardinal splines travel through all given points
See http://www.ibiblio.org/e-notes/Splines/Cardinal.htm and http://en.wikipedia.org/wiki/Cardinal_spline for more information on Cardinal splines.