Fundamentals of Graphics Paths

Introduction

We now know how to draw lines and curves using the .NET Framework. We also know that the primary way to draw is a line, which can be drawn from one point to another point. To conntinue drawing, you can keep adding lines and/or curves and connecting them. A graphics path is a geometric shape that is gotten by connecting two or more lines or curves.

Practical LearningPractical Learning: Introducing Graphics Paths

  1. Start Microsoft Visual Studio
  2. Create a new Windows Forms App named GraphicsPathsFundamentals

A Class for a Graphics Path

To support graphics paths, the .NET Framework provides a sealed class named GraphicsPath:

public sealed class GraphicsPath : MarshalByRefObject, ICloneable, IDisposable

To help you create a graphics path, the GraphicsPath class is equipped with various (six) constructors. To start, the GraphicsPath class is equipped with a default constructor:

public GraphicsPath();

A Class for a Graphics Path

To support graphics paths, the .NET Framework provides a sealed class named GraphicsPath:

public sealed class GraphicsPath : MarshalByRefObject, ICloneable, IDisposable

The GraphicsPath class is defined in a namespace named System.Drawing.Drawing2D.

To help you create a graphics path, the GraphicsPath class is equipped with various (six) Constructors. To start, the GraphicsPath class is equipped with a default constructor:

public GraphicsPath();

Drawing by Following a Path

To help you draw on a path, the Graphics class is equipped with a method named DrawPath. Its syntax is:

public void DrawPath(System.Drawing.Pen pen, System.Drawing.Drawing2D.GraphicsPath path);

The first argument is a Pen object. The second argument a GraphicsPath object. 

Drawing Lines

Adding a Line

In our introduction to drawing, we saw how to draw a line. To support lines, the GraphicsPath is equipped with an overloaded method named AddLine. The syntax of one of the versions is:

public void AddLine(Point start, Point end);

The first argument represents the starting point of the line. The second argument represents the ending point of the line. You can call this method on a GraphicsPath object and pass two points to it. After doing that, you can call a Graphics.DrawPath() method. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph  = e.Graphics;
            Pen pnRed       = new(Color.Red,  2.25f);
            Pen pnBlue      = new(Color.Blue, 5.25f);

            Point start     = new( 45, 325);
            Point end       = new(625,  40);
            GraphicsPath gp = new GraphicsPath();

            gp.AddLine(start, end);

            graph.DrawPath(pnBlue, gp);

            graph.DrawEllipse(pnRed, start.X - 15, start.Y - 15, 30, 30);
            graph.DrawEllipse(pnRed, end.X   - 15, end.Y   - 15, 30, 30);
        }
    }
}

This would produce:

Graphics Path - Adding a Line

In the same way, you can keep calling the GraphicsPath.AddLine() method to add more lines. In the ideal world, you would proceed as follows: add a point with its two coordinates; add the next point in which its first value is the same as the second value of the previous point, and so on. In that case, if you want to create a closed shape, you can add a last line in which the first argument is the last point and the second argument is the first point. Here is an example that uses three points:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph  = e.Graphics;

            Pen pnRed       = new(Color.Red, 1.25f);
            Pen pnBlue      = new(Color.Blue, 5.25f);

            Point first     = new(40, 40);
            Point second    = new(650, 120);
            Point third     = new(350, 320);

            GraphicsPath gp = new GraphicsPath();

            gp.AddLine(first, second);
            gp.AddLine(second, third);
            gp.AddLine(third, first);

            graph.DrawPath(pnBlue, gp);

            graph.DrawEllipse(pnRed, first.X  - 10, first.Y  - 10, 20, 20);
            graph.DrawEllipse(pnRed, second.X - 10, second.Y - 10, 20, 20);
            graph.DrawEllipse(pnRed, third.X  - 10, third.Y  - 10, 20, 20);
        }
    }
}

If you proceed as done above, the compiler would use consecutive points (one point followed by the next) to draw a closed figure. The above code would produce:

Graphics Path - Adding Lines

In reality, you can add a line that uses points in any order of your choice. If you do that, after adding the first line, every time you call the GraphicsPath.AddLine() method, the compiler would draw a line from the end of the previous line to the begining of the next line. Conside the following code:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph  = e.Graphics;
            Pen pnRed       = new(Color.Red, 1.25f);
            Pen pnBlue      = new(Color.Blue, 5.25f);

            Point first     = new(40, 40);
            Point second    = new(450, 120);
            Point third     = new(150, 220);
            Point fourth    = new(650, 345);
            GraphicsPath gp = new GraphicsPath();

            gp.AddLine(first, second);
            gp.AddLine(third, fourth);

            graph.DrawPath(pnBlue, gp);

            graph.DrawEllipse(pnRed, first.X  - 10, first.Y  - 10, 20, 20);
            graph.DrawEllipse(pnRed, second.X - 10, second.Y - 10, 20, 20);
            graph.DrawEllipse(pnRed, third.X  - 10, third.Y  - 10, 20, 20);
            graph.DrawEllipse(pnRed, fourth.X - 10, fourth.Y - 10, 20, 20);
        }
    }
}

This would produce:

Graphics Path - Adding Lines

In the above code pieces, we used points with natural numbers. If you want your lines to use more precision, the GraphicsPath class provides another version of its AddLine() method. Its syntax is:

public void AddLine(PointF start, PointF end);

If you decide to call this version, provide the values of the points as floating-point numbers. Here are examples:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph  = e.Graphics;
            Pen pnBlue      = new(Color.Blue, 5.25f);

            PointF first    = new( 44.84F, 537.97F);
            PointF second   = new( 44.84F,  55.36F);
            PointF third    = new(586.07F, 537.97F);
            PointF fourth   = new(586.07F,  55.36F);
            GraphicsPath gp = new GraphicsPath();

            gp.AddLine(first, second);
            gp.AddLine(third, fourth);

            graph.DrawPath(pnBlue, gp);
        }
    }
}

This would produce:

Graphics Path - Adding Lines

Instead of points, you can directly pass the coordinates of the line(s) for a path. To support this, the precise values that use decimal numbers. In this case, you can use point with floating-point number with single precision. To support this, the GraphicsPath class provides another version of its AddLine() method. Its syntax is:

public void AddLine(int x1, int y1, int x2, int y2);

This version uses natural numbers. If you prefer to use decimal numbers, the GraphicsPath class provides another version of its AddLine() method. Its syntax is:

public void AddLine(float x1, float y1, float x2, float y2);

Adding Lines

In our introduction to the AddLine() method, we saw how to create one line using the GraphicsPath class; then we saw that you can call that method to add more lines. Instead of making you add one line at a time, the GraphicsPath class provides a method named AddLines. It allows you to defines the points you need for the lines. The syntax of that method is:

public void AddLines(Point[] points);

To use this method, create an array of points and pass it as argument. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph  = e.Graphics;
            Pen pnBlue      = new(Color.Blue, 8.25f);

            Point[] points = { new( 40, 550),
                   new( 40,  40),
                   new(300, 250),
                   new(550,  40),
                   new(550, 550) };

            GraphicsPath gp = new GraphicsPath();

            gp.AddLines(points);

            graph.DrawPath(pnBlue, gp);
        }
    }
}

This would produce:

Graphics Path - Adding Lines

A Rectangle from a Path

Introduction

To let you add a rectangle, the Graphics class provides an overloaded method named AddRectangle. One of its syntaxes is as follows:

public void AddRectangle(System.Drawing.Rectangle rect);

When calling this method, you can create a Rectangle object and pass it argument. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph  = e.Graphics;
            Pen pnRed       = new(Color.Red,  1.25f);
            Pen pnBlue      = new(Color.Blue, 5.25f);

            Rectangle rect  = new(40, 40, 565, 325);
            GraphicsPath gp = new GraphicsPath();

            gp.AddRectangle(rect);

            graph.DrawPath(pnBlue, gp);

            graph.DrawEllipse(pnRed, rect.X              - 15, rect.Y               - 15, 30, 30);
            graph.DrawEllipse(pnRed, rect.X + rect.Width - 15, rect.Y               - 15, 30, 30);
            graph.DrawEllipse(pnRed, rect.X + rect.Width - 15, rect.Y + rect.Height - 15, 30, 30);
            graph.DrawEllipse(pnRed, rect.X              - 15, rect.Y + rect.Height - 15, 30, 30);
        }
    }
}

