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 );
    }
}