On this page:
1 Converting numbers
2 Plotting posns
3 Converting places
4 Converting coordinates
5 Plotting graphs
6 Plotting functions
7 Challenge
8.12

Problem set 9: Plotting functions🔗

This assignment is due on Wednesday, March 27 at 11:59pm. Submit it using Handin as assignment ps9. Corrections can be presented until Friday, April 19.

This problem set builds on the skills that we introduced in Lectures 2−9 and 11−18. To encourage you to review those lectures, your grade on this assignment will be capped by your grade on those lectures at the time you submit (or correct) this assignment. You can resubmit lecture exercises at any time.

Study tip: To do this problem set, you only need to know what we teach before the second midterm. In fact, doing this problem set is a great way to prepare for the second midterm.

Remember to follow the design recipe for each function, even if defined locally. In particular, every type mentioned in a signature must be introduced by a data definition, except for these well-known types: Number, Image, String, Color, Boolean, KeyEvent, MouseEvent, Posn, Anything, NaturalNumber, ListOfNumbers, 1String, [ListOf X], [NEListOf X].

Note: Use the “Intermediate Student with lambda” language on this and further assignments in this course.

1 Converting numbers🔗

Here is a useful function:
; convert : Number Number Number Number Number -> Number
; if a1 becomes b1 and a2 becomes b2, then what should a become?

;

; *Assumption*: a1 and a2 are different
(check-expect (convert 0 32 100 212   0)  32)
(check-expect (convert 0 32 100 212 100) 212)
(check-expect (convert 0 32 100 212  50) 122)
(define (convert a1 b1 a2 b2 a)
  (/ (+ (* b1 (- a2 a)) (* b2 (- a a1))) (- a2 a1)))

Exercise 1. Design a function celsius->fahrenheit which takes a temperature in Celsius and returns a number in Fahrenheit.

Do not use any arithmetic operations such as + - * /. Instead, use convert.

Hint: To use convert, you only need to pay attention to its signature, purpose, and examples. You don’t need to look at its definition. If you’re not sure what convert does, study the three examples we provide above, and plug their inputs into the purpose statement we provide above. For example, (convert 0 32 100 212 50) answers this question: if 0 becomes 32 and 100 becomes 212, then what should 50 become?

Exercise 2. Design a function number-between-20-and-180 that takes a number n between 0 and 100 as input and answers this question: Imagine you are walking on the number line from the number 20 to the number 180. Where are you after going n% of the way?

Do not use any arithmetic operations such as + - * /. Instead, use convert.

Hint: If you’re not sure what convert does, plug more inputs into the purpose statement we provide above. For example, (convert 0 20 100 180 50) answers this question: if 0 becomes 20 and 100 becomes 180, then what should 50 become?

Exercise 3. Design a function fahrenheit->celsius which takes a temperature in Fahrenheit and returns a number in Celsius.

Do not use any arithmetic operations such as + - * /. Instead, use convert.

Hint: If you’re not sure what convert does, plug more inputs into the purpose statement we provide above. For example, (convert 32 0 212 100 50) answers this question: if 32 becomes 0 and 212 becomes 100, then what should 50 become?

2 Plotting posns🔗

In the remainder of this assignment, you will develop a program that plots the graph of a function and lets the user move around and zoom in and out, like a graphing calculator does.

Exercise 4. Define constants width and height which will be used to determine the width and height of the scene. Using build-list, define my-points to be a long list of posns of your making.

Hint: To recall how to use build-list, study the build-list section of Lecture 19: Follow the template. Also, study the signature and purpose in Figure 95 of the textbook. There, N means NaturalNumber. But what is X?

Exercise 5. Design a function visible which takes a list of posns and returns the list of all the given posns whose x coordinate is between 0 and width (inclusive) and whose y coordinate is between 0 and height (inclusive). Use filter.

Hint: To recall how to use filter, study the Filter section of Lecture 19: Follow the template. Also, study the signature and purpose in Figure 95 of the textbook. What is X?

Exercise 6. Design a function draw-lop which takes a list of posns and returns an image whose size is width by height. The purpose of draw-lop is to represent each posn as a dot of some shape and color on the scene. Use foldr. Apply draw-lop to my-points and store the result in a constant my-plot:
(define my-plot (draw-lop my-points))

Hint: To recall how to use foldr, review the Fold section of Lecture 19: Follow the template. Also, study the signature and purpose in Figure 96 of the textbook. What is X and what is Y?

3 Converting places🔗

This section is optional (even for H211 students), and you don’t need to submit anything, but it may help you do the next section.

Optional exercise W. Here are the locations (longitudes and latitudes) of 5 places in Bloomington:
(define wells-library (make-posn -86.5170 39.1710))
(define sample-gates  (make-posn -86.5270 39.1665))
(define courthouse    (make-posn -86.5340 39.1670))
(define stadium       (make-posn -86.5255 39.1810))
(define college-mall  (make-posn -86.4955 39.1615))
(define bloomington-places
  (list wells-library sample-gates courthouse stadium college-mall))
