You want to draw a rectangle with rounded corners, an offset, or rotation.

Create a custom *MovieClip.drawRectangle( )*
method using

the Drawing API and invoke it on a movie clip.

The *drawSimpleRectangle( )* method from Recipe 4.3 is, as the name suggests, quite simple.

Let's create a more complex version that also:

Draws a rectangle with a specified angle of rotation

Let's you specify the rectangle center's coordinates

Can draw a rectangle with rounded corners

The *drawRectangle( )* method accepts six
parameters:

*width*-
The width of the rectangle in pixels

*height*-
The height of the rectangle in pixels

*round*-
The radius (in pixels) of the arc that is used to round the corners. If the value is

`undefined`or 0, the corners remain square. *rotation*-
The clockwise rotation to apply to the rectangle in degrees. If

`undefined`, the rectangle is not rotated. *x*-
The x coordinate of the center point for the rectangle. If

`undefined`, the rectangle is centered at x = 0. *y*-
The y coordinate of the center point for the rectangle. If

`undefined`, the rectangle is centered at y = 0.

Here is our enhanced *drawRectangle( )* method,
defined on `MovieClip.prototype`, so
it's available to all movie clip instances:

// Include the custom Math library from Chapter 5 to access. #include "Math.as" MovieClip.prototype.drawRectangle = function (width, height, round, rotation, x, y) { // Make sure the rectangle is at least as wide and tall as the rounded corners. if (width < (round * 2)) { width = round * 2; } if (height < (round * 2)) { height = round * 2; } // Convert the rotation from degrees to radians. rotation = Math.degToRad(rotation); // Calculate the distance from the rectangle's center to one of the corners (or // where the corner would be in rounded-cornered rectangles). See the line labeled //Math.degToRad( )in Figure 4-2. var r = Math.sqrt(Math.pow(width/2, 2) + Math.pow(height/2, 2)); // Calculate the distance from the rectangle's center to the upper edge of the // bottom-right rounded corner. See the line labeledrin Figure 4-2. Whenrx// is 0,roundis equal torx. var rx = Math.sqrt(Math.pow(width/2, 2) + Math.pow((height/2) - round, 2)); // Calculate the distance from the rectangle's center to the lower edge of the // bottom-right rounded corner. See the line labeledrin Figure 4-2. Whenry// is 0,roundis equal tory. var ry = Math.sqrt(Math.pow((width/2) - round, 2) + Math.pow(height/2, 2)); // Calculate angles.ris the angle between the X axis that runs through the // center of the rectangle and the liner1Angle.rxis the angle betweenr2Angleandrx. //ris the angle betweenr3Angleandr. Andryis the angle betweenr4Angleand // the Y axis that runs through the center of the rectangle. var r1Angle = Math.atan( ((height/2) - round) /( width/2) ); var r2Angle = Math.atan( (height/2) / (width/2) ) - r1Angle; var r4Angle = Math.atan( ((width/2) - round) / (height/2) ); var r3Angle = (Math.PI/2) - r1Angle - r2Angle - r4Angle; // Calculate the distance of the control point from the // arc center for the rounded corners. var ctrlDist = Math.sqrt(2 * Math.pow(round, 2)); // Declare the local variables used to calculate the control point. var ctrlX, ctrlY; // Calculate where to begin drawing the first side segment and then draw it. rotation += r1Angle + r2Angle + r3Angle; var x1 = x + ry * Math.cos(rotation); var y1 = y + ry * Math.sin(rotation); this.moveTo(x1, y1); rotation += 2 * r4Angle; x1 = x + ry * Math.cos(rotation); y1 = y + ry * Math.sin(rotation); this.lineTo(x1, y1); // Setryto the starting point for the next side segment and calculate the x // and y coordinates. rotation += r3Angle + r2Angle; x1 = x + rx * Math.cos(rotation); y1 = y + rx * Math.sin(rotation); // If the corners are rounded, calculate the control point for the corner's curve // and draw it. if (round > 0) { ctrlX = x + r * Math.cos(rotation - r2Angle); ctrlY = y + r * Math.sin(rotation - r2Angle); this.curveTo(ctrlX, ctrlY, x1, y1); } // Calculate the end point of the second side segment and draw the line. rotation += 2 * r1Angle; x1 = x + rx * Math.cos(rotation); y1 = y + rx * Math.sin(rotation); this.lineTo(x1, y1); // Calculate the next line segment's starting point. rotation += r2Angle + r3Angle; x1 = x + ry * Math.cos(rotation); y1 = y + ry * Math.sin(rotation); // Draw the rounded corner, if applicable. if (round > 0) { ctrlX = x + r * Math.cos(rotation - r3Angle); ctrlY = y + r * Math.sin(rotation - r3Angle); this.curveTo(ctrlX, ctrlY, x1, y1); } // Calculate the end point of the third segment and draw the line. rotation += 2 * r4Angle; x1 = x + ry * Math.cos(rotation); y1 = y + ry * Math.sin(rotation); this.lineTo(x1, y1); // Calculate the starting point of the next segment. rotation += r3Angle + r2Angle; x1 = x + rx * Math.cos(rotation); y1 = y + rx * Math.sin(rotation); // If applicable, draw the rounded corner. if (round > 0) { ctrlX = x + r * Math.cos(rotation - r2Angle); ctrlY = y + r * Math.sin(rotation - r2Angle); this.curveTo(ctrlX, ctrlY, x1, y1); } // Calculate the end point for the fourth segment and draw it. rotation += 2 * r1Angle; x1 = x + rx * Math.cos(rotation); y1 = y + rx * Math.sin(rotation); this.lineTo(x1, y1); // Calculate the end point for the next corner arc and, if applicable, draw it. rotation += r3Angle + r2Angle; x1 = x + ry * Math.cos(rotation); y1 = y + ry * Math.sin(rotation); if (round > 0) { ctrlX = x + r * Math.cos(rotation - r3Angle); ctrlY = y + r * Math.sin(rotation - r3Angle); this.curveTo(ctrlX, ctrlY, x1, y1); } }rotation

