oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button Programming With Cocoa

Introduction to Cocoa Graphics, Part 2


Today's column is part two of our push to learn 2D graphics in Cocoa. In the last column, I covered how NSView and NSBezierPath work together to let us draw simple objects, and I discussed how to use some of the Foundation Framework’s fundamental data types in drawing. This allowed us to make ellipses and rectangles using NSBezierPath.

Now I'll build upon these concepts to draw arbitrary paths that are constructed from straight lines and curves, which allow us to make complex shapes.

Today's initial setup will be identical to that of the previous column (except for different filenames, of course). Start by creating a new project called Curves (or whatever you like), and add to it the files for a subclass of NSView called CurvesView (or, again, whatever suits your fancy). After that, go through the steps that we went through in the last column to connect CurvesView to an NSView container in Interface Builder. When you have completed these essential first steps, you’ll be ready to continue with this project.

Before I delve into the mechanics of NSBezierPath, allow me to indulge in a tiny bit of history about Bezier paths. Bezier paths (or curves) were developed by the French mathematician and automobile engineer Pierre Bezier (surprise!) in the 1970s. They were originally developed for CAD/CAM systems to help design and prototype Renault car bodies, and thus they made their way into computer graphics.

We’ve all had some contact with Bezier curves, as they are the underpinnings of Adobe’s Postscript drawing system. One of the reasons that Bezier curves are used extensively in computer graphics is because of their mathematical nature; Bezier curves are described by equations. Shapes drawn using Bezier curves can be magnified without any loss of sharpness or increase in granularity, as is common with bitmap images.

That's enough history for now. Let's get back to work.

Constructing paths

Bezier paths are built using a small set of construction methods such as –moveToPoint:, -lineToPoint:, and –curveToPoint:controlPoint1:controlPoint2: (more on this method to come).

When you construct paths, keep in mind the idea of the current point. The current point is the ending point of the last element you added, and the starting point of your next path segment. Essentially, the current point is the most recent point you used in constructing your path. This explains why –lineToPoint: only takes one NSPoint argument, when we know full well that two points define a line. For example, if your current point was (100, 100), then the code would create a vertical line:

[aPath lineToPoint:NSMakePoint(100, 300)];

It is understood that path segments will begin at the current point, and extend to the point indicated in the argument. Using the –moveToPoint: method, we can change the location of the current point without adding to the path. This is useful for defining the starting point of a freshly instantiated path, or for simply "lifting your pencil" in the middle of constructing a path.

Let’s consider the example of drawing a simple triangle to illustrate these ideas. The way we’ll do this is to first define three points that will be the vertices of the triangle; then create a new, empty instance of NSBezierPath; construct the path of our triangle using the three points we've defined; and finally, draw it on screen. All of this will be written in the –drawRect method of CurvesView:

- (void)drawRect:(NSRect)rect
    // The three vertices of the triangle
    NSPoint p1 = NSMakePoint(100, 100);
    NSPoint p2 = NSMakePoint(200, 300);
    NSPoint p3 = NSMakePoint(300, 100);

    // Constructing the path
    NSBezierPath *triangle = [NSBezierPath bezierPath];
    [triangle moveToPoint:p1];
    [triangle lineToPoint:p2];
    [triangle lineToPoint:p3];
    [triangle lineToPoint:p1];

    // Draw the path
    [[NSColor blueColor] set];
    [triangle stroke];	// or [triangle fill];

Comment on this articleNow that we know you can create graphics in Cocoa, let's discuss how we can put these tools to use.
Post your comments

As you can see, we first defined the three vertices of the triangle, and then we started our path construction by instantiating NSBezierPath, assigning the variable triangle to this object, and then “moving” to the first point—putting our pencil on the paper. From there we added the three lines connecting the vertices, set the color, and finally told the triangle to draw itself onto the screen.

NSBezierPath has a method called –closePath that appends a path element to your path that is a straight line from the current point to the starting point of your path — it connects the beginning and end of your path with a line, thereby closing it. As we’ll see in a moment, it’s a good idea to close your path (if your path is indeed supposed to be a closed path) when you’ve finished. We could have used –closePath above to finish our triangle by replacing the last line's construction code

[triangle lineToPoint:p1];

with a –closePath message to triangle:

[triangle closePath];

Both of these messages to triangle have the effect of adding a straight line from p3 to p1. The second message, using –closePath, has the effect of actually joining the endpoint to the starting point of the path rather than creating the appearance of a closed path, as is done in the first method. In a moment, we’ll see a more annoying consequence of not using –closePath.

Pages: 1, 2, 3

Next Pagearrow