Shapes

Problem

  • You’re making an app that allows kids to create pictures using rectangles and circles of different sizes and colors.
  • A pictures is now stored as a file with one shape per line
  • Input format for a rectangle is (unit is pixels)
    • color Rectangle height width x y
  • Input format for a circle is
    • color Circle radius x y
  • All shapes should have an ID (in case two shapes are otherwise identical)

Problem-solving

  • It helps tremendously to draw the inheritance hierarchy for this problem and place data members and functions in the hierarchy before try to implement the solution

Part I

  • Your app will
    • read in a file of shapes
    • create an object for each shape
    • print a summary of each shape
  • For starters, create the base class and create objects of this class
  • Your printed summary should include the color and unique ID. For example, for a rectangle:
    • ID#0, Yellow
  • For a circle:
    • ID#1, Blue

Part II

  • Next create derived classes and create objects of these classes instead of the base class
  • To the printed summary, add type and dimensions. For example, for a rectangle:
    • Rectangle (25 x 30), ID#0, Yellow
  • For a circle:
    • Circle (radius 12), ID#1, Blue
  • Let the user select a color to change all of the shapes to before they are printed to the console

Part III

  • Finally, add functions to find area and edge length for a shape
  • To the printed summary, add area and edge length. For example, for a rectangle:
    • Rectangle (25 x 30), ID#0, Yellow – area:750 – edge:110
  • For a circle:
    • Circle (radius 12), ID#1, Blue – area:452.39 – edge:75.4

Unimplemented Tasks

Scaling

  • What if we wanted to also be able to scale an object’s size?
  • Let the user scale all objects before printing them to the console

Moving

  • What if we wanted to include x, y coordinates of the shape’s center in the shape summary?
  • What if we wanted to be able to move a shape (positive y meaning up and positive x meaning right)?
  • Allow the user to move all shapes’ positions by a certain amount before printing them to the console

Solution

Source.cpp

/*
Test case classes (you should provide specific test cases)
 
Test file should include both Circle and a Rectangle
 
Test file should include shapes with dimension values of 0
 
Test file should include shapes of many different colors and sizes
 
*/
 
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "Circle.h"
#include "Rectangle.h"
#include "Shape.h"
 
using namespace std;
 
void create_objects_from_file(string filename, vector<Shape *> & shapes);
 
int main()
{
	vector<Shape *> shapes;
 
	// 1. READ IN A FILE AND CREATE AN OBJECT FOR EACH SHAPE
	cout << "Filename:" << endl;
	string filename;
	getline(cin, filename);
 
	// static helper function
	create_objects_from_file(filename, shapes);
 
	// Let the user select a color to change all of the shapes to before they are printed to the console
	string user_fav_color;
	cout << "Fav color: ";
	getline(cin, user_fav_color);
 
	// 2. PRINT A SUMMARY OF THE SHAPES (to_string() is fine)
	for (int i = 0; i < shapes.size(); i++)
	{
		// All derived classes have access to the base class set_color() function
		shapes[i]->set_color(user_fav_color);
 
		// Each derived class implements and executes these differently. HERE IS POLYMORPHISM
		cout << shapes[i]->to_string() << endl;
		cout << "\tAREA:" << shapes[i]->area() << endl;
		cout << "\tEDGE:" << shapes[i]->edge_length() << endl;
	}
 
	system("pause");
	return 0;
}
 
/*
Function to load contents of a file and create shape objects depending on file contents
@param filename name of file to load shapes from
@param shapes vector into which to load new shape objects
*/
void create_objects_from_file(string filename, vector<Shape *> & shapes)
{
	ifstream in_file;
 
	in_file.open(filename);
	// Note: we don't test if the filename is valid or not
 
	string line;
	// loop continues as long as another line can be read
	while (getline(in_file, line))
	{
		// cout << "LINE: " << line << endl;
		// use an istringstream when trying to read different data types from a string
		istringstream strm(line);
		string color;
		string shape_type;
 
		strm >> color;
		strm >> shape_type;
 
		// This is a pointer to the shape we are going to create for the given line
		Shape * new_shape = NULL;
		if (shape_type == "Rectangle")
		{
			int width, height;
			strm >> width >> height;
 
			// The substitution principle allows us to put an object of a derived class
			// where an object of the base class is expected
			new_shape = new Rectangle(color, width, height);
		}
		else if (shape_type == "Circle")
		{
			int radius;
			strm >> radius;
 
			// The substitution principle allows us to put an object of a derived class
			// where an object of the base class is expected
			new_shape = new Circle(color, radius);
		}
		// cout << "shape created with color: " << new_shape->get_color() << endl;
 
		// this is NULL if the shape_type was neither Rectangle nor Circle
		if (new_shape != NULL)
		{
			shapes.push_back(new_shape);
		}
	}
 
}

Shape.h

#include <string>
 
using namespace std;
 
#pragma once
class Shape
{
public:
	// CONSTRUCTOR
	/*
	Default constructor for a Shape object
	*/
	Shape();
 
	// OTHER MEMBER FUNCTIONS
	/*
	@return color of *this* Shape object
	*/
	string get_color() const;
 
	/*
	@param new_color color to be set as *this* Shape object's color
	*/
	void set_color(string new_color);
 
	/*
	@return the unique id for this Shape
	*/
	int get_id() const;
 
