Chapter 19 - Graphics with GDI+ | |
bySimon Robinsonet al. | |
Wrox Press 2002 | |
In our last example, we encountered the base struct, Rectangle, which is used to represent the coordinates of a rectangle. GDI+ actually uses several similar structures to represent coordinates or areas, and we're at a convenient point in the chapter to go over the main ones. We'll look at the following structs, which are all defined in the System.Drawing namespace:
Struct | Main Public Properties |
---|---|
struct Point | X, Y |
struct PointF | |
struct Size | Width, Height |
struct SizeF | |
struct Rectangle | Left, Right, Top, Bottom, Width, Height, X, Y, Location, Size |
struct RectangleF |
Note that many of these objects have a number of other properties, methods , or operator overloads not listed here. In this section we'll just discuss the most important ones.
We'll look at Point first. Point is conceptually the simplest of these structs. Mathematically, it's completely equivalent to a 2D vector. It contains two public integer properties, which represent how far you move horizontally and vertically from a particular location (perhaps on the screen). In other words, look at this diagram:
In order to get from point A to point B, you move 20 units across and 10 units down, marked as x and y on the diagram as this is how they are commonly referred to. We could create a Point struct that represents that as follows :
Point ab = new Point(20, 10); Console.WriteLine("Moved {0} across, {1} down", ab.X, ab.Y);
X and Y are read-write properties, which means you can also set the values in a Point like this:
Point ab = new Point(); ab.X = 20; ab.Y = 10; Console.WriteLine("Moved {0} across, {1} down", ab.X, ab.Y);
Note that although conventionally horizontal and vertical coordinates are referred to as x and y coordinates (lowercase), the corresponding Point properties are X and Y (uppercase) because the usual convention in C# is for public properties to have names that start with an uppercase letter.
PointF is essentially identical to Point , except that X and Y are of type float instead of int . PointF is used when the coordinates are not necessarily integer values. A cast has been defined so that you can implicitly convert from Point to PointF . (Note that because Point and PointF are structs, this cast involves actually making a copy of the data). There is no corresponding reverse case to convert from PointF to Point you have to explicitly copy the values across, or use one of three conversion methods, Round() , Truncate(), and Ceiling() :
PointF abFloat = new PointF(20.5F, 10.9F); // converting to Point Point ab = new Point(); ab.X = (int)abFloat.X; ab.Y = (int)abFloat.Y; Point ab1 = Point.Round(abFloat); Point ab2 = Point.Truncate(abFloat); Point ab3 = Point.Ceiling(abFloat); // but conversion back to PointF is implicit PointF abFloat2 = ab;
You may be wondering what a "unit" is measured in. By default, GDI+ will interpret units as pixels along the screen (or printer, whatever the graphics device is) so that's how the Graphics object methods will view any coordinates that they get passed as parameters. For example, the point new Point(20,10) represents 20 pixels across the screen and 10 pixels down. Usually these pixels will be measured from the top left corner of the client area of the window, as has been the case in our examples up to now. However, that won't always be the case for example, on some occasions you may wish to draw relative to the top left corner of the whole window (including its border), or even to the top left corner of the screen. In most cases, however, unless the documentation tells you otherwise , you can assume you're talking pixels relative to the top left corner of the client area.
We'll have more to say on this subject later on, after we've examined scrolling, when we mention the three different coordinate systems in use, world, page, and device coordinates.
Like Point and PointF , sizes come in two varieties. The Size struct is for when you are using int s; SizeF is available if you need to use float s. Otherwise Size and SizeF are identical. We'll focus on the Size struct here.
In many ways the Size struct is identical to the Point struct. It has two integer properties that represent a distance horizontally and a distance vertically the main difference is that instead of X and Y , these properties are named Width and Height . We can represent our earlier diagram by:
Size ab = new Size(20,10); Console.WriteLine("Moved {0} across, {1} down", ab.Width, ab.Height);
Although strictly speaking, a Size mathematically represents exactly the same thing as a Point; conceptually it is intended to be used in a slightly different way. A Point is used when we are talking about where something is, and a Size is used when we are talking about how big it is. However, because Size and Point are so closely related , there are even supported explicit conversions between these two:
Point point = new Point(20, 10); Size size = (Size) point; Point anotherPoint = (Point) size;
As an example, think about the rectangle we drew earlier, with top left coordinate (0,0) and size (50,50). The size of this rectangle is (50,50) and might be represented by a Size instance. The bottom right corner is also at (50,50), but that would be represented by a Point instance. To see the difference, suppose we drew the rectangle in a different location, so it's top left coordinate was at (10,10):
dc.DrawRectangle(bluePen, 10,10,50,50);
Now the bottom right corner is at coordinate (60,60), but the size is unchanged that's still (50,50).
The addition operator has been overloaded for Point s and Size s, so that it is possible to add a Size to a Point giving another Point :
static void Main(string[] args) { Point topLeft = new Point(10,10); Size rectangleSize = new Size(50,50); Point bottomRight = topLeft + rectangleSize; Console.WriteLine("topLeft = " + topLeft); Console.WriteLine("bottomRight = " + bottomRight); Console.WriteLine("Size = " + rectangleSize); }
This code, running as a simple console application, called PointsAndSizes , produces this output:
Notice that this output also shows how the ToString() method has been overridden in both Point and Size to display the value in { X,Y } format.
It is also possible to subtract a Size from a Point to give a Point , and you can add two Sizes together, giving another Size . It is not possible, however, to add a Point to another Point . Microsoft decided that adding Point s doesn't conceptually make sense, and so chose not to supply any overload to the + operator that would have allowed that.
You can also explicitly cast a Point to a Size and vice versa:
Point topLeft = new Point(10,10); Size s1 = (Size)topLeft; Point p1 = (Point)s1;
With this cast s1.Width is assigned the value of topLeft.X , and s1.Height is assigned the value of topLeft.Y . Hence, s1 contains (10,10). p1 will end up storing the same values as topLeft .
These structures represent a rectangular region (usually of the screen). Just as with Point and Size , we'll only consider the Rectangle struct here. RectangleF is basically identical except that those of its properties that represent dimensions all use float , whereas those of Rectangle use int .
A Rectangle can be thought of as composed of a point, representing the top left corner of the rectangle, and a Size , which represents how large it is. One of its constructors actually takes a Point and a Size as its parameters. We can see this by rewriting our earlier code from the DrawShapes sample that draws a rectangle:
Graphics dc = e.Graphics; Pen bluePen = new Pen(Color.Blue, 3); Point topLeft = new Point(0,0); Size howBig = new Size(50,50); Rectangle rectangleArea = new Rectangle(topLeft, howBig); dc.DrawRectangle(bluePen, rectangleArea);
This code also uses an alternative override of Graphics.DrawRectangle() , which takes a Pen and a Rectangle struct as its parameters.
You can also construct a Rectangle by supplying the top left horizontal coordinate, top left vertical coordinate, width, and height separately, and in that order, as individual numbers :
Rectangle rectangleArea = new Rectangle(0, 0, 50, 50);
Rectangle makes quite a few read-write properties available to set or extract its dimensions in different combinations:
Property | Description |
---|---|
int Left | x-coordinate of left-hand edge |
int Right | x-coordinate of right-hand edge |
int Top | y-coordinate of top |
int Bottom | y-coordinate of bottom |
int X | same as Left |
int Y | same as Top |
int Width | width of rectangle |
int Height | height of rectangle |
Point Location | top left corner |
Size Size | size of rectangle |
Note that these properties are not all independent for example setting Width will also affect the value of Right .
We'll mention the existence of the System.Drawing.Region class here, though we don't have space to go details in this book. Region represents an area of the screen that has some complex shape. For example the shaded area in the diagram could be represented by Region :
As you can imagine, the process of initializing a Region instance is itself quite complex. Broadly speaking, you can do it by indicating either what component simple shapes make up the region or what path you take as you trace round the edge of the region. If you do need to start working with areas like this, then it's worth looking up the Region class.