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 difference (in addition to the fact that a Graphics object is backed by a surface such as a screen or a printer) is that the figures can be started and ended arbitrarily. 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 4.19 shows a rounded rectangle (a shape that the Graphics object can't draw for you directly).

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

Imagine a method called GetRoundedRectPath that takes a rectangle and a 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 FramePath:

 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 path ”one at each of the corners of the rectangle. Each shape added to the path will be 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, not when the shapes are added.

Also, notice that none of the lines is 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 4.20 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.

Figure 4.20. 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, by default the overlap will be subtractive . For example, the following code produces the donut in Figure 4.21:

 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 because an ellipse will close itself  path.AddEllipse(holeRect);  return path; } 
Figure 4.21. Figures That Overlap Completely Act Subtractively

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

Figure 4.22. Overlapping Figures and the Alternate FillMode

This behavior is governed by the FillMode property on the Path, 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.



Windows Forms Programming in C#
Windows Forms Programming in C#
ISBN: 0321116208
EAN: 2147483647
Year: 2003
Pages: 136
Authors: Chris Sells

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net