Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Marching Ants

0.00/5 (No votes)
23 Jul 2008 1  
Learn how to create marching ants easily and efficiently

Introduction

Marching ants are a common phenomenon in graphics programs that use them to show an area that has been selected by the user. Usually, this area can be a union of various shapes and even user-selected areas. This article will attempt to show you how to create your own marching ants program.

Background

It is preferable that you know the basics of GDI+. I will attempt to show everything that you need to know to create a working program, however.

Setting Up the Selected Area

The most important part of the technique is a pair of objects: a GraphicsPath object and a Region object. Region is found in System.Drawing, and GraphicsPath is found in System.Drawing.Drawing2D. Although it is not necessary, I created a separate class where I stored my GraphicsPath and Region.

The meat of the code is right here:

public void AddRectangle(Rectangle rectangle)
{
    _gp.AddRectangle(rectangle);
    _region.Union(_gp);
}

public void AddCircle(Rectangle rectangle)
{
    _gp.AddEllipse(rectangle);
    _region.Union(_gp);
}

public void AddFreeform(Point[] points)
{
    _gp.AddPolygon(points);
    _region.Union(_gp);
}

After initializing my GraphicsPath (_gp) and Region (_region), I have several methods for adding new shapes to them. The Region is necessary because it is the only class that can do a union of these various shapes.

Creating the Ants

The only other part of my code is the rendering code. To create the marching ants effect, I use a sneaky little trick. Normally, the GraphicsPath cannot do a union, so you would have lines from several different rectangles (instead of just the outside lines). I create a Bitmap and then fill in the center of the lines with the Region, which gets rid of all of the unnecessary lines. Then, I set the color I just filled in to be the transparent color for the Bitmap and draw it on my surface.

private void Canvas_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.DrawImage(_image, new Rectangle(0, 0, 
                         _image.Width, _image.Height));
    //draws your image

    e.Graphics.FillRegion(new SolidBrush(Color.FromArgb(128, 0, 0, 255)), 
                          _region);
    //fills the selected region with 
    //a semi-transparent blue for a nice effect

    Pen pen = new Pen(Color.White, 1);
    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
    pen.DashPattern = new float[2] { 3, 3 };
    //these lines set up a pen that will draw dashed white lines

    Bitmap ant = new Bitmap(CanvasPanel.Width, CanvasPanel.Height);
    Graphics g = Graphics.FromImage(ant);
    //creates a Graphics object from the Bitmap we will draw the ants on

    g.Clear(Color.Magenta);
    //makes the Bitmap Magenta so that the outsides will be transparent

    g.DrawPath(new Pen(Color.Black), _graphicsPath);
    g.DrawPath(pen, _graphicsPath);
    //draws a black outline (for underneath) 
    //and the dashed white line on top

    g.FillRegion(new SolidBrush(Color.Magenta), _region);
    //fills in the middle so the extra lines don't show

    ant.MakeTransparent(Color.Magenta);
    //makes the areas not covered by the marching ants transparent

    e.Graphics.DrawImageUnscaled(ant, 0, 0);
    //draw the ants on to the image
}

Fixing the Center

Because the GraphicsPath and the Region are the same size, drawing the Region inside the GraphicsPath actually obscures some of the ants. To fix this, we can use a handy function of the GraphicsPath class - Widen. I have found that it looks best to widen 1 or 2 pixels. Here is a function that I used to fix this:

public GraphicsPath Line()
{
    GraphicsPath gp = new GraphicsPath();
    if (_gp.PointCount > 0)                
    {        
        //I had to add this if statement so the program 
        //wouldn't crash if my GraphicsPath was still empty
        gp.AddPath(_graphicsPath, false);
        gp.Widen(new Pen(Color.White, 2));
    }
    return gp;
}

I used this function so that I wouldn't widen my original GraphicsPath object every frame or every time I added a new shape (this is an easy-to-make error that can quickly crash your program).

Animating the Ants

To animate the ants, I merely created a Timer and redrew the image at each tickCount. I am sure that there are more efficient ways to do this (such as invalidating the correct region), but for now this is simpler.

private void AntTimer_Tick(object sender, EventArgs e)
{
    _antOffset++;
    if (_antOffset > 50) _antOffset = 0;
    Canvas.Refresh();
}

Then, up in the OnPaint section of the code, you need to add a line that takes this offset into effect:

pen.DashOffset = _antOffset;

Placing this code with the other code that sets the properties for your pen should give you a fully working marching ants program.

Points of Interest

Many thanks to the authors of The Secret of Marching Ants and Marching Ants Revisited. Their programs gave me some great ideas, although I think that my program is more efficient and allows for more complex paths.

Other

This is my first article, please give me feedback. :-)

History

  • 7/14/2008 - Original upload
  • 7/23/2008 - Source updated

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here