CSCI 261 Programming Concepts (C++)

Winter/Spring 2012

Homework 28: Moving Squares

Right-click me and select "Save target as..." to save.

Remember to place the 28_movingSquares folder inside your toolkit, next to the other assignments.

Concepts

Focus on one main concept for this assignment: how to use a sine function to model motion that oscillates back and forth.

Using sine to Model Oscillating Motion

Consider a function that you likely take for granted: sine. You may know it in a trigonometric context, where sine gives you the ratio between two sides of a triangle given a particular angle. But consider the numbers that sine generates:


Attribution: wikipedia

What happens to the value of "y" (labeled as f(x) on the vertical axis above) as "x" increases indefinitely? The value returned by sine "oscillates" between 1 and -1 over and over again. This behavior is useful for modeling all sorts of wavelike phenomena.

In particular, this behavior is also useful for modeling the motion of an object that oscillates back and forth. In this assignment, you will use the sine function to modify the vertical position of a simple Square. When implemented correctly, you'll witness an animation of your Square object, in which it moves up and down smoothly, oscillating back and forth.

Instructions

This is a challenge!

Implement a program that draws a row of colored squares on the screen in which the squares "bounce" up and down. While the actual drawing work has been implemented for you, there is one critical component missing: a Square class that meets certain requirements in order for the application to work.

As such, your goal is to implement the Square class given the specifications below. In addition, a test suite is provided to help guide your implementation and ensure that your Square will work with the graphics-generating code.

Your goal is to implement a Square class that meets these requirements:

  1. It should have a default constructor. The constructor will not be used, and for this assignment, should not "do" anything and consist of an empty body. Because we are defining a parameterized constructor (below), we must implement a default constructor to satisfy the C++ compiler. In general, default constructors assign default values to member variables. We're telling you that it doesn't have to do anything merely to save you a little time (despite the strange convention).
  2. It should have the following private member variables:
    • int sideLength which represents the length of one side of the square.
    • int red which will store the "red" RGB value
    • int green which will store the "green" RGB value
    • int blue which will store the "blue" RGB value
    • int x which will store the horizontal position
    • int y which will store the vertical position
    • int initialVerticalPosition which will represent the "middle" vertical position as the Square oscillates up and down. For example, when the initialVerticalPosition is 250, the Square will oscillate with a vertical "y" position above and below 250 (eg, values 150 to 350).
  3. It should have a parameterized constructor that:
    • Accepts six int parameters, in this order:
      • int length which should be assigned to the Square's sideLength
      • int r which should be assigned to the Square's red attribute
      • int g which should be assigned to the Square's green attribute
      • int b which should be assigned to the Square's blue attribute
      • int initX which should be assigned to the Square's x attribute
      • int initY which should be assigned to the Square's y attribute and the Square's initialVerticalPosition attribute
  4. It should have the following "getter" accessor methods:
    • int getRed() which returns the value of the red attribute
    • int getGreen() which returns the value of the green attribute
    • int getBlue() which returns the value of the blue attribute
    • int getX() which returns the value of the x attribute
    • int getY() which returns the value of the y attribute
    • int getSideLength() which returns the value of the sideLength attribute
    • int getInitialVerticalPosition() which returns the value of the initialVerticalPosition attribute
  5. It should have a move function that is implemented as follows:
    void Square::move(double angle) {
        y = 34 * sin(angle) + initialVerticalPosition;
    }

    This function will be called in testApp.cpp, and will be passed a value, angle, that continually increases. Note that as angle continually increases, the Square's y attribute will be oscillating with a value +/- initialVerticalPosition.

    You should be asking, what is 34 doing there? Since sin(angle) returns a value between -1 and 1, we're multiplying that return value by 34 to get numbers between 34 and -34. But, why 34? We found this amount of change results in a "nice" amount of motion and is arbitrary.

Note that a test suite also runs, which is called in main and generates output to the console. If you meet the requirements above and pass the test suite, your program will compile and run, and squares should move up and down on the screen!

How It Works (Reading Code!)

We've implemented the graphics work to keep you focused on classes, but take a look at main to see how the program works. First it runs your test suite, then it instantiates an ofAppGlutWindow (a window for 3d graphics). It then starts your OpenFrameworks app in testApp.cpp.

Take a look at testApp.cpp and what setup(), update() and draw() do. Remember, when your OpenFrameworks application starts, it calls setup() once, and then calls draw() over and over again. Note that it also calls update(), before each call to draw(). The update() function is where "work" is done before draw()ing things.

setup() calls generateSquares(). Take a look at the function! It opens up a data file containing RGB color values, and uses those values to instantiate some number of squares and "stores" them in a vector called squares.

update() updates the "movement amount," which is really the angle value that will be passed to the Square's move() member function. We chose to increment this slowly, at a rate of 0.1. Since update() is being called over and over again automatically by OpenFrameworks, it updates the global variable angle (at the top of testApp.cpp). This is the horizontal "x" value ultimately given to sine, as shown in the graph near the top of this page.

update() then moves all the squares in the squares vector. Notice the "method chaining" in the for loop of moveSquares(). Read it from left to right. What is squares? It's a vector of Square objects. What is squares.at(i)? It's a square. Wow! The multiple dots there is a chain of methods, as if telling the computer, "Give me one Square in the vector and call move(angle) on it."

Lastly, draw() just draws the squares. See the drawSquares() function to see more examples of method chaining and how each Square's attributes are used to set the color, size and position of the graphics.

Hints

Start with the required specifications above first. Try implementing the Square class according to the requirements above, and then rely on the test suite.

The console window will display the output of the test suite (eg, "PASSED" or "FAILED").

The code in test.cpp (and testApp.cpp) illustrate the expected API that your Square class must support.

Ask questions! While software requirements are written to be as clear and specific as possible, we frequently must ask additional questions when we have trouble interpreting requirements as code.

If you'd like to change the colors of the squares, change the numbers in the file 28_movingSquares\bin\data\colors.cfg.txt. Take a look at testApp.cpp's generateSquares() function to see how it reads data from the file and uses those numbers to instantiate Squares objects of a particular color.

Requirements and Rubric

A friendly message from The Terminator, our grading program

Hello ag *bzzzt* again. I will check for the following:


Your program must not print "FAILED" on the console in order to receive full credit.

You must not modify main.cpp (except adding your name), test.cpp or test.h.

Your class definitions should be in square.h and square.cpp, as provided.


I will *bzzzt* try to break your Square with my tests. Can you *bzzt* defeat me?

This work is worth 190 points.

Requirement Points Notes
Place your name in the comment header in main.cpp 5
Correct submission of src directory as a .zip file. 5
Passes all six tests (30 points each) 180

Concepts Exercised: OOP, classes, unit tests, reading code, graphics, mathematics, animation, specification comprehension