By working through the steps in this handout, you will develop a Haskell program to draw a picture made up of many overlapping squares of different sizes and colors.
data Square = Square Point Int RGBThis says that a
Square
will be specified by a
Point
(the center), an Int
(the
distance from the center to any of the sides, i.e., half the length of a side), and a color
expressed as red, green, and blue components. The type
Int
is one of the built-in types (it is a variant of
Integer
that gives up the very large values in
exchange for greater efficiency); each of the other types is
already defined in the Graphics Library, so we just need to know
how to use them.
Create a Haskell script file named project2.hs
in
your directory on the I:
drive. It should start with
the following lines:
-- CSC122, Project 2 -- <your name> -- <the date> import SOEGraphics data Square = Square Point Int RGBNow
:load
your script into Hugs. For this item, all
we will do is construct a Square
object. To use the
Square
constructor function, we need a
Point
, an Int
, and an RGB
.
A Point
is simply a pair of integers, so we can use
something like (200, 100)
. An Int
is
just a value like 50
. An RGB
needs
three integers in the range 0 to 255; to get white, we can use
the RGB
constructor as follows: RGB 255 255
255
. Putting all this together, we can form a white
square, 100 units on each side, centered on the point (200, 100), by using the expression
Square (200, 100) 50 (RGB 255 255 255)
. If you type
this in, the system will complain that it cannot find an
appropriate ``show'' function. This is OK; we can still use the
Square
type in a program, and we can check that we
are using it correctly by only requesting the type of the above
expression: :type Square (200, 100) 50 (RGB 255 255
255)
.Square
objects. The Haskell Graphics library
provides a function polygon
which takes a list of
Point
s and produces a filled-in shape with the given
points as vertices. We will write a convenience function
fillSquare
which takes a Square
and
makes the appropriate call to polygon
:
fillSquare (Square (x,y) d rgb) = withRGB rgb (polygon [(x-d,y-d), (x-d,y+d), (x+d,y+d), (x+d,y-d)])As usual, we write the function to match its argument against a pattern so that the variables in the pattern will be bound to the corresponding values for the
Square
we wish to draw.
The center point will be (x,y)
, the distance from
the center to each side will be d
, and the color
will be rgb
. We call the polygon
function with an appropriate list of points for the four corners
of the square, and pass the result to the withRGB
function to apply a color to the shape. Add the above function
definition to your script. If you try to apply
fillSquare
to the square mentioned previously,
fillSquare (Square (200,100) 50 (RGB 255 255 255))
,
the system should again complain about the lack of an appropriate
``show'' function. The result is of the type Draw
()
, which the system might also call Graphic
.
The square has not been drawn yet, but this Graphic
value packages up all the information that the Graphics Library
will need to display it. The next step will do so.showGraphic g = runGraphics $ do { w <- openWindow "Graphics Window" (400,300); drawInWindow w g; getKey w; closeWindow w }Add this to your script, load it, and try applying
showGraphic
to the result of various calls to
fillSquare
. Each time you do, it should create a
graphics window, display a filled square, and wait for you to hit
any key before closing the window. For testing, it will be
convenient to define some sample squares. Add some lines like the
following to your script and reload:
redGiant = Square (150,150) 100 (RGB 255 0 0) whiteDwarf = Square (250,250) 25 (RGB 255 255 255)Now you can easily try drawing squares by entering
showGraphic (fillSquare redGiant)
, etc., at the
Main>
prompt in Hugs. However, this only lets you
draw one square at a time. To combine several squares in one
picture, we need the overGraphic
function. It will
be easiest to use this as an operator, as follows:
showGraphic ((fillSquare redGiant) `overGraphic`
(fillSquare whiteDwarf))
. Notice how the squares are
combined, and see what happens if you reverse the order of the
squares.Square
s. We will need a
function which takes a list of squares, fills each one, and
combines them all into a single Graphic
. 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
emptyGraphic
to produce a blank picture:
fillSquareList [] = emptyGraphicThe recursive case will have the following form:
fillSquareList (s : ss) =When this pattern matches,
s
will be the first
square on the list, and ss
will be a list of the
remaining squares. Write an appropriate right-hand side for this
case of the function. You will need to use
fillSquare
to produce a Graphic
from
s
, and fillSquareList
to produce
another Graphic
from ss
; then these two
Graphic
s will need to be combined into one with
overGraphic
. When you have added the function to
your script, try it out with showGraphic (fillSquareList
[redGiant, whiteDwarf])
.diagonalSquares (x,y) d rgb 0 = [] diagonalSquares (x,y) d rgb n = Square (x,y) d rgb : diagonalSquares (x+d,y+d) d rgb (n-1)Try this:
showGraphic (fillSquareList (diagonalSquares
(50,50) 25 (RGB 255 255 255) 9))
. The cases for this
function mean that diagonalSquares (x,y) d rgb 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 be centered at
(x,y)
, with dimension d
and color
rgb
. Succeeding squares will be offset by adding
d
to the x
and y
coordinates of the center; 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 white staircase.vanishingSquares
which is similar to
diagonalSquares
except replace the argument
d
in the recursive call (to
vanishingSquares
, of course) with the expression
(d `quot` 2)
. To try this out, you will probably
want to start with a larger initial square; something like this
should work: showGraphic (fillSquareList (vanishingSquares
(200,100) 80 (RGB 255 255 255) 7))
++
. Here is a skeleton of the
code for you to fill in:
squareDesign (x,y) d rgb 0 = squareDesign (x,y) d rgb n = Square (x,y) d rgb : (squareDesign (x-d,y-d) (d `quot` 2) rgb (n-1) ++ squareDesign (x-d,y+d) (d `quot` 2) rgb (n-1) ++ squareDesign ++ squareDesign )When you have the finished function saved in your script file, load it into Hugs and try the following:
showGraphic
(fillSquareList (squareDesign (200,150) 70 (RGB 255 255 255)
5))
5
to 6
to draw one
more level, but it will probably crash if you try to do 7 or more
levels (each level has four times as many squares, so there are
4096 squares at level 7).(RGB r g b)
instead of the
simple variable rgb
. Call the new function
colorDesign
.2
; for example, if you modify
colorDesign
, then your new function should be named
colorDesign2
.When you are done, make sure your script file with all of the
above code is saved in your folder on the I:
drive.