This would produce:

Graphics Path - Rectangle

Adding Rectangles

You can keep calling the GraphicsPath.AddRectangle() method to draw as many rectangels as you want. Here is an example that draws two rectangles:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph    = e.Graphics;
            Pen pnRed         = new(Color.Red,  1.25f);
            Pen pnBlue        = new(Color.Blue, 5.25f);

            Rectangle rect1 = new(20, 20, 365, 125);
            Rectangle rect2 = new(20, 165, 650, 175);

            GraphicsPath gp   = new GraphicsPath();

            gp.AddRectangle(rect1);
            gp.AddRectangle(rect2);

            graph.DrawPath(pnBlue, gp);
        }
    }
}

This would produce:

Graphics Path - Adding Rectangles

Instead of drawing one rectangle at a time, to assist you in drawing a series of rectangles, the GraphicsPath provides an overloaded method named AddRectangles. One of its syntaxes is:

public void AddRectangles(System.Drawing.Rectangle[] rects);

When calling this method, pass an array of rectangles to it. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph    = e.Graphics;
            Pen pnBlue        = new(Color.Blue, 5.25f);

            Rectangle[] rects = { new(20, 20, 365, 125),
                                  new(20, 165, 650, 175) };
            GraphicsPath gp   = new GraphicsPath();

            gp.AddRectangles(rects);

            graph.DrawPath(pnBlue, gp);
        }
    }
}

The rectangles in the above method used integral values. To let you use more precise values, the Graphics class provides the following version of its AddRectangles() method:

public void AddRectangles(System.Drawing.RectangleF[] rects);

Adding a Polygon

To support polygons, the GraphicsPath class is equipped with an overloaded method named AddPolygon. One of its syntaxes is:

public void AddPolygon(System.Drawing.Point[] points);

This method expects an array of points as argument. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph = e.Graphics;
            GraphicsPath path = new GraphicsPath();
            Pen pnBlue = new(Color.Blue, 5.25f);

            Point[] Pts = { new Point( 22,  55),
                            new Point(324,  55),
                            new Point(324,  22),
                            new Point(525,  78),
                            new Point(324, 127),
                            new Point(324,  94),
                            new Point( 22,  94) };

            path.AddPolygon(Pts);
            
            graph.DrawPath(pnBlue, path);
        }
    }
}

This would produce:

Graphics Path - Adding a Polygon

Ellipses

Introduction

To support ellipses, the GraphicsPath class is equipped with an overloaded method named AddEllipse. One of its syntaxes is:

public void AddEllipse(System.Drawing.Rectangle rect);

This version of the GraphicsPath.AddEllipse() takes a rectangle as argument. The values of that rectangle must be natural numbers. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph    = e.Graphics;
            GraphicsPath path = new GraphicsPath();
            Pen pnRed         = new(Color.Red,  1.25f);
            Pen pnGreen       = new(Color.Green, 1.25f);
            Pen pnBlue        = new(Color.Blue, 5.25f);

            Rectangle rect = new(40,  40, 550,  260);
            path.AddEllipse(rect);

            graph.DrawPath(pnBlue, path);

            graph.DrawRectangle(pnGreen, rect);
            graph.DrawEllipse(pnRed, rect.X              - 15, rect.Y               - 15, 30, 30);
            graph.DrawEllipse(pnRed, rect.X + rect.Width - 15, rect.Y               - 15, 30, 30);
            graph.DrawEllipse(pnRed, rect.X + rect.Width - 15, rect.Y + rect.Height - 15, 30, 30);
            graph.DrawEllipse(pnRed, rect.X              - 15, rect.Y + rect.Height - 15, 30, 30);
        }
    }
}

This would produce:

Graphics Path - Add Arc

