Paths are built from lines, arcs, curves, and rectangles. Although a rectangle can be constructed using four lines, this shape is used so frequently that Quartz provides functions that allow you to create a rectangle in one step. Points are also essential building blocks of paths because points define starting and ending locations of shapes.
Points
Lines
Arcs
Curves
Ellipses
Rectangles
Points are x- and y-coordinates that specify a location in user space. You can call the function CGContextMoveToPoint to specify a starting location when you build a path. Quartz keeps track of the current point, which is the last location used for path construction. For example, if you call the function CGContextMoveToPoint to set a location at (10, 10), then you draw a horizontal line 50 units long, the last point on the line, that is, (60, 10), becomes the current point. Lines, arcs, and curves are always drawn starting from the current point.
Most of the time you specify a point by passing to Quartz functions two floating-point values to specify x- and y-coordinates. Some functions require that you pass a CGPoint data structure, which holds two floating-point values.
A line is defined by its endpoints. Its starting point is always assumed to be the current point, so when you create a line, you specify only its endpoint. You use the function CGContextAddLineToPoint to append a single line to a path.
You can add a series of connected lines to a path by calling the function CGContextAddLines. You pass this function an array of points. The first point must be the starting point of the first line; the remaining points are endpoints. Quartz connects each point in the array with the next point in the array, using straight line segments.
Arcs are circle segments. Quartz provides two functions that create arcs. The function CGContextAddArc creates a curved segment from a circle. You specify the center of the circle, the radius, and the radial angle (in radians). You can create a full circle by specifying a radial angle of 2 pi. Figure 3-4 shows multiple paths drawn independently. Each path contains a randomly generated circle; some are filled and others are stroked.
The function CGContextAddArcToPoint is ideal to use when you want to round the corners of a rectangle. Quartz uses the endpoints you supply to create two tangent lines. You also supply the radius of the circle from which Quartz slices the arc. The center point of the arc is the intersection of two radii, each of which is perpendicular to one of the two tangent lines. Each endpoint of the arc is a tangent point on one of the tangent lines, as shown in Figure 3-5. The red portion of the circle is what’s actually drawn.
If the current path already contains a subpath, Quartz appends a straight line segment from the current point to the starting point of the arc. If the current path is empty, Quartz creates a new subpath for the arc and does not add the initial straight line segment.
Quadratic and cubic Bézier curves are algebraic curves that can specify any number of interesting curvilinear shapes. Points on these curves are calculated by applying a polynomial formula to starting and ending points, and one or more control points. Shapes defined in this way are the basis for vector graphics. A formula is much more compact to store than an array of bits and has the advantage that the curve can be recreated at any resolution.
Figure 3-6 shows a variety of curves created by drawing multiple paths independently. Each path contains a randomly generated curve; some are filled and others are stroked.
The polynomial formulas that give to rise to quadratic and cubic Bézier curves, and the details on how to generate the curves from the formulas, are discussed in many mathematics texts and online sources that describe computer graphics. These details are not discussed here.
You use the function CGContextAddCurveToPoint to append a cubic Bézier curve from the current point, using control points and an endpoint you specify. Figure 3-7 shows the cubic Bézier curve that results from the current point, control points, and endpoint shown in the figure. The placement of the two control points determines the geometry of the curve. If the control points are both above the starting and ending points, the curve arches upward. If the control points are both below the starting and ending points, the curve arches downward. If the second control point is closer to the current point (starting point) than the first control point, the curve crosses over itself, creating a loop.
You can append a quadratic Bézier curve from the current point by calling the function CGContextAddQuadCurveToPoint, and specifying a control point and an endpoint. Figure 3-8 shows two curves that result from using the same endpoints but different control points. The control point determines the direction that the curve arches. It’s not possible to create as many interesting shapes with a quadratic Bézier curve as you can with a cubic one because quadratic curves use only one control point. For example, it’s not possible to create a crossover using a single control point.
An ellipse is essentially a squashed circle. You create one by defining two focus points and then plotting all the points that lie at a distance such that adding the distance from any point on the ellipse to one focus to the distance from that same point to the other focus point is always the same value. Figure 3-9 shows multiple paths drawn independently. Each path contains a randomly generated ellipse; some are filled and others are stroked.
In Mac OS X v10.4 and later, you can add an ellipse to the current path by calling the function CGContextAddEllipseInRect. You supply a rectangle that defines the bounds of the ellipse. Quartz approximates the ellipse using a sequence of Bézier curves. The center of the ellipse is the center of the rectangle. If the width and height of the rectangle are equal (that is, a square), the ellipse is circular, with a radius equal to one-half the width (or height) of the rectangle. If the width and height of the rectangle are unequal, they define the major and minor axes of the ellipse.
Ellipse drawing starts with a move-to operation and ends with a close-subpath operation, with all moves oriented in the clockwise direction.
If your application runs in versions of Mac OS X earlier than v10.3 you can create an ellipse as shown in Listing 3-1. First, you apply a transform to the context. Then, you call the function CGContextAddArc, passing 0 as the starting angle and 2 pi as the ending angle, which would normally result in a circle. Because the code in the listing scales the drawing destination so it’s twice as width as it is high, drawing a circle into the transformed context, results in an ellipse. See “Transforms” for more information on setting up and using affine transforms.
Listing 3-1 Code that creates an ellipse by applying a transform to a circle
CGContextScaleCTM(context, 1,2); |
CGContextBeginPath(context); |
CGContextAddArc(context, 0, 0, 25, 0, 2*M_PI, false); |
CGContextStrokePath(context); |
You can add a rectangle to the current path by calling the function CGContextAddRect. You supply a CGRect structure that contains the origin of the rectangle and its width and height. Quartz draws the rectangle at the origin you specify; the current point has no bearing on the placement of the rectangle. Unlike arcs, if the path already contains a subpath, Quartz does not append a line segment from the current point to the origin of the rectangle.
You can add many rectangles to the current path by calling the function CGContextAddRects and supplying an array of CGRect structures. Figure 3-10 shows multiple paths drawn independently. Each path contains a randomly generated rectangle; some are filled and others are stroked.
Last updated: 2007-12-11