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));
e.Graphics.FillRegion(new SolidBrush(Color.FromArgb(128, 0, 0, 255)),
_region);
Pen pen = new Pen(Color.White, 1);
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
pen.DashPattern = new float[2] { 3, 3 };
Bitmap ant = new Bitmap(CanvasPanel.Width, CanvasPanel.Height);
Graphics g = Graphics.FromImage(ant);
g.Clear(Color.Magenta);
g.DrawPath(new Pen(Color.Black), _graphicsPath);
g.DrawPath(pen, _graphicsPath);
g.FillRegion(new SolidBrush(Color.Magenta), _region);
ant.MakeTransparent(Color.Magenta);
e.Graphics.DrawImageUnscaled(ant, 0, 0);
}
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)
{
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