Let the user draw a smooth curve in C#

[smooth curve]

This example lets the user click points to draw a smooth curve. When the user clicks the left mouse button, the program adds a point to the curve. When the user clicks the right button, the program stops drawing the current curve. If the user then clicks the left button again, the program starts drawing a new smooth curve.

The following code shows variables that the program uses to keep track of what it is doing and how it should draw the curve whether it is in progress or a finished curve.

// True when we are drawing.
private bool Drawing = true;

// The currently selected points.
private List<Point> Points = new List<Point>();

// The curve's tension.
private float Tension = 0.5f;

The variable Drawing is true while the program is defining a new smooth curve. Initially it is true.

The Points list holds the points that define the current smooth curve.

The Tension variable stores the tension parameter that is used when drawing the smooth curve. The program allows the user to select values between 0.0 and 5.0. Smaller values make the curve lie more closely to straight lines connecting the points. Larger values make the curve smoother when it

The following MouseClick event handler executes whenever the user clicks a mouse button.

// The user clicked. Add a point,
// stop drawing, or start a new curve.
private void picCanvas_MouseClick(object sender, MouseEventArgs e)
{
    // See if we are currently drawing.
    if (Drawing)
    {
        // See if this is the left or right mouse button.
        if (e.Button == MouseButtons.Left)
        {
            // Left button. Add a new point.
            Points.Add(e.Location);
        }
        else
        {
            // Right button. Stop drawing.
            Drawing = false;
        }
    }
    else
    {
        // We are not drawing. Start a new curve.
        Drawing = true;
        Points = new List<Point>();

        // Add a new point.
        Points.Add(e.Location);
    }

    // Redraw.
    picCanvas.Refresh();
}

This code is somewhat long but relatively straightforward. If the program is currently drawing a smooth curve, then it determines which button is being clicked. If the user clicked the left button, then the code adds a new point to the Points list. If the user clicked the right button, then the code sets Drawing to false to indicate that it is done drawing the current curve.

If the program is not currently drawing, then it sets Points to a new list and adds the clicked point to the list to start a new curve.

In any case, the code refreshes the picCanvas PictureBox. That makes the following Paint event handler execute to draw the smooth curve.

// Draw the curve and its points.
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    // Draw the curve.
    if (Points.Count > 1)
    {
        // Make a pen to use.
        using (Pen pen = new Pen(Color.Blue))
        {
            // See if we're currently drawing.
            if (Drawing)
            {
                // Use a dashed pen.
                pen.DashPattern = new float[] { 5, 5 };
            }

            // Draw the curve.
            e.Graphics.DrawCurve(pen, Points.ToArray(), Tension);
        }
    }

    // Draw the points.
    if (Drawing && (Points.Count > 0))
    {
        const int r = 4;
        foreach (Point point in Points)
        {
            Rectangle rect = new Rectangle(
                point.X - r, point.Y - r, 2 * r, 2 * r);
            e.Graphics.FillRectangle(Brushes.White, rect);
            e.Graphics.DrawRectangle(Pens.Black, rect);
        }
    }
}

This code sets the Graphics object’s SmoothingMode to produce smooth curves. Then, if the Points list contains more than one point, the code draws the curve. To do that, it creates a new blue pen. If the user is still drawing the curve, the code sets the pen’s DashPattern to draw 5 pixels and then skip 5 pixels. (You could simply set the pen’s DashStyle to Dash, but that produces very small dashes so I think this code produces a better result.) The code then draws the curve, passing the DrawCurve method the pen, the points (converted into an array), and the tension.

Having drawn the curve, the code then draws the points. If the user is still drawing the curve and the Points list contains at least one point, then the code loops through the Points list. For each point, it creates a rectangle at the point’s position, fills the rectangle with white, and then outlines the point in black.

The program’s last piece is the following event handler, which execute whenever the user adjusts the tension scroll bar.

// Adjust the curve's tension.
private void scrTension_Scroll(object sender, ScrollEventArgs e)
{
    Tension = scrTension.Value / 10f;
    lblTension.Text = Tension.ToString();
    picCanvas.Refresh();
}

This code gets the scroll bar’s value and divides it by 10. That allows the user to select values to the nearest tenth. The scroll bar has the following properties.

  • Minimum = 0
  • Maximum = 59
  • SmallChange = 1
  • LargeChange = 10

The event handler divides the scroll bar’s value by 10 to allow the user to select tenths. That means the LargeChange value (which is used when the user clicked between the scrollbar’s thumb and arrows) adjust the value by 10, which is converted into 1 unit of tension.

Because of a weirdness in the way scroll bars work, the user can interactively select values between Minimum and Maximum - LargeChange + 1, so in this example that means the user can select scroll bar values between 0 and 59 – 10 + 1 = 50. After dividing by 10, that means the user can select tension values between 0 and 5.

After it divides the scroll bar value by 10 to get tension to the nearest tenth, the scrTension event handler displays the tension in the lblTension label ad then refreshes the PictureBox.

Download the example program to experiment with the program.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, graphics and tagged , , , , , , , , , . Bookmark the permalink.

13 Responses to Let the user draw a smooth curve in C#

  1. Tony says:

    Thanks for this. I was working on a directed acyclic graph type drawing in WPF. Got the nodes. Still working on getting the smooth lines. 🙂

  2. Carmen says:

    Should I create a picture box for this?

    • RodStephens says:

      Download the example to see all of the details. Yes the example uses a PictureBox named picCanvas. You could do something similar with the form itself, but I usually use a PictureBox so it's movable.

  3. Carmen says:

    How to clear the curve created?

  4. Eric says:

    How to save this as a jpg file?

  5. Thomas says:

    Thank you for your post! It works for me!
    I am wondering how to allow the user to change the colour of the pen?

    • RodStephens says:

      You would need to add a user interface element such as a menu item or button to let the user pick the color. For example, you could use a set of radio buttons for different colors. The Paint event handler could see which one is checked and make an appropriate pen.

      It would be even more interesting to let the user draw several curves with different colors. See this example:

      Copy and paste scribble data in C#

      The copy and paste part isn’t what you’re asking, but the Polyline class stores color, link style, and line thickness. You could do something similar for the smooth curve drawn in this example. That example also shows a simple user interface to let the user pick a line color.

  6. Pingback: Let the user save an image of a smooth curve in C# - C# HelperC# Helper

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.