To let you draw the ellipse with more precise values, the GraphicsPath class provides the following version of the method:

public void AddEllipse (System.Drawing.RectangleF rect);

An Ellipse with a Size

Instead of defining the ellipse as belonging to an ellipse, you can explicitly specify its coordinates and size. To support this, the GraphicsPath class provides the following version of the method:

public void AddEllipse(int x, int y, int width, int height);

This version still follows the logic of an ellipse drawn inside a rectangle. This time, provide the (x, y) coordinates of the top-left corner of the rectangle. Then provide the width and the height of the ellipse. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph          = e.Graphics;
            GraphicsPath path       = new GraphicsPath();
            Pen pnGreen             = new(Color.Lime, 3.25f);
            Pen pnNavajoWhite       = new(Color.NavajoWhite, 15.25f);

            (int x, int y)          = ( 40,  40);
            (int width, int height) = (582, 269);

            BackColor               = Color.CadetBlue;
            path.AddEllipse(x, y, width, height);

            graph.DrawRectangle(pnGreen, x, y, width, height);

            graph.DrawPath(pnNavajoWhite, path);
        }
    }
}

This would produce:

Graphics Path - Adding an Ellipse

To let you use more precise values, the GraphicsPath class provides the following version of the GraphicsPath.AddEllipse() method:

public void AddEllipse(float x, float y, float width, float height);

Adding Ellipses

In the above examples, we drew the ellipses the same way it was done with the Graphics.DrawEllipse() method. WE saw that you can keep calling that mehthod to draw many ellipses. In the same way, to draw many circles or ellipses, you can call the GraphicsPath.AddEllipse() method many times. Of course, each call can use one of the versions we saw. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicsAccessories
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph = e.Graphics;
            GraphicsPath path = new GraphicsPath();
            Pen pnBlue = new(Color.MediumTurquoise, 15.25f);

            (int x, int y) = (40, 80);
            (int width, int height) = (550, 160);

            path.AddEllipse(x, y, width, height);

            Rectangle rect = new Rectangle(x + 115, y - 55, width - 255, height + 215);
            BackColor = Color.DarkSlateGray;
            
            path.AddEllipse(rect);

            graph.DrawPath(pnBlue, path);
        }
    }
}

This would produce:

Graphics Path - Adding Ellipses

Arcs

Introduction

As you know already, the .NET Framework supports the drawing of various types of curves. These operations are also available and enhanced when following a path.

Adding an Arc

To let you draw an arc, the GraphicsPath class provides an overloaded method named AddArc. One of its versions uses the following syntax:

public void AddArc(System.Drawing.Rectangle rect, float startAngle, float sweepAngle);

When calling this method, pass a rectangle that will contain the arce. Pass a second argument as the starting argument. Pass a third argument as the sweeping angle. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph  = e.Graphics;
            Pen pnRed       = new(Color.Red,  1.25f);
            Pen pnBlue      = new(Color.Blue, 5.25f);

            Rectangle rect  = new(20,  20, 550,  260);
            GraphicsPath gp = new GraphicsPath();

            gp.AddArc(rect, 15, 245);
            
            graph.DrawRectangle(pnRed, rect);

            graph.DrawPath(pnBlue, gp);
        }
    }
}

This would produce:

Graphics Path - Add Arc

Adding Arcs

If you need many arcs on a form, you can call the Graphics.DrawArc() method as many times as you want. With that method, each arc would be draw as a separate shape. Consider the following example:

private void Exercise_Paint(object sender, PaintEventArgs e)
{
    Graphics graph = e.Graphics;

    Pen pnRed = new(Color.Red, 1.88F);
    Pen pnMain = new(Color.Blue, 5.25f);

    float startAngle = 125.00F;
    float sweepAngle = 88.85F;

    Rectangle rect = new Rectangle(20, 20, 582, 275);

    graph.DrawArc(pen: pnMain, rect, startAngle, sweepAngle);
    
    startAngle = -42.00F;
    sweepAngle = 102.85F;
    pnMain.Color = Color.Green;
    graph.DrawArc(pen: pnMain, rect, startAngle, sweepAngle);
    
    graph.DrawRectangle(pnRed, rect);
}

