Paths






Paths

In addition to using the basic shapes, you can compose and draw shapes together using a path. A path, modeled via the GraphicsPath class, is very much like a Graphics object, in that it's a logical container of zero or more shapes (called figures or subpaths). The main difference is that the figures can be started and ended arbitrarily.[7]

[7] Another important difference is that a Graphics object is backed by a surface such as a screen or a printer.

This means that you can compose one or more complicated figures from a set of basic shapes. You collect figures into a path so that you can frame or fill them as a unit using a single brush or pen, which is applied when the path is drawn. For example, Figure shows a rounded rectangle (a shape that the Graphics object can't draw for you directly).

20. A Rounded Rectangle Composed of Arc Figures in a GraphicsPath Object


Imagine a method called GetRoundedRectPath that takes a rectangle and the radius of an arc describing the curve. Calling the function returns a path, which can be filled and framed using the Graphics methods FillPath and DrawPath:

Graphics g = e.Graphics;
int width = this.ClientRectangle.Width;
int height = this.ClientRectangle.Height;
Rectangle rect = new Rectangle(10, 10, width - 20, height - 20);
 using( GraphicsPath path = GetRoundedRectPath(rect, width / 10) ) {
   g.FillPath(Brushes.Yellow, path);
   g.DrawPath(Pens.Black, path);
}

Even though the rounded rectangle path is composed of eight shapes (four arcs and four lines), the entire path is filled with one brush and framed with one pen. Here is the implementation of the method that composes the rounded rectangle:

GraphicsPath GetRoundedRectPath(Rectangle rect, int radius) {
  int diameter = 2 * radius;
  Rectangle arcRect =
    new Rectangle(rect.Location, new Size(diameter, diameter));

  GraphicsPath path = new GraphicsPath();

  // top left
  path.AddArc(arcRect, 180, 90);

  // top right
  arcRect.X = rect.Right - diameter;
  path.AddArc(arcRect, 270, 90);

  // bottom right
  arcRect.Y = rect.Bottom - diameter;
  path.AddArc(arcRect, 0, 90);

  // bottom left
  arcRect.X = rect.Left;
  path.AddArc(arcRect, 90, 90);

  path.CloseFigure();

  return path;
}

This function adds four arcs to the pathone at each of the corners of the rectangle. Each shape added to the path is filled or framed as appropriate when the path is drawn or filled. In fact, notice that no pen or brush is used to add each shape. The pen or brush is provided when the path is drawn, and not when the shapes are added.

Also, notice that none of the lines are added explicitly. The first three lines are added implicitly by the path itself. As each new unclosed shape is added, the starting point of the new shape is joined to the ending point of the last unclosed shape, creating a connected figure. After the last arc is added, we call the CloseFigure method to join the ending point of that arc to the starting point of the first arc. If CloseFigure had not been called, we'd still have a closed figure when the path was filled and framed, but the line connecting the top-left arc with the bottom-left arc would be missing. On the other hand, adding a closed shape, such as a rectangle or an ellipse, will close itself, so there's no need to call CloseFigure.

If, after calling CloseFigure, we were to add another shape, then another figure would be started for us implicitly. If you'd like to start a new figure without closing the current figure, you can do so by calling StartFigure. Figure shows what would happen if StartFigure were called after the second arc at the top right is added to the path. Notice that there would be two figures in the path, the first one unclosed because the second figure was started without closing the first.

21. Starting a New Figure in a Path Without Closing the Current Figure


Paths can add any of the shapes that the Graphics class can draw or fill. In fact, paths are handy because they can be used to create closed figures that aren't normally closed. For example, the following function returns a closed Bezier, another shape that the Graphics class doesn't provide directly:

GraphicsPath GetClosedBezierPath(Rectangle rect, Point[] points) {
   GraphicsPath path = new GraphicsPath();
   path.AddBeziers(points);
   path.CloseFigure();
   return path;
}

Fill Modes

When you compose a path of multiple figures that overlap, the overlap is subtractive by default. For example, the following code produces the donut in Figure:

22. Figures That Overlap Completely Act Subtractively


GraphicsPath GetDonutPath(Rectangle rect, int holeRadius) {
   GraphicsPath path = new GraphicsPath();
   path.AddEllipse(rect);
   Point centerPoint = new Point(...);
   Rectangle holeRect = new Rectangle(...);
   path.StartFigure(); // not needed: an ellipse will close itself
   path.AddEllipse(holeRect);
   return path;
}

However, notice that when the donut is resized, as in Figure, only the overlapping parts subtract from each other.

23. Overlapping Figures and the Alternate FillMode (See Plate 12)


This behavior is governed by the FillMode property on the GraphicsPath, of type FillMode. The FillMode enumeration has two values: Alternate and Winding. Alternate, the default, changes how shapes are filled by noticing when lines cross. Switching to Winding mode, in this case, would fill both circles, because Winding mode changes how shapes are filled based on a complicated scheme of line segment direction that wouldn't be invoked in our case. You can also set the FillMode on a polygon and a closed curve, but the default Alternate FillMode is the overwhelming favorite and is seldom changed.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows