CS 70

Homework 2: Plotting Example Data

In this step, you will use a Turtle object to plot a small data set.

The resulting plot will be added to a rectangular patch by using your rect function from the previous step.

Implementation Steps

Setup and Reading Input Data

You’ll be writing the code for this part in the function plotExampleData().

Constant Definitions

Your function should start by declaring the constants from the previous part as well as the following:

constexpr int PLOT_STEP = 1;
constexpr int NUM_DATA_POINTS = 59;
const string INPUT_NAME = "/home/<YOUR_STUDENT_USERNAME_HERE>/data/hw2/us_population_growth.txt";
const string OUTPUT_NAME = "example_data.dst";

where you swap out the YOUR_STUDENT_USERNAME_HERE for your ssh username.

NOTE FOR AUTOGRADER

The constant expression for INPUT_NAME should be

const string INPUT_NAME = "/home/student/data/hw2/us_population_growth.txt";

for the final submission to gradescope for autograder purposes. We need to change it here for the ssh server to run your code correctly while you are still working on it.

  • Cat speaking

    If we have the same constants in two functions, isn't that duplicated code and bad style?

  • LHS Cow speaking

    In principle, we can change the values of these constants to be different in the different functions. If, when you're done, you have a constant that is the same everywhere, you can make just one global constant outside of all the functions if you like. But mostly don't worry about it, as the benefit of named constants outweighs the duplication.

  • Horse speaking

    Hay! Why are the integer constants constexpr but the strings are just const? What's up with that?

  • LHS Cow speaking

    Full-blown C++ std::string objects are complicated things, and creating one is something that can only be done when the program actually runs. Because it can't be figured out by the compiler ahead of time, it can't be a compile-time constant.

  • RHS Cow speaking

    There are alternatives to std::string that can be figured out at compile time, but we won't go down the rabbit hole of exploring them.

Declaring the Array

Then create a statically-sized array of floats that is big enough to hold NUM_DATA_POINTS values.

Reading the Values into the Array

We want to read NUM_DATA_POINTS values from the file INPUT_NAME into your array of floats.

The data set you will plot in this part of the assignment is a text file with one floating point value per line. The values represent 59 years of US population growth.

To read values from the text file, you will need to create an ifstream (short for input file stream) object. You can use the parameterized constructor for the ifstream class that takes one argument: a string representing the name of the file to read from. The syntax is

ifstream inputFile{"mytextfile.txt"};

Of course you should replace inputFile and "mytextfile.txt" with the variable name you prefer and the name of the file you want to read, respectively. Once you have your ifstream object, the >> operator will read one value at a time from the file. For example, if we have a float called myFloat, the following line would read the first float from the file and store it in myFloat:

inputFile >> myFloat;

Once you have read all of the values from the file, you should call the close member function of your ifstream object to let the system know that you are done reading from the file.

Normalizing by the Largest Value

When you plot the data, you’ll want it to be appropriately scaled when visualized in your patch. To accomplish this, you’ll normalize the data by its largest value. You’ll also be excluding the rectangular border for the patch from the available visualization space.

Find the Largest Value

You should implement a loop-based approach to finding the largest float in your array.

Generate Normalized Data

Since the height of your patch is fixed, we want to make sure that all of the data points we plot will fit on the patch. To do that, we will normalize all of the values so that they fall between 0 and the largest available y-axis value.

  1. First, calculate the availableHeight on the patch: the PATCH_HEIGHT minus the size of the edge stitching, EDGE_STEP.
  2. Create a new statically sized array of floats to store the normalized data values.
  3. Then scale each float in your array of data values so that it falls between 0 and availableHeight.

Plotting the Points

Now you’ll use the Turtle to draw a border for the plot and plot the data.

Draw the Border

Create a Turtle object and draw a satin-stitched rectangle that is PATCH_WIDTH wide, PATCH_HEIGHT tall, and has a step size of EDGE_STEP. Be sure to use your rect function!

