Topics:
1. Tracing through the execution of a program
2. Completing the smiley face drawing
Completing the smiley face drawing (ellipses, pies, triangles)
The goal is to create the following drawing:
(click to enlarge)
Drawing the eye spots
To add the white eye spots we need to express their locations and size relative to
the facial features. We also need to decide where to make modifications in the code.
For each eye spot it is easier to work relative to its eye, instead of relative to
the center of the face.
From the diagram we see that a spot's vertical radius (we'll call it height) is equal to
half of the eye radius. No information is obvious about the spot's horizontal radius
(which we call width), so we arbitrarily decided that a spot's width is 40% of its height:
spotHeight = 0.5*eyeRadius
spotWidth = 0.4*spotHeight
The center of a spot is directly under the center of its eye, so their X coordinates are
the same. For the Y coordinate we need to move down from the eye level one spot height.
spotX = eyeX
spotY = eyeY + spotHeight (remember that height is actually the vertical radius)
The canvas has a procedure to draw an ellipse:
canvas.drawEllipse( <center X>, <center Y>, <radius in X>, <radius in Y>, <color> )
Here is the code for drawing the eye spots (each spot is drawn right after its eye):
void drawSmiley( double faceX, double faceY, double faceRadius )
{
// draw the face
canvas.drawCircle( faceX, faceY, faceRadius, "yellow" );
// draw the left eye
double eyeX = faceX + 0.3*faceRadius;
double eyeY = faceY - 0.3*faceRadius;
double eyeRadius = 0.2*faceRadius;
canvas.drawCircle( eyeX, eyeY, eyeRadius, "black" );
// draw the left highlight -- note we say spotHeight but mean spotVerticalRadius
// spotWidth but mean spotHorizontalRadius
double spotHeight = 0.5*eyeRadius;
double spotWidth = 0.4*spotHeight;
double spotX = eyeX;
double spotY = eyeY + spotHeight;
canvas.drawEllipse( spotX, spotY, spotWidth, spotHeight, "white" );
// draw the right eye -- only the x coordinate needs to be updated
eyeX = faceX - 0.3*faceRadius;
canvas.drawCircle( eyeX, eyeY, eyeRadius, "black" );
// draw the right highlight -- only the X needs to be updated, since
// the dimensions and Y-level are the same for the two spots
spotX = eyeX;
canvas.drawEllipse( spotX, spotY, spotWidth, spotHeight, "white" );
}
Drawing the mouth
The canvas does not have a procedure for drawing arcs, but it can draw "pie/pizza slices":
canvas.drawPie( <center X>, <center Y>, <radius>, <start Angle>, <pie Angle>, <color> )
We can create an arc by overlaying two "pie slices" -- a black slice and a slightly
smaller yellow slice:
(click to enlarge)
From the diagram we see that:
mouthX and mouthY = faceX and faceY (the "pie slices" are anchored at the nose)
mouthRadius = .67*faceRadius (from the original design)
mouthStart = 220 (a bit tricky)
mouthAngle ~ 100 degrees (visually seems a bit more than 90)
The start angle is a bit tricky. In order to reach the correct pizza slice we need to walk around the
whole top of the pizza (180 degrees) plus anther 40 degrees for the bottom side of the pizza.
Once we draw the black piece we can draw the yellow cover on top. Since the two pieces have to match
we can reuse most of the information for the yellow piece, except for a slightly smaller radius:
coverRadius = 0.8*mouthRadius (we estimate 80% of mouth radius)
void drawSmiley( double faceX, double faceY, double faceRadius )
{
...
...
...
// draw the mouth using two overlapping "pie slices"
double mouthX = faceX;
double mouthY = faceY;
double mouthRadius = 0.67*faceRadius;
double mouthStart = 220;
double mouthAngle = 100;
canvas.drawPie( mouthX, mouthY, mouthRadius, mouthStart, mouthAngle, "black" );
// draw the cover for the mouth -- reuse most of the mouth components
double coverRadius = 0.8*mouthRadius;
canvas.drawPie( mouthX, mouthY, coverRadius, mouthStart, mouthAngle, "yellow" );
}
Drawing the bow tie
For the bow tie we can use the canvas procedure for drawing triangles:
canvas.drawTriangle( <X1>, <Y1>, <X2>, <Y2>, <X3>, <Y3>, <color> )
No information is given about the tie dimensions, so we pick some reasonable estimates.
The width of the tie seems equal to the radius of the face; the height of the tie seems
to be half the width:
tieWidth = faceRadius
tieHeight = 0.5*tieWidth
The center/middle point of the bow tie is directly below the center of the face, so it
has the same X, but the Y is one face radius distance away from the nose:
midX = faceX
midY = faceY + faceRadius
To get to the left edge of the bow tie, we can start at the mid pint and then walk to
the left half of the width -- this gives the X coordinate of the left side of the tie.
For the Y level of the top/bottom points we need to go up/down half of tie height from
the Y level of the middle point.
topX = midX - 0.5*tieWidth go LEFT(-) from middle point HALF TIE WIDTH
botX = topX same X coordinate as top point
topY = midY - 0.5*tieHeight go UP(-) from middle point HALF TIE HEIGHT
botY = midY + 0.5*tieHeight go DOWN(+) from middle point HALF TIE HEIGHT
Once we draw the left triangle, we only need to re-compute the X coordinates for the
right edge (walk right instead of left) from the center/middle point. The Y levels of
the top/bottom points on the right edge are the same as the Y levels on the left edge,
so we don't recompute.
topX = midX - 0.5*tieWidth go RIGHT(+) from middle HALF TIE WIDTH
botX = topX same X coordinate as top point
Y coordinates remain the same
Here is the final code for the smiley with the two triangles drawn at the end:
public class SmileyApp
{
// draws a smiley face centered at the given "(x,y)" and of the given "radius"
// the face features are scaled relative to the face radius
void drawSmiley( double faceX, double faceY, double faceRadius )
{
// draw the face
canvas.drawCircle( faceX, faceY, faceRadius, "yellow" );
// draw the left eye
double eyeX = faceX + 0.3*faceRadius;
double eyeY = faceY - 0.3*faceRadius;
double eyeRadius = 0.2*faceRadius;
canvas.drawCircle( eyeX, eyeY, eyeRadius, "black" );
// draw the left highlight -- note we say spotHeight but mean spotVerticalRadius
// spotWidth but mean spotHorizontalRadius
double spotHeight = 0.5*eyeRadius;
double spotWidth = 0.4*spotHeight;
double spotX = eyeX;
double spotY = eyeY + spotHeight;
canvas.drawEllipse( spotX, spotY, spotWidth, spotHeight, "white" );
// draw the right eye and the spot -- only change the X coordinate
eyeX = faceX - 0.3*faceRadius;
spotX = eyeX;
canvas.drawCircle( eyeX, eyeY, eyeRadius, "black" );
canvas.drawEllipse( spotX, spotY, spotWidth, spotHeight, "white" );
// draw the mouth using two overlapping "pie slices"
double mouthX = faceX;
double mouthY = faceY;
double mouthRadius = 0.67*faceRadius;
double mouthAngle = 100;
double mouthStart = 220;
canvas.drawPie( mouthX, mouthY, mouthRadius, mouthStart, mouthAngle, "black" );
// draw the cover for the mouth
double coverRadius = 0.8*mouthRadius;
canvas.drawPie( mouthX, mouthY, coverRadius, mouthStart, mouthAngle, "yellow" );
// compute bow tie dimensions based on picture above
double tieWidth = faceRadius;
double tieHeight = 0.5*tieWidth;
// compute mid, top, and bottom points of left triangle of bow tie
double midX = faceX;
double midY = faceY + faceRadius;
// draw the left triangle
double topX = midX - 0.5*tieWidth;
double topY = midY - 0.5*tieHeight;
double botX = topX;
double botY = midY + 0.5*tieHeight;
canvas.drawTriangle( midX, midY, botX, botY, topX, topY, "blue" );
// compute points of right triangle of bow tie -- only X coordinate changes
topX = midX + 0.5*tieWidth;
botX = topX;
canvas.drawTriangle( midX, midY, botX, botY, topX, topY, "blue" );
}
public void run()
{
drawSmiley( 150, 200, 60 );
drawSmiley( 10, 20, 8 );
drawSmiley( 300, 350, 70 );
}
}