Question: #include application.h #include int main() { Application app; app.init(); while(app.getState() == Application::STATE_RUNNING) { app.draw(); app.setUserInput(_getch()); app.update(); } app.release(); return 0; } // INSTRUCTIONS // ------------
#include "application.h"
#include
int main()
{
Application app;
app.init();
while(app.getState() == Application::STATE_RUNNING)
{
app.draw();
app.setUserInput(_getch());
app.update();
}
app.release();
return 0;
}
// INSTRUCTIONS
// ------------
// Compile this code. After pressing any key to clear the instructions, You
// should see three rectangles labeled '#', 'b', and 'c'. You should also see
// two triangles, labeled 'd' and 'e'. Pressing '>' and '<' will change which
// shape is labeled '#'. Pressing 'w', 'a', 's', and 'd' will move the shape
// labeled '#'. Pressing 'space' will randomize the selected shape.
//
// Read through this code! Try to understand it before starting the assignment.
// Comment confusing lines with what you think code is doing, and experiment
// with existing code to test your understanding.
// Once you feel comfortable with this code, accomplish each of the following,
// and make sure your code compiles and runs after each step is completed.
//
// 1. Getting comfortable with the code
// a) Create a "makeRandom" method in both the Rect and Tri classes, based
// on the "makeRandomRect" and "makeRandomTri" functions in
// "application.cpp". The makeRandom function should take no parameters,
// and instead make itself random. Removing the old "makeRandomRect"
// and "makeRandomTri" functions as well. Be sure to consider what to do
// about "screenMin" and "screenMax".
// b) Create a print method for the Tri class, similar to the print method
// for the Rect class. This method may come in very handy when debugging.
// 2. Create Shape base class
// a) Create a header file (without a .cpp file) for a Shape class.
// b) Create the Shape class, which should have no member variables.
// c) Make the Shape class an interface for the Rect and Tri classes. Shape
// should have pure-virtual methods for each method that Rect and Tri have
// in common.
// d) Make sure Shape has a virtual destructor with an empty body.
// 3. Make Rect and Triangle extend Shape
// 4. Change selected
// a) Change the type of "Application::selected" from "void *" to "Shape *".
// b) Every piece of code that typecasts "selected" (and the logic around it)
// can be removed now. Simply call functions using the "Shape" interface.
// c) Remove the "selectedType" variable from Application. Logic that needs
// some form of RunTime Type Information should use dynamic_cast instead.
// 5. Merge all Shape objects into a single array
// a) Create an array of Shape pointers in the Application called "shapes".
// b) Making a complementary NUM_SHAPES variable would make sense.
// b) Remove "rectangles" and "triangles" arrays.
// c) Put each Tri and Rect object managed by the Application class into
// the "shapes" array. This will require re-factoring in multiple files.
// While removing references to "rectangles" and "triangles" arrays, it
// may make sense to replace pairs of for-loops using each of the old
// arrays with a single for-loop using just "shapes".
// 6. Make "shapes" dynamic
// a) Give Application::init() 2 parameters: int numRect, int numTri
// b) Make "shapes" a pointer of type "Shape **", and allocate it to be
// "numShapes" big, where "numShapes" is an int member of Application
// equal to (numRect + numTri), defined in Application::init().
// c) When calling "app.init()" in main, pass valid arguments for numRect
// and numTri.
// d) De-allocate the "shapes" array in Application::Release().
// 7. Clean up old variables
// a) Remove the TYPE_RECT and TYPE_TRI variables from Application.
// b) Remove NUM_TRI and NUM_RECT, and any NUM_SHAPES variable as well. Use
// numShapes where needed.
// 8. Add Circle class
// a) Create a header file AND a source file for a Circle class.
// b) Use Rect and Tri as examples to create the Circle class with.
// c) A Circle class should have at least a 2 dimensional position, and a
// radius.
// d) A simple algorithm for drawing a Circle will be similar to drawing a
// Rect or Tri, thought it might include the following code:
// float dx = center.x - col, dy = center.y - row;
// if( dx*dx + dy*dy <= radius * radius ) {
// moveCursor(col, row);
// putchar(letter);
// }
// e) Add an additional parameter to Application::init(), "int numCircles".
// Implement init to generate Circle objects along with Rect and Tri
// objects.
// 9. Implement add/remove for the shapes array
// a) Add code that increases the size of the shapes array, adding a random
// shape, whenever the '=' or '+' key is pressed. Take a look at the week
// 5 lecture showing explicit constructors for help with this algorithm.
// a) Add code that decreases the size of the shapes array, removing the last
// shape, whenever the '-' or '_' key is pressed. End the program when the
// last shape is removed. Removing a shape, like adding a shape, can be
// done by allocating an array of a different size (smaller this time).
consoleutil.cpp
#include
#include "consoleutil.h"
/**
* moves the console cursor to the given x/y coordinate
* @param x
* @param y
*/
void moveCursor(int x, int y)
{
COORD c = {x,y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
consoleutil.h
#pragma once
/** * moves the console cursor to the given x/y coordinate * @param x * @param y */ void moveCursor(int x, int y);
#define SCREEN_WIDTH 80 #define SCREEN_HEIGHT 24
application.cpp
#include "application.h"
#include
using namespace std;
#include "consoleutil.h"
#include
Vector2 screenMin(0,0), screenMax(SCREEN_WIDTH-1,SCREEN_HEIGHT-1);
void clearScreen()
{
Rect screen(screenMin, screenMax);
screen.draw(' ');
}
void makeRandomTri(Tri & t)
{
t.a = Vector2::random(screenMin, screenMax);
t.b = Vector2::random(screenMin, screenMax);
t.c = Vector2::random(screenMin, screenMax);
}
void makeRandomRect(Rect * r)
{
Vector2 randomv = Vector2::random(screenMin, screenMax);
r->min = randomv;
r->max = randomv;
randomv = Vector2::random(screenMin, screenMax);
r->add(randomv);
}
void Application::init()
{
for(int i = 0; i < NUM_RECT; ++i)
{
rectangles[i] = new Rect;
makeRandomRect(rectangles[i]);
}
for(int i = 0; i < NUM_TRI; ++i)
{
triangles[i] = new Tri;
makeRandomTri(*triangles[i]);
}
selectedIndex = 0;
selected = rectangles[0];
selectedType = Application::TYPE_RECT;
state = Application::STATE_RUNNING;
cout << "'<' and '>' to select shapes" << endl
<< "'w', 'a', 's', 'd' to move the selected shape" << endl
<< "'space' to randomize current shape" << endl
<< endl << "press any key to begin the program" << endl;
_getch();
clearScreen();
}
void Application::release()
{
for(int i = 0; i < NUM_RECT; ++i)
{
delete rectangles[i];
}
for(int i = 0; i < NUM_TRI; ++i)
{
delete triangles[i];
}
}
void Application::draw()
{
// draw all shapes
for(int i = 0; i < NUM_RECT; ++i)
{
rectangles[i]->draw('a'+i);
}
for(int i = 0; i < NUM_TRI; ++i)
{
triangles[i]->draw(NUM_RECT+'a'+i);
}
// draw the selected shape
switch(selectedType)
{
case TYPE_RECT:
((Rect*)selected)->draw('#');
break;
case TYPE_TRI:
((Tri*)selected)->draw('#');
break;
}
// draw extra info at the bottom
moveCursor(0, 0);
cout << "shape[" << selectedIndex << "], a ";
const char * shapeName;
if(selectedType == TYPE_RECT)
{
shapeName = "rectangle ";
}
else
{
shapeName = "triangle ";
}
cout << shapeName << endl;
}
void Application::setUserInput(int input)
{
userKeyPress = input;
}
void Application::update()
{
bool changedSelection = false;
bool shapeMoving = false;
switch(userKeyPress)
{
case 27:
state = Application::STATE_QUIT;
break;
case ',':
case '<':
selectedIndex--;
changedSelection = true;
break;
case '.':
case '>':
selectedIndex++;
changedSelection = true;
break;
case 'w':
case 'a':
case 's':
case 'd':
case ' ':
shapeMoving = true;
break;
}
if(shapeMoving)
{
switch(selectedType)
{
case TYPE_RECT:
((Rect*)selected)->draw(' ');
break;
case TYPE_TRI:
((Tri*)selected)->draw(' ');
break;
}
switch(userKeyPress)
{
case 'w':
switch(selectedType)
{
case TYPE_RECT:
((Rect*)selected)->translate(Vector2(0,-1));
break;
case TYPE_TRI:
((Tri*)selected)->translate(Vector2(0,-1));
break;
}
break;
case 'a':
switch(selectedType)
{
case TYPE_RECT:
((Rect*)selected)->translate(Vector2(-1,0));
break;
case TYPE_TRI:
((Tri*)selected)->translate(Vector2(-1,0));
break;
}
break;
case 's':
switch(selectedType)
{
case TYPE_RECT:
((Rect*)selected)->translate(Vector2(0,+1));
break;
case TYPE_TRI:
((Tri*)selected)->translate(Vector2(0,+1));
break;
}
break;
case 'd':
switch(selectedType)
{
case TYPE_RECT:
((Rect*)selected)->translate(Vector2(+1,0));
break;
case TYPE_TRI:
((Tri*)selected)->translate(Vector2(+1,0));
break;
}
break;
case ' ':
switch(selectedType)
{
case TYPE_RECT:
makeRandomRect( (Rect*)selected );
break;
case TYPE_TRI:
makeRandomTri( *((Tri*)selected) );
break;
}
break;
}
}
userKeyPress = 0;
if(changedSelection)
{
int TOTAL_NUMBER_OF_SHAPES = NUM_RECT + NUM_TRI;
if(selectedIndex < 0)
{
selectedIndex = TOTAL_NUMBER_OF_SHAPES-1;
}
else if (selectedIndex >= TOTAL_NUMBER_OF_SHAPES)
{
selectedIndex = 0;
}
if(selectedIndex >= 0 && selectedIndex < NUM_RECT)
{
selectedType = TYPE_RECT;
selected = rectangles[selectedIndex];
}
else if(selectedIndex >= NUM_RECT && selectedIndex < NUM_RECT+NUM_TRI)
{
selectedType = TYPE_TRI;
selected = triangles[selectedIndex - NUM_RECT];
}
if(selectedIndex < 0 || selectedIndex >= TOTAL_NUMBER_OF_SHAPES)
{
selected = 0;
selectedType = -1;
}
}
}
application.h
#pragma once
#include "rect.h"
#include "tri.h"
class Application
{
private:
int state;
int userKeyPress;
/** what shape is selected */
int selectedIndex;
/** address to the selected object */
void * selected;
/** type of the selected object */
int selectedType;
/** which type the "selected" pointer could be pointed at */
static const int TYPE_RECT = 1, TYPE_TRI = 2;
int numShapes;
public:
static const int NUM_RECT = 3, NUM_TRI = 2;
Rect * rectangles[NUM_RECT];
Tri * triangles[NUM_TRI];
static const int
STATE_INIT = 0, // an application not yet fully initialized
STATE_RUNNING = 1, // running
STATE_QUIT = 2; // user requested quit
/** @return Application::STATE_* */
int getState(){ return state; }
Application():state(STATE_INIT){}
void init();
void release();
void draw();
void setUserInput(int input);
void update();
};
rect.cpp
#include "consoleutil.h"
#include "rect.h"
#include
void Rect::print()
{
printf("[(%f, %f), (%f, %f)]", min.x, min.y, max.x, max.y);
}
void Rect::draw(char letter)
{
Vector2 i;
for(i.y = (float)((int)min.y); i.y < max.y; ++i.y)
{
for(i.x = (float)((int)min.x); i.x < max.x; ++i.x)
{
// only print in a standard console window
if(i.isWithin(0, 0, 80, 24))
{
moveCursor((int)i.x, (int)i.y);
putchar(letter);
}
}
}
}
void Rect::translate(Vector2 delta)
{
min += delta;
max += delta;
}
float Rect::getArea()
{
return getWidth() * getHeight();
}
/** @param v if the argument is out of the Rect, the Rect will extend */
void Rect::add(Vector2 v)
{
if(v.x < min.x) { min.x = v.x; }
if(v.y < min.y) { min.y = v.y; }
if(v.x > max.x) { max.x = v.x; }
if(v.y > max.y) { max.y = v.y; }
}
rect.h
#pragma once
#include "vector2.h"
class Rect
{
public:
Vector2 min, max;
Rect(){}
Rect(Vector2 min, Vector2 max):min(min),max(max){}
float getWidth(){return max.x-min.x;}
float getHeight(){return max.x-min.x;}
Vector2 getCenter(){ return (min+max)/2; }
void draw(char letter);
void translate(Vector2 delta);
float getArea();
void print();
/** @param v if the argument is out of the Rect, the Rect will extend */
void add(Vector2 v);
};
tri.cpp
#include "tri.h"
#include "consoleutil.h"
#include
float Tri::getWidth() {return (b-a).magnitude();}
float Tri::getHeight() {
// calculate base line, which is a->b
Vector2 baseSlope = b - a;
// create perpendicular slope, which goes along the height
Vector2 perpSlope = baseSlope;
perpSlope.x = -baseSlope.y;
perpSlope.y = baseSlope.x;
// get distance of point C from the base
Vector2 basePoint;
float dist;
Vector2::lineIntersection(a, b, c, c+perpSlope, dist, basePoint);
if(dist < 0) {
dist *= -1;
}
return dist;
}
#include "rect.h"
void Tri::draw(char letter) {
Rect area;
area.add(a);
area.add(b);
area.add(c);
Vector2 i;
for(i.y = (float)((int)area.min.y); i.y < area.max.y; ++i.y) {
for(i.x = (float)((int)area.min.x); i.x < area.max.x; ++i.x) {
if(i.isWithin(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
&& i.isInsideTriangle(a, b, c)) {
moveCursor((int)i.x, (int)i.y);
putchar(letter);
}
}
}
}
void Tri::translate(Vector2 delta) {
a += delta;
b += delta;
c += delta;
}
float Tri::getArea() {
return (getWidth() * getHeight() ) / 2;
}
tri.h
#pragma once
#include "vector2.h"
class Tri
{
public:
Vector2 a, b, c;
Tri(){}
Tri(Vector2 a, Vector2 b, Vector2 c):a(a),b(b),c(c){}
float getWidth();
float getHeight();
/** @return average location (TODO replace with "triangle centroid") */
Vector2 getCenter(){ return (a+b+c)/3; }
void draw(char letter);
void translate(Vector2 delta);
float getArea();
};
vector2.cpp
#include "vector2.h"
#include
/** @return true if the given x, y coordinate mathes this Vector2's data */
bool Vector2::is(float x, float y) const
{
return this->x == x && this->y == y;
}
/** @return true if the given x, y coordinate mathes this Vector2's data */
bool Vector2::is(Vector2 xy) const
{
return xy.x == x && xy.y == y;
}
/** @return true if the Vector2 is within the given rectangular boundary */
bool Vector2::isWithin(float minx, float miny, float maxx, float maxy)
{
return x >= minx && y >= miny && x < maxx && y < maxy;
}
/** re-initialize */
void Vector2::init(float a_x, float a_y)
{
x = a_x;
y = a_y;
}
/** @return pythagorean distance from the origin */
float Vector2::magnitude()
{
return std::sqrt(x*x+y*y);
}
Vector2 Vector2::operator+(Vector2 const & v) const { return Vector2(x+v.x, y+v.y); }
Vector2 Vector2::operator-(Vector2 const & v) const { return Vector2(x-v.x, y-v.y); }
Vector2 Vector2::operator*(float value) const { return Vector2(x*value, y*value); }
Vector2 Vector2::operator/(float value) const { return Vector2(x/value, y/value); }
Vector2 & Vector2::operator+=(Vector2& v) { x += v.x; y += v.y; return *this; }
Vector2 & Vector2::operator-=(Vector2& v) { x -= v.x; y -= v.y; return *this; }
Vector2 & Vector2::operator*=(float value) { x *= value; y *= value; return *this; }
Vector2 & Vector2::operator/=(float value) { x /= value; y /= value; return *this; }
#include "stdlib.h"
float randInUnitInterval()
{
const int RANDMAX = 32768;
return (rand() % RANDMAX)/((float)RANDMAX);
}
/** @return a random Vector2 within the specified boundary */
Vector2 Vector2::random(Vector2 min, Vector2 max)
{
float w = max.x-min.x, h = max.y-min.y;
return Vector2(
randInUnitInterval() * w + min.x,
randInUnitInterval() * h + min.y);
}
/**
* @param A,B line 1
* @param C,D line 2
* @param point __OUT to the intersection of line AB and CD
* @param dist __OUT the distance along line AB to the intersection
* @return true if intersection occurs between the lines
*/
bool Vector2::lineIntersection(const Vector2 & A, const Vector2 & B,
const Vector2 & C, const Vector2 & D,
float & dist, Vector2 & point)
{
float rTop = (A.y-C.y)*(D.x-C.x)-(A.x-C.x)*(D.y-C.y);
float rBot = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);
float sTop = (A.y-C.y)*(B.x-A.x)-(A.x-C.x)*(B.y-A.y);
float sBot = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);
if ( (rBot == 0) || (sBot == 0))
{
//lines are parallel
return false;
}
float r = rTop/rBot;
float s = sTop/sBot;
Vector2 delta = B - A;
dist = delta.magnitude() * r;
point = A + ( delta * r);
return ( (r > 0) && (r < 1) && (s > 0) && (s < 1) );
}
/**
* @return positive if v2 is clockwise of this vector
* (assume Y points down, X to right)
*/
float Vector2::sign(const Vector2 & v) const
{
return (x*v.y) - (y*v.x);
}
/** @return true if this point is inside the given triangle */
bool Vector2::isInsideTriangle(Vector2 const & a, Vector2 const & b,
Vector2 const & c) const
{
float signab = (*this-a).sign(b-a),
signbc = (*this-b).sign(c-b),
signac = (*this-c).sign(a-c);
return(((signab>=0) == (signbc>=0)) && ((signbc>=0) == (signac>=0)))
||(((signab<=0) == (signbc<=0)) && ((signbc<=0) == (signac<=0)));
}
vector2.h
#pragma once
/**
* Object Oriented implementation of a 2 dimensional (math) vector
*/
struct Vector2
{
/** the x, y coordinates */
float x, y;
/** initializes the Vector2 */
Vector2(float x, float y)
{
this->x = x;
this->y = y;
}
/** default constructor - sets x,y to 0,0 */
Vector2()
{
x = 0;
y = 0;
}
/** @return true if the given x, y coordinate mathes this Vector2's data */
bool is(float x, float y) const;
/** @return true if the given x, y coordinate mathes this Vector2's data */
bool is(Vector2 xy) const ;
/** @return true if the Vector2 is within the given rectangular boundary */
bool isWithin(float minx, float miny, float maxx, float maxy);
/** re-initialize */
void init(float x, float y);
/** @return a random Vector2 within the specified boundary */
static Vector2 random(Vector2 min, Vector2 max);
/** @return pythagorean distance from the origin */
float magnitude();
Vector2 operator+(Vector2 const & v) const;
Vector2 operator-(Vector2 const & v) const;
Vector2 operator*(float value) const;
Vector2 operator/(float value) const;
Vector2 & operator+=(Vector2& v);
Vector2 & operator-=(Vector2& v);
Vector2 & operator*=(float value);
Vector2 & operator/=(float value);
/**
* @param A,B line 1
* @param C,D line 2
* @param point __OUT to the intersection of line AB and CD
* @param dist __OUT the distance along line AB to the intersection
* @return true if intersection occurs between the lines
*/
static bool lineIntersection(const Vector2 & A, const Vector2 & B,
const Vector2 & C, const Vector2 & D,
float & dist, Vector2 & point);
/**
* @return positive if v2 is clockwise of this vector
* (assume Y points down, X to right)
*/
float sign(const Vector2 & v) const;
/** @return true if this point is inside the given triangle */
bool isInsideTriangle(Vector2 const & a, Vector2 const & b,
Vector2 const & c) const;
};
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