What image do you get when you use bloomington-places as the input to draw-lop? Why?

So, to make a picture of bloomington-places, we have to first convert the longitudes and latitudes into screen coordinates suitable for draw-lop.

Optional exercise X. Recall that the x coordinates of the scene are 0 at the left edge and width at the right edge. Finish designing the following function. Use convert, like you did in the first section; do not use any arithmetic operations such as + - * /.
; bloomington-x->screen : Number -> Number
; turn a longitude in Bloomington into an X on screen
(check-expect (bloomington-x->screen -86.5725) 0)
(check-expect (bloomington-x->screen -86.5225) (/ width 2))
(check-expect (bloomington-x->screen -86.4725) width)

Optional exercise Y. Recall that the y coordinates of the scene are 0 at the top edge and height at the bottom edge. Finish designing the following function. Use convert, like you did in the first section; do not use any arithmetic operations such as + - * /.
; bloomington-y->screen : Number -> Number
; turn a latitude in Bloomington into an Y on screen
(check-expect (bloomington-y->screen 39.1150) height)
(check-expect (bloomington-y->screen 39.1650) (/ height 2))
(check-expect (bloomington-y->screen 39.2150) 0)

Optional exercise Z. Finish designing the following function. Use bloomington-x->screen and bloomington-y->screen; do not use any arithmetic operations such as + - * /.
; bloomington->screen : Posn -> Posn
; turn Bloomington coordinates into screen coordinates
(check-expect (bloomington->screen wells-library)
              (make-posn (* 0.555 width) (* 0.440 height)))
(check-expect (bloomington->screen sample-gates)
              (make-posn (* 0.455 width) (* 0.485 height)))
Finally, make a picture of bloomington-places using draw-lop, map, and bloomington->screen.

4 Converting coordinates🔗

In this assignment, the word graph will refer to a list of posns (like bloomington-places) obtained from a given function f. The x field of each posn will store an x-value. The y field of each posn will store the value of f evaluated at the number stored in the x field. By drawing or plotting, we mean producing an image from the graph of a function.

The data contained in a graph will be interpreted as points in a Cartesian coordinate system. The goal of this section is to relate lists of posns interpreted in a Cartesian coordinate system to lists of posns interpreted in the coordinate system of a DrRacket image. In this way, we will not just show a fixed image, but let the user move and zoom the view of the function.

As our big-bang program runs, the view will change, but the graph will stay the same. So our World only needs to store the view, not the graph.
; A World is (make-world Number Number Number Number)
; *Interpretation*:
;   xmin is the Cartesian x coordinate of the left edge of the view
;   xmax is the Cartesian x coordinate of the right edge of the view
;   ymin is the Cartesian y coordinate of the BOTTOM edge of the view
;   ymax is the Cartesian y coordinate of the TOP edge of the view
(define-struct world [xmin xmax ymin ymax])
Here is an example of a World, which is the view of Bloomington from the previous section.
(define bloomington-view
  (make-world -86.5725 -86.4725 39.1150 39.2150))

Exercise 7. Design a function cartesian->screen which takes a World and a list of posns and returns a list of posns. Use map and convert; do not use any arithmetic operations such as + - * /.

The input list of posns is in a Cartesian coordinate system where the positive y-direction points upward. The new function cartesian->screen should produce the corresponding list of posns in the coordinate system of a DrRacket image, where the positive y-direction points downward. For example, we can make a picture of bloomington-places:
(check-expect (cartesian->screen bloomington-view bloomington-places)
              (list (make-posn (* 0.555 width) (* 0.440 height))
                    (make-posn (* 0.455 width) (* 0.485 height))
                    (make-posn (* 0.385 width) (* 0.480 height))
                    (make-posn (* 0.470 width) (* 0.340 height))
                    (make-posn (* 0.770 width) (* 0.535 height))))
So (draw-lop (cartesian->screen bloomington-view bloomington-places)) should produce an image depicting bloomington-places.

Here are two more examples, whose inputs only differ in the World:
(check-expect (cartesian->screen (make-world -2 2 -1 1)
                                (list (make-posn 0 0)
                                      (make-posn 2 0)
                                      (make-posn -2 1)))
              (list (make-posn (/ width 2) (/ height 2))
                    (make-posn width (/ height 2))
                    (make-posn 0 0)))
(check-expect (cartesian->screen (make-world -2 6 0 1)
                                (list (make-posn 0 0)
                                      (make-posn 2 0)
                                      (make-posn -2 1)))
              (list (make-posn (/ width 4) height)
                    (make-posn (/ width 2) height)
                    (make-posn 0 0)))

For this exercise and the rest of this assignment, you might find the following picture helpful, but don’t worry about it if it doesn’t make sense to you.

