;; CMPU101, Spring 2013 ;; Lecture #15 (require 2htdp/image) (require 2htdp/universe) ; ; Defining functions to animate images: ; ; The universe teachpack is a library created to allow animations to ; be easily coded in Racket. ; ; Animate is a higher-order function that starts a counter at 0 and ; increments the counter 28 times a second. ; ; Animate is used to create time-based simulations, a series of ; images. ; ; The animate function is written in the universe teachpack, which ; is why we need to put the line (require 2htdp/universe) at the ; top of the program. ; ;--------------- ; SIMPLE ANIMATION: ; ; Animate starts a counter at 0 and increments the counter 28 times ; a second. The value of this counter is given as input to the ; function passed as an argument to animate. ; ; animate is an example of a higher-order function, because it consumes ; a one parameter function and calls it repeatedly. The simplest kind of ; animated world program is a time-based simulation, which can display a ; series of images. The programmer’s task is to supply a function that ; creates something to display for each positive natural number that is ; the value of a clock tick. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Class exercise 1: Write a 0-parameter function called ran-clr that ; generates a random color by using the built-in color struct ; ; Contract: (ran-clr) -> color ; Header: (define ran-clr (lambda () ...)) ; Purpose: To create a color object with random entries between ; 0 and 255 for red, green, and blue fields ; Prefunction tests by visual inspection. (define UPPER 256) ; Function definition: (define ran-clr (lambda () (make-color ; Each of the next 3 lines generates a number between 0 and 255 (random UPPER) (random UPPER) (random UPPER)))) ; Post-function printfs: (printf "(ran-clr) => ~a~%" (ran-clr)) (printf "(ran-clr) => ~a~%" (ran-clr)) (printf "(ran-clr) => ~a~%" (ran-clr)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Class exercise 2: ; To start the discussion of animating functions, we'll write a simple, ; non-animated graphics function and then make it change over time. ; The following function should display a random-colored, solid square ; that fills a square empty scene with side length 400. ;----------------BEGIN SQUARE CONSTANTS--------- (define MODE "solid") (define SIZE 400) ;; size of screen (define MTSQ (empty-scene SIZE SIZE)) ;-----------------END SQUARE CONSTANTS---------- ; Contract: (show-square) -> image ; Header: (define show-square (lambda () ...)) ; Purpose: randomly set color of square ; Pre-function tests: By visual inspection. ; Function definition: (define show-square (lambda () (place-image (square SIZE MODE (ran-clr)) ;; SIZE/2 PUTS THE PIN-HOLE OF THE SQUARE AT THE CENTER OF THE SCENE. (/ SIZE 2) ;; X-coordinate (/ SIZE 2) ;; Y-coordinate MTSQ) ) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Animating show-square: ; ; To use animate with show-square, we need to add a numeric parameter ; to the show-square function (even if we don't use the parameter inside ; the function). ; The results of a call to animate are displayed on the empty-scene. ; The simulation runs until you click the Stop button in DrRacket or close ; the window. At that point, animate returns the number of ticks that ; have passed. ;; uncomment the line below to bring up a square scene that changes color ;; very fast. ;(animate show-square) ; ; We can also write a function that uses the natural number supplied by ; animate as a means of positioning a shape and moving it across ; the scene. ; If the shape should move only from left to right, which coordinate ; will be changing, x or y? Which coordinate should remain fixed? ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Class exercise 3: ; Write a function to move a circle (ie, a "disco ball") of constantly ; changing color from left to right across a scene. ; Constants for the disco-ball animation: (define WIDTH 500) (define HEIGHT 150) (define RADIUS 50) (define Y-COORD (/ HEIGHT 2)) (define EMPTY (empty-scene WIDTH HEIGHT)) ; Contract: (move-disco-ball number) -> scene (image) ; Header: (define move-disco-ball (lambda (x) ... )) ; Purpose: Move circle from left to right across scene. ; Pre-function tests: By visual inspection ; Function definition: (define move-disco-ball (lambda (x) (place-image (circle RADIUS MODE (ran-clr)) x Y-COORD EMPTY))) ;; uncomment the line below to start the animation of the circle ;; moving across the scene. ;(animate move-disco-ball) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Class exercise 4: ;; Create a scene in which a rocket image is moving downward toward ;; the bottom of the scene. The rocket (and simulation) stops when ;; the rocket touches the bottom of the scene. ;; The world-state is a number, the current y-coordinate of the rocket. ;------------BEGIN ROCKET SCENE CONSTANTS------------------ (define ROCKET .) (define ROCKET-HEIGHT/2 (/ (image-height ROCKET) 2)) (define COORD-X 100) (define SQHTWD 200) ; Height and width of sqare scene (define MT (empty-scene SQHTWD SQHTWD)) ;------------END ROCKET SCENE CONSTANTS-------------------- ;Contract: (create-rocket-scene number) -> image ;Header: (define create-rocket-scene (lambda (y) ... )) ;Purpose: display rocket at given coordinates. This function ; is called by the big-bang function which passes ; in the current y coordinate: the state of the world. (define create-rocket-scene (lambda (y) ;; note that the x coordinate remains fixed, but the y coordinate changes ;; in the following line (place-image ROCKET COORD-X y MT))) ;Contract: (on-ground? number) -> number ;Header: (define on-ground? (lambda (y) ... )) ;Purpose: return true when the y coordinate of the rocket pinhole ; is >= the height of the scene minus half the image-height ; of the rocket. (define on-ground? (λ (y) (>= y (- SQHTWD ROCKET-HEIGHT/2)))) ;; The first argument to big-bang is the negative value of ;; 1/2 the rocket height so that, at the start of the animation, ;; the rocket is completely above the scene. (big-bang (* -1 ROCKET-HEIGHT/2) ;; on-draw clause passes the world state (a number) ;; into the create-rocket-scene function 28 times per second (on-draw create-rocket-scene) ;; on-tick clause increments the world state (a number) ;; 28 times per second and passes it as an argument to add1 (on-tick add1) ;; stop-when clause passes the world state (a number) in to ;; the on-ground? function and stops the animation when the ;; state of the world has the rocket on the bottom of the scene. (stop-when on-ground?)) ; ; The BIG-BANG function: ; ; The big-bang function includes a set of discrete event handlers ; that can be programmed to respond to more than just clock ticks. ; You can also use big-bang to write a function that responds to ; mouse events and key events. ; ; Big-bang can take any data type as an input argument. The initial ; argument to big-bang should be the starting state of the world. ; ; The world is the part of the simulation that changes over time. ; The clauses of big-bang change the state and create new worlds. ; Clauses to big-bang are higher-order functions that can't be used ; anywhere outside big-bang: ; ; Clauses Effects ; -------- --------------------------------------- ; on-draw: event-handler function that redraws ; the scene whenever anything changes, ; using the current value of the world ; state. ; ; The function argument to on-draw must have ; the contract (where the name of the function ; (draw-func) may vary): ; (draw-func world-state) -> scene ; ; on-tick: event-handler that specifies what will change ; about the world at regular intervals: ; ; (tick-func world-state) -> world-state ; The result of calling tick-func is a new world ; state, whatever type it may be. Again, the ; name of the function (tick-func) may vary. ; ; stop-when: Consumes a predicate function that stops the ; counter when it returns true, depending on the ; world state. ; (last-world? world-state) -> boolean ; ; The name of the function (last-world?) may vary. ; ; There are other clauses we will use later, including event- ; handlers for mouse and key events. ; ; The first animations with big-bang that we'll see use primitive ; data types (usually numbers) to represent the world state be- ; cause one number is sufficient to represent all the differences ; between scenes. ; ; ; Writing an animated world: ; ; Step 1 - Identify what needs to change during the animation. The ; initial world state should be an instance of the type ; of the world state that changes. ; ; Clearly specify the world state before starting to code. ; ; Step 2 - Define constants for every part of the world state that ; remains fixed. These constants should be written ; together at the top of the program, before, or within ; the function in which they are used. ; ; Step 3 - Decide what event handlers you will need. The on-draw ; handler is needed to display and give your scene content, ; so plan on writing a function to draw the scene in every ; animation. ; ; If the scene will change over time, you will need the ; tick handler, on-tick. ; ; If the animation should respond to mouse or keyboard ; events, include a mouse and key handler (on-mouse, ; on-key). ; ; If the animation should stop at some point you will ; need a stop-when clause. ; ; Step 4 - For each event handler used by your big-bang expression, ; you need to write a function that consumes an input of ; the world state type. Sometimes, instead of writing a ; new function for input to an event handler, you can use ; a built-in function (e.g. add1). ; ; Develop an animation of a square, initially 1 X 1, which grows by 1 ; pixel in each dimension at each clock tick. The square should be ; centered in a 200 X 200 scene (see example1.rkt). The square should ; stop growing when it fills scene. ;; World state: positive natural number, the side length of the square. ;; Constants: (define WDTH 200) (define HIGHT 200) (define SQR-X (/ WDTH 2)) (define SQR-Y (/ HIGHT 2)) (define MTY (empty-scene WDTH HIGHT)) (define COLOR "green") ;; NOTE: MODE is defined at top of this program. ;Contract: (create-square-scene number) -> image ;Header: (define create-square-scene (lambda (w-s) ... )) ;Purpose: display square of given size. This function ; is called by the big-bang function which passes ; in the current square size: the state of the world. (define create-square-scene (lambda (w-s) (place-image (square w-s MODE COLOR) SQR-X SQR-Y MTY))) ;Contract: (big-enough? number) -> boolean ;Header: (define big-enough? (lambda (w-s) ... )) ;Purpose: return true if the w-s is the same as the side-length of ; the scene (define big-enough? (lambda (w-s) (= w-s WDTH))) (define sq-main (lambda (x) (big-bang x ;; initial world state is 1 because square starts as 1X1 pixels (on-draw create-square-scene) (on-tick add1) ;; add 1 to square side length 28 times per second (stop-when big-enough?)))) ;(sq-main 1)