	/*
	Function to compute shape's area. Note that this can't be computed for a generic shape
	hence why it is a pure virtual function
	@return the area of *this* Shape object
	*/
	// a Pure Virtual Function must, by definition, be overwritten by any derived class
	virtual double area() const = 0;
 
	/*
	Function to compute shape's perimeter edge length. Note that this can't be computed for a generic shape
	hence why it is a pure virtual function
	@returns the perimeter of *this* Shape object
	*/
	// a Pure Virtual Function must, by definition, be overwritten by any derived class
	virtual double edge_length() const = 0;
 
	/*
	@return a string representation of *this* Shape object
	*/
	virtual string to_string() const;
 
private:
	// DATA MEMBERS
	string color;
	int id;
 
	// static variables exist once for all instances of the class
	static int next_id;
};

Shape.cpp

Note that even though Shape is an abstract class (by virtue of having a pure virtual function), there is still an implementation file.

#include <sstream>
#include "Shape.h"
 
// initialize our static variable
int Shape::next_id = 0;
 
Shape::Shape()
{
	color = "";
	id = next_id; // static variable at work
	next_id++;
}
 
string Shape::get_color() const
{
	return color;
}
 
void Shape::set_color(string new_color)
{
	color = new_color;
}
 
int Shape::get_id() const
{
	return id;
}
 
string Shape::to_string() const
{
	// use an ostringstream when creating a string from different data types
	ostringstream strm;
 
	strm << "ID# " << id << ", " << color;
 
	return strm.str();
}

Circle.h

#pragma once
// to BE a Shape, we must know what a Shape is
#include "Shape.h"
 
// Here is where we define that a Circle IS-A Shape and inherits all of its members and functions
class Circle : public Shape
{
public:
	// CONSTRUCTOR
	/*
	Constructor to construct a new Circle object
	@param _color color of new Circle object
	@param _radius radius of the new Circle object
	*/
	Circle(string _color, int _radius);
 
	// MEMBER FUNCTION
	/*
	@return a string representation of *this* Circle object
	*/
	virtual string to_string() const;
 
	/*
	@return area of *this* Circle object
	*/
	virtual double area() const;
 
	/*
	@return perimeter edge length of *this* Circle object
	*/
	virtual double edge_length() const;
 
private:
	// DATA MEMBERS
	int radius;
 
	// Note that this inherits all other data members and member functions that it needs from the base class
};

Circle.cpp

#include <sstream>
#include "Circle.h"
 
 
Circle::Circle(string _color, int _radius)
	:Shape(), radius(_radius)
	// Everything after ':' is the initializer list
	// The Shape() constructor is called by default but we include it for emphasis
{
	set_color(_color);
}
 
string Circle::to_string() const
{
	// use an ostringstream when creating a string from different data types
	ostringstream strm;
 
	strm << "Circle (radius " << radius << "), "
		<< Shape::to_string();
 
	return strm.str();
}
 
double Circle::area() const
{
	return 3.14159 * radius * radius;
}
 
double Circle::edge_length() const
{
	return 2 * 3.14159 * radius;
}

Rectangle.h

#pragma once
// to BE a Shape, we must know what a Shape is
#include "Shape.h"
 
// Here is where we define that a Rectangle IS-A Shape and inherits all of its members and functions
class Rectangle : public Shape
{
public:
	// CONSTRUCTOR
	/*
	Constructor to construct a new Rectangle object
	@param _color color of new Rectangle object
	@param _radius radius of the new Rectangle object
	*/
	Rectangle(string _color, int _width, int _height);
 
	// MEMBER FUNCTION
	/*
	@return a string representation of *this* Rectangle object
	*/
	virtual string to_string() const;
 
	/*
	@return area of *this* Rectangle object
	*/
	virtual double area() const;
 
	/*
	@return perimeter edge length of *this* Rectangle object
	*/
	virtual double edge_length() const;
 
private:
	int width;
	int height;
 
	// Note that this inherits all other data members and member functions that it needs from the base class
};

Rectangle.cpp

#include <sstream>
#include "Rectangle.h"
 
 
Rectangle::Rectangle(string _color, int _width, int _height)
	:Shape(), width(_width), height(_height)
	// Everything after ':' is the initializer list
	// The Shape() constructor is called by default but we include it for emphasis
{
	set_color(_color);
}
 
string Rectangle::to_string() const
{
	// use an ostringstream when creating a string from different data types
	ostringstream strm;
 
	strm << "Rectangle (" << width << " x " << height << "), "
		<< Shape::to_string();
 
	return strm.str();
}
 
double Rectangle::area() const
{
	return width * height;
}
 
double Rectangle::edge_length() const
{
	return 2 * width + 2 * height;
}

shapes.txt

Here is a possible input file

Red Circle 38 12 22
Blue Circle 18 56 79
Green Rectangle 22 42 190 76
Yellow Circle 78 78 190
Black Circle 1 78 78
Supercalifragilisticexpialidocious this is the only error we test
Grey Circle 3 78 78
Black Circle 5 78 78
Green Rectangle 10 10 100 100
Pink Circle 10 50 50
cs-142/shapes.txt · Last modified: 2015/06/09 19:41 by cs142ta
Back to top
CC Attribution-Share Alike 4.0 International
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0