OverviewScheduleResourcesAssignmentsHome

CSC 122: Computer Science II, Fall 2006

HasCl Laboratory Exercise

By working through the steps in this handout, you will develop a HasCl program to draw a picture made up of many overlapping squares of different sizes and colors.

  1. The following data type, which is defined in the graphics module in Funnie, represents a graphical object to be displayed on the screen (in fact, this is a simplification--the full Shape type has several other constructors):
    data Shape = Poly([(Num, Num)], Color, Color) | Over(Shape, Shape) | Blank;
    
    This says that a Shape value is either a polygon, or the combination of one Shape over another, or nothing at all. A polygon needs three parameters--the value Poly(ps, c1, c2) describes a polygon whose vertices are given by the list ps, with border color c1 and interior color c2. Each point in the list of vertices is a pair (x, y) :: (Num, Num). The coordinate system of the graphics window (which pops up when you evaluate an expression of type Shape) initially has the point (0, 0) at the upper-left corner and (200, 200) at the lower-right, although this will change as you resize the window, pan around (click and drag the mouse), or zoom in and out (if you have a mouse with a scroll wheel). A color is specified as a value of the following data type, which is also defined in the graphics module:
    data Color = RGBA(Num, Num, Num, Num);
    
    The value RGBA(r, g, b, a) specifies a color with red, green, blue, and "alpha" components given by the numbers r, g, b, and a respectively, where each component ranges from 0 (completely absent) to 255 (full strength). The alpha level determines the transparency of the color, from 0 for completely transparent to 255 for completely opaque. The graphics module also defines some useful constants of type Color; for example, red is the value RGBA(255, 0, 0, 255) (look in the graphics module function browser in Funnie to see what other colors are defined). There is also a function RGB :: (Num, Num, Num) -> Color which makes it easier to create an opaque color given just the red, green, and blue components.
  2. In this lab, we will only be working with squares, so we will start by defining a function to draw a square given its location, size, and color. Open a new module window and enter the following in the source tab:
    import std.graphics;
    
    square(x, y, r, c) = Poly([(x-r, y-r), (x+r, y-r), (x+r, y+r), (x-r, y+r)], c, c);
            
    The import declaration is similar to the one in Java--it tells the compiler that we want to use the definitions from another module (by default, only definitions in the current module and std.base are available). This function will create a polygon with four vertices, giving us a square with center (x, y) and half-width r (like the radius of a circle--for our purposes, this will be a convenient way to specify a square). Both the border and the interior will be of color c.
  3. For this item, all we will do is display a single square. Using the function above, we can form a green square, 30 units on each side, with center at (50, 40), by using the expression square(50, 40, 15, RGB(0, 255, 0)). If you type this in, the system should pop-up a graphics window containing the square.

    For testing, it will be convenient to define some sample squares. Add each of the following to the source in your module window and compile:

    redGiant = square(70, 80, 50, RGB(255, 0, 0));
    whiteDwarf = square(20, 50, 10, RGB(255, 255, 255));
    purpleMedium = square(60, 40, 30, RGB(128, 0, 128));
    
    Now you can easily draw a square by entering its name, e.g., redGiant, in the module's expression window. However, this only lets you draw one square at a time. To combine several squares in one picture, we need the Over constructor of the Shape data type, as follows: Over(redGiant, purpleMedium). Notice how the squares are combined, and see what happens if you reverse the order of the squares. For convenience, you may also use the +++ operator to put one Shape over another: try whiteDwarf +++ purpleMedium +++ redGiant.
  4. Now let's display a list of shapes. We will need a function which takes a list of shapes and combines them all into a single Shape. As usual when working with a list of things, we will define the function by recursion. Here is the base case, which uses the special value Blank to produce a blank picture:
    showShapeList([ ]) = Blank;
    
    The recursive case will have the following form:
    showShapeList([s : ss]) =                    ;
    
    When this pattern matches, s will be the first shape on the list, and ss will be a list of the remaining shapes. Write an appropriate right-hand side for this case of the function. The shape s is already a Shape; you will need to use showShapeList to produce another Shape from ss. These two Shapes will then need to be combined into one with Over. When you have defined the function, try it out with showShapeList([redGiant, whiteDwarf]).
  5. We will also want a function to generate a list of squares, so that we don't have to type them all in by hand. Here is an example:
    diagonalSquares(x, y, r, c, 0) = [ ];
    diagonalSquares(x, y, r, c, n) = [square(x, y, r, c) :
                                        diagonalSquares(x+r, y+r, r, c, n-1)];
    
    After entering this function, evaluate showShapeList(diagonalSquares(30, 30, 10, blue, 9)). The cases for this function mean that diagonalSquares(x, y, r, c, n) will produce a list of n squares (because the list is empty when n is 0, and it gets one extra element for each recursive call as n counts down to 0). The first one will have its center at (x,y), with half-width r and color c. Succeeding squares will be offset by adding r to the x and y coordinates of the corner; the effect will be that each square will be centered on the lower-right corner of the previous one. You should get a picture that looks like a blue staircase.
  6. A simple modification to the previous code gives us a chain of squares of different sizes. Create a new function named vanishingSquares which is similar to diagonalSquares, except replace the size argument r in the recursive call (to vanishingSquares, of course) with the expression r*3/4. To try this out, you will probably want to start with a larger initial square; something like this should work: showSquareList(vanishingSquares(30, 30, 20, blue, 7))
  7. Now we can approach our original goal of creating an interesting picture by replacing the single recursive call in the previous step with four separate calls, one at each of the four corners of the initial square. Each of these recursive calls will produce a list of squares, so we will need to append all of the lists together, using ++. Here is a skeleton of the code for you to fill in:
    squareDesign(x, y, r, c, 0) =     ;
    squareDesign(x, y, r, c, n) = [square(x, y, r, c) :
                                     squareDesign(x-r, y-r, r/2, c, n-1) ++
                                     squareDesign(x+r, y-r, r/2, c, n-1) ++
                                     squareDesign(                     ) ++
                                     squareDesign(                     )];
    
    When you have successfully compiled the finished function, try evaluating the following expression: showShapeList(squareDesign(100, 100, 40, blue, 4)). You can change the 4 to 5 to draw one more level, but it will take too long if you try to do 6 or more levels (each level has four times as many squares, so there are 1024 squares at level 6).
  8. The last step will be to change the color as well as the size. Modify the recursive calls so that the upper-left and lower-right corners have only half the red component of the current square, while the upper-right and lower-left corners have only half the green component; leave the blue and alpha components untouched. The easiest way to do this is to write two auxilliary functions, lessRed and lessGreen, each of type (Color) -> Color. Here is one:
    lessRed(RGBA(r, g, b, a)) = RGBA(r/2, g, b, a);
    
    You will need to enter this and a similar definition for lessGreen, then write a new function similar to squareDesign that calls these functions in the appropriate places. Call this new function colorDesign, and test it (make sure the initial square has some red and green in it--for example, showShapeList(colorDesign(100, 100, 40, white, 5))).
    showShapeList(colorDesign(100, 100, 40, white, 5))
  9. As a final problem, how could you change the code to draw the larger squares under the smaller squares, instead of overlapping them? Whichever functions need to be modified, name the new version by appending a 2; for example, if you modify colorDesign, then your new function should be named colorDesign2.
OverviewScheduleResourcesAssignmentsHome

Valid HTML 4.01!Valid CSS!DePauw University, Computer Science Department, Fall 2006
Maintained by Brian Howard (bhoward@depauw.edu). Last updated