A rectangle labeled "visible area".
The left   edge of the rectangle is labeled "Cartesian x = xmin" and "screen x = 0".
The right  edge of the rectangle is labeled "Cartesian x = xmax" and "screen x = width".
The bottom edge of the rectangle is labeled "Cartesian y = ymin" and "screen y = height".
The top    edge of the rectangle is labeled "Cartesian y = ymax" and "screen y = 0".
A handful of dots are sprinkled around the picture, mostly inside the rectangle, loosely forming a curve.

Hint: To recall how to use map, review the Map section of Lecture 19: Follow the template. Also, study the signature and purpose in Figure 95 of the textbook. What is X and what is Y?

5 Plotting graphs🔗

To move and zoom the view, the user should change the World by pressing keys.

Exercise 8. Design a function navigate to be used as a big-bang key-event handler. Thus, navigate takes a World and a key event and returns a World. navigate enables 8 distinct keyboard-driven behaviors (aside from doing nothing at all).

The arrow keys will allow the user to move in four different directions in the scene. For example, the "down" key should subtract the same amount from both ymin and ymax. Because the view should never move by more than one screenful at a time, that amount should be something proportional to (- ymax ymin), such as (* speed (- ymax ymin)). Define the constant speed to be a number between 0.1 and 0.4, such as 0.2.

The keys "w", "a", "s" and "d" will allow the user to zoom in four ways: With respect to the x-axis, the "d" key will zoom in, and the "a" key will zoom out. With respect to the y-axis, the "w" key will zoom in, and the "s" key will zoom out. For example, the "w" key should add an amount to ymin and subtract the same amount from ymax. Because ymin should never exceed ymax, that amount should again be something proportional to (- ymax ymin), such as (* speed (- ymax ymin)).

Exercise 9. Design a big-bang program using the functions defined in Exercises 6, 7 and 8 that allows you to navigate a plot of my-points. Define a constant my-interactive-plot that stores the result of this big-bang program:
(define my-interactive-plot
  (big-bang ...))
Hint: Every to-draw handler should have the signature World -> Image. Every on-key handler should have the signature World KeyEvent -> World.

6 Plotting functions🔗

Now that we can plot a list of posns, let’s turn to plotting a graph. The y coordinates in a graph are obtained by feeding many x coordinates to the same function. The function must have the signature Number -> Number, like sqr and sin. So we need to generate many x coordinates, feed them to the same function to obtain y coordinates, and draw the resulting list of posns.

Exercise 10. Design a function xticks which takes three numbers xmin, xmax and n and produces an evenly spaced list of n numbers from xmin to xmax. The output list will be interpreted as x-values in the Cartesian coordinate system.

If n is taken to be (add1 width), the output numbers will range across the visible portion of the plot. For example, if width is set to 400, then
(xticks -2 2 (add1 width))
will produce a list of 401 evenly distributed numbers starting with -2 and ending with 2:
(list -2 -1.99 -1.98 ... 1.98 1.99 2)

Use build-list and convert; do not use * /.

Hint: To recall how to use build-list, review the build-list section of Lecture 19: Follow the template. Also, study the signature and purpose in Figure 95 of the textbook. There, N means NaturalNumber. But what is X?

Exercise 11. Design a function graph which takes a function Number -> Number and a list of numbers and produces a list of posns. The output posns contain a number from the input list paired with the value of the input function applied to that number. For example,
(graph sqr (list 0 2))
would give
(list (make-posn 0 0) (make-posn 2 4))
Use map.

Exercise 12. Design a function draw-graph which takes a World and a function Number -> Number and produces its plot. draw-graph should be the composite of draw-lop, cartesian->screen, graph and xticks. Pass (add1 width) to xticks as the number of ticks to generate.

Exercise 13. Design a big-bang program with draw-graph and navigate to visualize interactively the sin function. Store the result in a constant named interactive-sin:
(define interactive-sin
  (big-bang ...))

7 Challenge🔗

Exercise 14. Let’s give the user the ability to select a point in the graph of a function with the mouse and to see its coordinates. Specifically, when the user clicks on the plot with the mouse, a mark should appear corresponding to the point of the graph closest to location of the mouse event. Moreover, the program should display the Cartesian coordinates of the mark:

Here are a few suggestions. Extend the data definition of World to include a number representing the x-coordinate of the current mark (if there exists one) in Cartesian coordinates. This extra number will be updated on the mouse event “button-down”. The mouse-handler will therefore determine (via helpers) which point in the current graph is closest and extract its x-coordinate. Note that this means that the mouse-handler should be aware of the function being plotted: so just as draw-graph is parametrized by the function to be plotted, your mouse-handler should also be parametrized by the function to be plotted.

The coordinates should display as inexact numbers rounded to the nearest hundredth: exact->inexact, round, * and / are useful in achieving this.

Note: Extending the data definition of World will cause Handin to complain that your code doesn’t work properly. You can safely ignore these messages, but be forewarned that Handin will stop giving you useful feedback on the non-challenge problems once you start the challenge.