This would produce:

Arc

The GraphicsPath class provides an alternative. It allows you to draw arcs that are connected. To proceed, provide a rectangle for each arc and call the GraphicsPath.AddArc() method to add each arc to the path. When the shape is drawn, the compiler would draw the first arc. For each subsequent arc, the compiler would use the ending point of the previous arc as the starting point of the next arc. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicsAccessories
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph = e.Graphics;
            GraphicsPath gp = new GraphicsPath();

            Pen pnRed = new(Color.Red, 1.88F);
            Pen pnMain = new(Color.Blue, 5.25f);

            float startAngle = 125.00F;
            float sweepAngle = 88.85F;

            Rectangle rect = new Rectangle(20, 20, 582, 275);

            gp.AddArc(rect, startAngle, sweepAngle);
            
            startAngle = -42.00F;
            sweepAngle = 102.85F;
            gp.AddArc(rect, startAngle, sweepAngle);

            graph.DrawPath(pen: pnMain, gp);

            graph.DrawRectangle(pnRed, rect);
        }
    }
}

This would produce:

Arc

Pies

A Pie in a Rectangle

To support pies, the GraphicsPath class is equipped with an overloaded method named AddPie. One of its syntax is:

public void AddPie(System.Drawing.Rectangle rect, float startAngle, float sweepAngle);

This method specifies the rectangle in which to draw the fractional ellipse. The startAngle argument specifies where to start the angle of the pie. The sweepAngle argument is the amount of angle the pie should cover. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph    = e.Graphics;
            GraphicsPath path = new GraphicsPath();
            Pen pnRed         = new(Color.Red,  1.25f);
            Pen pnGreen       = new(Color.Green, 1.25f);
            Pen pnBlue        = new(Color.Blue, 5.25f);

            Rectangle rect = new Rectangle(20, 20, 628, 248);
            path.AddPie(rect, 90F, 90F);

            graph.DrawPath(pnBlue, path);

            graph.DrawEllipse(pnRed, rect);
        }
    }
}

This would produce:

Arc

A Pie by Size

When adding a pie, you can define the rectangle that will contain the ellipse whose portion will constitute the pie. As an alternative, you can provide the oringin of the pie and its size. To support this, another version of the GraphicsPath.AddPie() method is available with the following syntax:

public void AddPie (int x, int y, int width, int height, float startAngle, float sweepAngle);

Here is an example of calling this method:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph = e.Graphics;
            GraphicsPath path = new GraphicsPath();
            Pen pnRed = new(Color.Red, 1.25f);
            Pen pnGreen = new(Color.Green, 1.25f);
            Pen pnBlue = new(Color.Blue, 5.25f);

            (int x, int y) = (20, 20);
            (int width, int height) = (550, 236);

            Rectangle rect = new Rectangle(x, y, width, height);
            path.AddPie(rect, -216F, 135F);

            graph.DrawPath(pnBlue, path);
            graph.DrawRectangle(pnRed, rect);
        }
    }
}

This would produce:

Arc

In the same way, you can call the GraphicsPath.AddPie() method many times to draw various pies. If you want pies to fit the same area, you can pass the same rectangle or coordinates to the method call. Here is an example:

using System.Drawing.Drawing2D;

namespace GraphicalApplication
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph = e.Graphics;
            GraphicsPath path = new GraphicsPath();
            Pen pnRed = new(Color.Red, 1.25f);
            Pen pnGreen = new(Color.Green, 1.25f);
            Pen pnBlue = new(Color.Blue, 5.25f);

            (int x, int y) = (24, 28);
            (int width, int height) = (550, 236);

            Rectangle rect = new Rectangle(x, y, width, height);

            path.AddPie(rect, 124.68F, 64.35F);
            path.AddPie(rect, -28F, 84F);

            graph.DrawPath(pnBlue, path);
            graph.DrawRectangle(pnRed, rect);
        }
    }
}

This would produce:

Arc

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2010-2024, FunctionX Friday 24 April 2024 Next