PSICS Fall 2004 Quiz #4

Quiz #4 - 9/30/2004

List based animations

Submit your solutions to this quiz by dropping in your webct drop box in the box labeled "Quiz4". Let Dave know if you have problems submitting!

You can get all the code here: code.scm, also available as HTML here: code.scm

Overview: using lists to create and sdisplay animations

This quiz involves the development of scheme code to generate lists that describe an animation. Each list item is termed an animation entity and describes a simple operation like "clear a circle", "draw a square", "wait a second". You also need to develop code to create an animation by playing back a list of these animation entities.

You are provided with definitions structures that represent 5 different simple animation instructions. These are collectively referred to as animation entities in this document. Below are detailed descriptions (and the actual code) for these structures:

Circles

(define-struct circle (center radius color))

There are two kinds of circle animation entities, these correspond to drawing a circle (the outline only) and clearing a circle. Both are represented using the structure definition shown above. The inidividual fields are described below:

To create an animation entity that indicates that a circle should be drawn at 100,100 with radius 20 and color red you would do this:

(make-circle (make-posn 100 100) 20 'Red)

To create an animation entity that indicates that a circle should be cleared at 100,100 with radius 20 you would do this:

(make-circle (make-posn 100 100) 20 'Clear)

This structure definition is probably very similar to what you came up with on an earlier exercise, although this has the addition of the special color 'Clear that indicates whether the circle should be drawn or cleared.

Squares

(define-struct square (center size color))

There are two kinds of square animation entities, these correspond to drawing a filled square and clearing a square. Both are represented using the structure definition shown above. The inidividual fields are described below:

To create an animation entity that indicates that a square should be drawn at 100,100 with size 20 and color Blue you would do this:

(make-square (make-posn 100 100) 20 'Blue)

To create an animation entity that indicates that a square should be cleared at 100,100 with size 20 you would do this:

(make-square (make-posn 100 100) 20 'Clear)

Delay

(define-struct delay (time))

The delay animation entity is used for slowing down an animation. Nothing happens for a while (how long is indicated by the time field found in this structure) whenever a delay is found in an animation. The only field is time, this represents the delay in seconds.

To create an animation entity that indicates that delay of 1.5 seconds should happen, you would need something like this:

(make-delay 1.5)

Animation Entity Support - draw-anim-entity

You are given the code for a function that will process one animation entity, this function determines what kind of entity it is given, and takes the appropriate action. The function defintion is shown below:

;; draw-anim-entity consumes an animation-entity (a struct that is either
;;    a circle, square or delay
;;
;; For square and circle, either draws or clears the entity (based on the color)
;; For delay, just waits for a while
; 
(define (draw-anim-entity s)
  (cond

    [(circle? s) 
	 ;; it's a circle - should we draw or clear?
	 (cond
	  [ (symbol=? (circle-color s) 'Clear)
		;; clear it
		(clear-circle (circle-center s) (circle-radius s))]

	  [ else
		;; draw it
		(draw-circle (circle-center s) (circle-radius s) (circle-color s)) ])]

    [(square? s) 
	 ;; it's a square - should we draw or clear?
	 (cond
	  [(symbol=? (square-color s) 'Clear)
	   ;; clear it - need to translate from center to topleft
	   (clear-solid-rect 
		(make-posn
		 (- (posn-x (square-center s)) (/ (square-size s) 2))
		 (- (posn-y (square-center s)) (/ (square-size s) 2)))
		(square-size s) (square-size s))]

	  [ else
		;; draw it
		(draw-solid-rect 
		 (make-posn
		  (- (posn-x (square-center s)) (/ (square-size s) 2))
		  (- (posn-y (square-center s)) (/ (square-size s) 2)))
		 (square-size s) (square-size s) (square-color s))])]

	[(delay? s)
	 ;; it's a delay - wait for a while
	 (sleep-for-a-while (delay-time s))]

	;; it's not anything we understand - report an error
    [else 
	 (error 'draw-anim-entity "I dont' know how to draw that (is it an animimation entity?)")]))

Below is some sample code that calls this function to display a circle and an inscribed square:

(start 400 400)

(draw-anim-entity
 (make-square (make-posn 200 200) 141 'Blue))

(draw-anim-entity
 (make-circle (make-posn 200 200) 100 'Red))

We will build animations by constructing lists of animation entities, and then giving these lists to a function that plays then back. You need to write the playback function, a function that creates a specific animation (generates a list of structures that do something specific), and any animation you want to create.

Assignment

Playback function

You need to create a function called play that will "play back" a list of animation entities. For example, your function could be given this list shown below and should produce something like the animation shown:

 (cons (make-circle (make-posn 100 100) 20 'Red)
    (cons (make-delay 0.5)
       (cons (make-circle (make-posn 100 100) 30 'Red)
          (cons (make-delay 0.5)
             (cons (make-circle (make-posn 100 100) 40 'Red)
                (cons (make-delay 0.5)
                   (cons (make-circle (make-posn 100 100) 50 'Red) empty)))))))

Note that all this function needs to do is step through the items in the list, calling draw-animation-entity once for each item in the list. (Writing this function is easy).

Circle growing animation creation function

Your playback function is not that useful if you have to build all the animation lists by hand, so instead we will write functions that build lists that we can play back. You will write a function named grow-circle that will create an animation based on the following information:

Here is one example of how your function could be called:

(play
  (grow-circle
    ;; initial circle
	(make-circle (make-posn 400 400) 10 'Blue)
	;; amount to grow
	2
	;; delay per step
	0.1
	;; total steps
	200))

As you can see, grow-circle consumes a circle structure (the initial circle), a number (how much to add to the radius each step), a number (delay time between steps) and another number (total number of steps). grow-circle produces a list of animation entities, which we can give to play.

Animation merge function

Suppose you have two different animation lists, but you want both to happen on the screen in the same window at the same time. All you need to do is to merge the two animation lists so that the playback function will process them both. By merge, I mean the following:

Given lists A ( which holds a0, a1, a2, ...) and B (which holds b0, b1, b2, ...) you create a new merged list that contains a0, b0, a1, b2, a2, b2, ...

You need to write a function named merge that consumes two lists (lists of anything!) and produces a new list. The new list contains alternating items from the original lists as long as that is possible (until it finds the end of the short list), then contains the remainder of the longer list. Below are some examples:

(define a (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 (cons 6 empty)))))))

(define b (cons 'barry (cons 'bill (cons 'bob empty))))

(merge a b) => 

(cons 1 
  (cons 'barry 
    (cons 2 
      (cons 'bill 
        (cons 3 
          (cons 'bob 
            (cons 4
              (cons 5 
                (cons 6 empty)))))))))

Once you have this function working you can do stuff like this:

(play
  (merge
    ;; red circle at 400,400
    (grow-circle
        ;; initial circle
    	(make-circle (make-posn 400 400) 10 'Red)
    	;; amount to grow
    	2
    	;; delay per step
    	0.1
    	;; total steps
    	200)

    ;; blue circle at 200,200
    (grow-circle
        ;; initial circle
    	(make-circle (make-posn 200 200) 2 'Blue)
    	;; amount to grow
    	1
		;; set time to 0 since we are merging
        0
    	;; total steps
    	200)))

Go crazy

So far, you are required to write:

  1. the play function
  2. the grow-circle function
  3. the merge function

Here is where you can do something more interesting. Write a function that produces an award-winning animation. The general idea is to make sure that your function produces a list that can be given to play, but you decide on the details... Below are some ideas:


Extra Credit

You can earn extra credit on this quiz by writing a function named play-forever that will cycle through a animation list endlessly without ever calling the function play (or something just like it). Your play-forever function can call draw-anim-entity, and can call itself recursively, but nothing else...


Submitting

Submit your code as a single program (file) to the WebCT Drop Box labeled "Quiz 4". We should be able to easily find the required functions and to test them out (please give us some test code!).