Figure 4-2 shows the geometry when drawing the rounded corners for the rectangle.

The preceding example will be clearer with a closer examination.

The ActionScript trigonometric methods
require angles measured in radians.
Therefore, whenever you specify an angle in degrees (which is
generally easier for humans), you must convert the units to radians
before passing them to ActionScript's trigonometric
methods. In this case, we convert the `rotation`
parameter from degrees to radians using the *Math.degToRad(
)* method
from Recipe 5.12:

rotation = Math.degToRad(rotation);

The length of the three imaginary lines used for drawing the rounded corners, as shown in Figure 4-2, are calculated using the Pythagorean theorem, as discussed in Recipe 5.13. In our example, these distances are:

var r = Math.sqrt(Math.pow(width/2, 2) + Math.pow(height/2, 2)); var rx = Math.sqrt(Math.pow(width/2, 2) + Math.pow((height/2) - round, 2)); var ry = Math.sqrt(Math.pow((width/2) - round, 2) + Math.pow(height/2, 2));

Next, we must calculate the angles formed between the axes and the
lines `r`, `rx`, and
`ry`. These angles are used to determine the x and y
coordinates of the starting and ending side segments. If you know the
lengths of the sides of a right triangle, you can determine the
angles that they form. Because the axes and the lines
`r`, `rx`, and
`ry` can be formed into right triangles you can
determine the angles these lines form using the tangent and
arctangent. The tangent in a right triangle is defined as the ratio
of the side opposite the angle to the side adjacent to the angle. The
arctangent is the inverse of the tangent function, so we use the
following to determine the angles:

var r1Angle = Math.atan(((height/2) - round)/(width/2)); var r2Angle = Math.atan((height/2)/(width/2)) - r1Angle; var r4Angle = Math.atan(((width/2) - round)/(height/2)); var r3Angle = (Math.PI/2) - r1Angle - r2Angle - r4Angle;

The corners are each composed of a single curve that is a semicircle. To determine the distance between the semicircle's center point and the control point used to draw that curve, again use the Pythagorean theorem:

var ctrlDist = Math.sqrt(2 * Math.pow(round, 2));

The first thing you want to do when you draw the rectangle is to move
the imaginary pen to a starting point on the rectangle without
actually drawing a line. In this example, the calculated starting
point is at the right end of the bottom segment (of an unrotated
rectangle). If you know the distance between two points and the angle
(the opposite angle formed by an imaginary right triangle with the
known line being the hypotenuse), you can calculate the x and y
coordinates of the destination point using trigonometric functions.
The x coordinate is determined by the distance times the cosine of
the angle. The y coordinate is determined by the distance times the
sine of the angle. In this example the x and y coordinates
(`x1` and `y1`) are also offset by
the `x` and `y` parameters to draw
a rectangle whose center is not at (0, 0):

rotation += r1Angle + r2Angle + r3Angle; var x1 = x + ry * Math.cos(rotation); var y1 = y + ry * Math.sin(rotation); this.moveTo(x1, y1);

The remainder of the example follows the same pattern: draw a line,
draw a rounded corner (if applicable), and then move to the next side
segment. The new coordinates for each segment are calculated using
the same process as described previously. Once you have defined and
included the *drawRectangle( )* method in your
Flash document, you can quickly draw a rectangle within any movie
clip instance. Don't forget that you still need to
define the line style (see Recipe 4.1) before Flash will actually draw
anything.

// Create a new movie clip into which to draw the rectangle. this.createEmptyMovieClip("rectangle_mc", 1); // Define a 1-pixel, black, solid line style. rectangle_mc.lineStyle(1, 0x000000, 100); // Draw a rectangle with dimensions of 100 x 200. The rectangle has rounded corners // with radii of 10, and it is rotated 45 degrees clockwise. rectangle_mc.drawRectangle(100, 200, 10, 45);

You can draw a square by using the *drawRectangle(
)* method with equal height and width values:

this.createEmptyMovieClip("square_mc", 1); square_mc.lineStyle(1, 0x000000, 100); square_mc.drawRectangle(100, 100);

You can draw filled rectangles by invoking *beginFill(
)* or *beginGradientFill( )* before
*drawRectangle( )* and invoking *endFill(
)* after *drawRectangle( )*:

this.createEmptyMovieClip("filledRectangle_mc", 1); filledRectangle_mc.lineStyle(1, 0x000000, 100); // Define a black, 1-pixel border. filledRectangle_mc.beginFill(0x0000FF); // Define a solid blue fill. filledRectangle_mc.drawRectangle(100, 200); filledRectangle_mc.endFill( );

Recipe 4.3, Recipe 4.8, Recipe 4.9, Recipe 5.12, Recipe 5.13, and Recipe 5.14