Plot the Line

Next you’ll generate a line plot with the values from the normalized array of floats. To plot the data:

  1. First, calculate how much width is available for each data point, and store that number in a variable. You will use that value to calculate the x position for each point in your plot. The total available width for all the points is PATCH_WIDTH - EDGE_STEP.
  2. Get ready to plot: Lift the pen (so you can jump to the first plot point) and set the Turtle’s step size to PLOT_STEP; satin stitch should still be on.
  3. Loop through the values in your normalized array of floats. For each, have the turtle gotopoint(x, y) where x is calculated from the available width for each point and y comes from the array of scaled floats. You’ll likely want the points to start at a slight offset to the right so as not to overlap the rectangle border. It may take some trial and error to get the line plot roughly centered inside the rectangle.

Note: Be sure that after the Turtle goes to the location of the first data point, the pen is down. Otherwise, you won’t see any of the points you plot! Also, these directions assume the Turtle’s position at the end of rect() is the bottom left corner of the rectangle.

Annotate the Plot

We can add labels to interesting points in our plots. For this step, label the peak in the middle of the plot with the year it corresponds to, 1992, by using gotopoint() and choosing a point to write the text. Aim to have the beginning of the first 9 of 1992 aligned vertically with the middle peak; again, you'll have to do some experimentation. You can view our example patch below in the next step.

Save the Plot to a File

Save the resulting embroidery pattern to example_data.dst. Then convert the pattern to example_data.svg and confirm that it looks right. When you are done, your patch should look something like

Example of expected output

Helpful Hints

  • Horse speaking

    What were those tips that were on the previous page? I think they still apply!

Don't Forget to Recompile (and Re-convert!)

Remember that every time you change embroidery.cpp, you need to recompile it, relink it, run ./homework2 again and regenerate the .svg file with convert before you can see the results. (If you fix a bug in the code and the output image doesn’t change, you probably forgot one of these steps.)

Pay Attention to Warnings (from clang++ and cpplint)

We recommend that you fix compiler warnings immediately, rather than waiting until the end. Even if most warnings are about issues that don’t cause trouble in practice, sometimes warnings reflect very serious errors in your code!

Similarly, cpplint is pretty picky about how you write your code, so your life will be better if you occasionally run it and correct the formatting as you go, rather than waiting until the end to fix dozens (or for larger programs, hundreds) of small annoying errors.

Visual Studio Code Is Sometimes Clueless

When working on C++ code, sometimes your editor, like VS Code, will try to “help” you. For this assignment, it may complain to you that it does not find turtle-related libraries on your system. This is OK, since VS Code does not really know what is inside of our special CS70 Docker image. When you compile inside of Docker, it will definitely be able to find them.

Want to Look at the Provided Data?

The data is located in the Docker image in /home/student/data/hw2/. If you want to take a look at it to make sure you are correctly reading data, you can use the cat command (short for ‘concatenate’). This command concatenates the contents of all the files specified in its arguments and dumps the result to the terminal. So, on the Docker command line you can try cat /home/student/data/hw2/us_population_growth.txt

Other Useful Unix Commands

Some other useful Unix commands, similar to cat, for looking at files on the command line are

  • head — view the first few lines of a file
  • tail — view the last few lines of a file
  • more — view a file pausing at each screenful

Feel free to try them out to see what they do; for example, run

head /home/student/data/hw2/us_population_growth.txt
tail /home/student/data/hw2/us_population_growth.txt
more /home/student/data/hw2/us_population_growth.txt

Another very useful command is man, which displays a “manual page” for the command you specify. So, for example, man man would show you the manpage for man itself, and man more shows you the manpage for more. Manpages show you what options and arguments a command takes, what they do, and sometimes include examples of how to use the command.

To Complete This Part of the Assignment…

You'll know you're done with this part of the assignment when you've done all of the following:

  • Write the plotExampleData function as described above.

(When logged in, completion status appears here.)