Please send questions to
st10@humboldt.edu .
; Bonus, after-class example: a scene of a "sun" rising
; and setting, as another example of a conditional
; function, another boolean function, and another
; collection of definitions and functions that work
; together to a single purpose
; NOTE: this is more complex than the animation
; asked for in HW 4 is required to be (although
; since you get to customize if desired, you might take
; yours to a comparable level... 8-) )
;--------------------------------------------------------
; goal: to animate a scene where a "sun" rises and sets...
; more specifically: a sun moves diagonally upward for
; a while, then diagonally downward for a while, and
; repeats;
; ...can't do that with just modulo, but you CAN
; with the help of a cond expression;
; (could the sun "rise" in a curved arc? Sure, with the right
; math... maybe a lovely quadratic equation for setting x and y
; given the modulo of the time counter... that's a
; variation for another time...)
; we know we need to create a scene -- one with a sun
; whose position is determined by the current time-counter
; value -- but sometimes the sun should be "rising" and
; sometimes the sun should be "setting".
; and, with a time-counter that gets ever-bigger, we can
; use modulo to coerce that value into a guaranteed-to-be-in
; -scene range, if we are careful;
;-------------------------------------------------------
; FIRST, then: some definitions:
(define WIDTH 400)
(define HEIGHT 300)
(define SUN (circle 45 "solid" "yellow"))
(define BACKDROP
(place-image (nw:rectangle WIDTH HEIGHT "solid" "blue")
0 0 (empty-scene WIDTH HEIGHT)))
BACKDROP
; might be fun to figure out how we could get the sky to
; brighten when the sun is going up, and darken when going
; down, but that's another possibility for another day... 8-)
; (it would require creating the nw:rectangle in the
; create-scene function, too, by the way, instead of
; having it be part of the backdrop)
; contract: create-sun-scene: number -> scene
; purpose: expects a time-counter value, and if
; (modulo time-counter WIDTH) is in the morning
; range, should produce a scene with a sun centered
; at...
; Hmm. Need to consider this!
; at time-counter of 0 -- where should sun be?
; how about in the middle of the left-hand side?
; ...what coordinates? x is 0; y is (/ HEIGHT 2)
; How long should it go up? That is, what is the "morning"
; range? How about -- [0, (/ WIDTH 2)]?
; "afternoon" range? Note that it is ((/ WIDTH 2), WIDTH)
; ...and WIDTH is NOT part of that interval, since
; (modulo time-counter WIDTH) can never equal WIDTH
; (it always produces 0 to (- WIDTH 1) )
; So -- at time-counter of (/ WIDTH 2), I'd LIKE to be at the
; top middle of the scene -- perhaps x of (/ WIDTH 2),
; y of 0
; it DOES take a little math to figure this out --
; I want to start at (0, (/ HEIGHT 2)) and end at
; ((/ WIDTH 2), 0) in (/ WIDTH 2) steps;
; ...while in morning range, as (modulo time-counter WIDTH)
; goes from 0 to (/ WIDTH 2), I want x coordinate to change
; from 0 ro (/ WIDTH 2). OK, then, that's set.
; ...how should y change? from (/ HEIGHT 2) to 0! in (/ WIDTH 2)
; steps;
; slope of a line is the change in y over the change in x;
; (/ (/ HEIGHT 2) (/ WIDTH 2) )
; ...that's how much it changes per unit change in x?
; OK then!
; auxiliary function one: I'd like a function that simply
; tells me if the time-counter is in "morning" range;
; ...it is in morning range if (modulo time-counter WIDTH)
; is in the interval [0, (/ WIDTH 2)]
;--------------------------------------------------------
; contract: morning-range?: number -> boolean
; purpose: expects a time-counter value, and produces
; whether (modulo time-counter WIDTH) is in
; the interval [0, (/ WIDTH 2)]
; normally, an interval like this would require at least
; FIVE specific examples/test cases: <0, =0, in the interval,
; =(/ WIDTH 2), and >(/ WIDTH 2).
; BUT! A time-counter can NEVER be negative -- so you can skip
; that one.. (it's OK to have it in a check-expect, but
; it would not be called by animate...)
(check-expect (morning-range? 0) true)
(check-expect (morning-range? (- (/ WIDTH 2) 5)) true)
(check-expect (morning-range? (/ WIDTH 2)) true)
(check-expect (morning-range? (+ (/ WIDTH 2) 5)) false)
; hmm -- and better have a few for "bigger" example
; time-counter values, to double-check my modulo
; expressions;
(check-expect (morning-range? (* 3 WIDTH)) true)
(check-expect (morning-range? (+ 1 (* 3 WIDTH))) true)
(check-expect (morning-range? (+ (* 3 WIDTH) (/ WIDTH 2)))
true)
(check-expect (morning-range? (+ (* 3 WIDTH) (/ WIDTH 2) 5))
false)
(define (morning-range? time-counter)
(and (>= (modulo time-counter WIDTH) 0)
(<= (modulo time-counter WIDTH) (/ WIDTH 2)))
)
; I think I'd like an auxiliary function that helps me
; with the y coordinate of the desired sun, too.
; ...how about two? One for the "rising" sun, and one
; for the "setting" sun?
;------------------------------------------------------
; contract: get-rising-y: number -> number
; purpose: expects a time-counter value, assumed to
; be in the "morning" range, and determines the
; y value for the sun for that time-counter
; value
; y better be this at time-counter of 0 (I want the sun to
; start on the left, mid-way up)
(check-expect (get-rising-y 0) (/ HEIGHT 2))
; y better be this at time-counter of (/ WIDTH 2) (I want the
; sun, at its height, to be at height 0)
(check-expect (get-rising-y (/ WIDTH 2)) 0)
; OK then! to get from (/ HEIGHT 2) to 0 as time-counter
; increases -- y had better decrease by
; (/ (/ HEIGHT 2) (/ WIDTH 2) ) by time-counter "step" --
; OR -- subtract (/ (/ HEIGHT 2) (/ WIDTH 2) ) * time-counter
; from initial y of (/ HEIGHT 2) for each step...
; (really, the modulo of time-counter and width...)
(check-expect (get-rising-y 10)
(- (/ HEIGHT 2)
(* (/ (/ HEIGHT 2) (/ WIDTH 2)) 10)))
; and make sure still works as time-counter gets beyond
; WIDTH... this should have the SAME y, as the above
; check-expect, true?
(check-expect (get-rising-y (+ 10 (* WIDTH 3)))
(- (/ HEIGHT 2)
(* (/ (/ HEIGHT 2) (/ WIDTH 2)) 10)))
(define (get-rising-y time-counter)
(- (/ HEIGHT 2)
(* (/ (/ HEIGHT 2) (/ WIDTH 2))
(modulo time-counter WIDTH)))
)
; hmm; won't get-setting-y be similar, except it ADDS to 0
; instead of subtracting from (/ HEIGHT 2)?
;--------------------------------------------------------
; contract: get-setting-y: number -> number
; purpose: expects a time-counter value, assumed to
; be in the "afternoon" range, and determines the
; y value for the sun for that time-counter
; value
; y better be this at time-counter of (/ WIDTH 2) (I want the
; sun to be at its highest point at this "midday" value)
(check-expect (get-setting-y (/ WIDTH 2)) 0)
; careful -- can x ever reach WIDTH? not with
; (modulo time-counter WIDTH) involved -- it goes to 0, then!
; ...there's no night-time in this particular scenario... 8-)
; ...and, really, get-setting-y should NOT be called with
; a time-counter of WIDTH -- that's not in the "afternoon"
; range...
; OK then! to get from 0 to (/ HEIGHT 2) as time-counter
; increases in the "afternoon" -- y had better increase by
; (/ (/ HEIGHT 2) (/ WIDTH 2) ) by time-counter "step" --
; OR -- add (/ (/ HEIGHT 2) (/ WIDTH 2) ) *
; (- time-counter (/ WIDTH 2))
; from initial y of 0 for each step past the middle;
; (and of course, adding to 0 -- that's just that product)
; (and, really, the modulo of time-counter and width...)
(check-expect (get-setting-y (+ (/ WIDTH 2) 10))
(* (/ (/ HEIGHT 2) (/ WIDTH 2)) 10))
; and make sure still works as time-counter gets beyond
; WIDTH... this should have the SAME y, as the above
; check-expect, true?
(check-expect (get-setting-y (+ 10 (* WIDTH 3) (/ WIDTH 2)))
(* (/ (/ HEIGHT 2) (/ WIDTH 2)) 10))
(define (get-setting-y time-counter)
(* (/ (/ HEIGHT 2) (/ WIDTH 2))
(- (modulo time-counter WIDTH) (/ WIDTH 2)))
)
; back to scene creation function, finally!
;------------------------------------------------------------
; contract: create-sun-scene: number -> scene
; purpose: expects a time-counter value, and if
; (modulo time-counter WIDTH) is in the morning
; range, creates a scene with the sun centered
; at the appropriate "rising" point for that
; time between (0, (/ HEIGHT 2)) and
; ((/ WIDTH 2), 0). Otherwise, it creates
; a scene at the appropriate "setting" point for
; that time between ((/ WIDTH 2), 0) and
; (WIDTH, (/ HEIGHT 2))
(check-expect (create-sun-scene 0)
(place-image SUN
0 (/ HEIGHT 2)
BACKDROP))
(check-expect (create-sun-scene 10)
(place-image SUN
10 (get-rising-y 10)
BACKDROP))
(check-expect (create-sun-scene (/ WIDTH 2))
(place-image SUN
(/ WIDTH 2) 0
BACKDROP))
(check-expect (create-sun-scene (+ (/ WIDTH 2) 5))
(place-image SUN
(+ (/ WIDTH 2) 5)
(get-setting-y (+ (/ WIDTH 2) 5))
BACKDROP))
; at time-counter of WIDTH -- it should be back on the
; left...
(check-expect (create-sun-scene WIDTH)
(place-image SUN
0 (/ HEIGHT 2)
BACKDROP))
; and make sure time-counter still OK as it goes out
; of range...
(check-expect (create-sun-scene (+ (* WIDTH 3) 5))
(place-image SUN
5 (get-rising-y 5)
BACKDROP))
(check-expect (create-sun-scene (+ (* WIDTH 3) (/ WIDTH 2) 5))
(place-image SUN
(+ (/ WIDTH 2) 5)
(get-setting-y (+ (/ WIDTH 2) 5))
BACKDROP))
; result of template step
;(define (create-sun-scene time-counter)
; (cond
; [... ...]
; [else ...]
; )
;)
(define (create-sun-scene time-counter)
(cond
[(morning-range? time-counter)
(place-image SUN
(modulo time-counter WIDTH)
(get-rising-y time-counter)
BACKDROP)]
[else
(place-image SUN
(modulo time-counter WIDTH)
(get-setting-y time-counter)
BACKDROP)]
)
)
; does this actually work...? YES!
(animate create-sun-scene)