diff --git a/labs/inheritance/cp1/input.txt b/labs/inheritance/cp1/input.txt new file mode 100644 index 0000000..a5164a6 --- /dev/null +++ b/labs/inheritance/cp1/input.txt @@ -0,0 +1,31 @@ +black (1,0) (0.5,0.866) (1,1.732) (3,1.732) (3.5,0.866) (3,0) +blue (0,0) (0,1) (1,1) (1,0) +brown (2,0) (0,1) (3,3) +cyan (0,0) (0,2) (1,0) +green (0,0) (0,1) (1,0) +grey (1,0) (1.5,0.866) (1,1.732) (2,1.732) (2.5,0.866) (2,0) +magenta (0,0) (1,1) (4,1) (3,0) +olive (0,0) (1,2) (6,3) (4,0) +orange (0,0) (0,3) (2,3) (2,0) +pink (1,0) (0,2) (4,2) (3,0) +purple (0,0) (0,2) (3,1) +red (0,0) (0.5,0.866) (1,0) +teal (1,0) (0,3) (1,4) (2,3) +white (2,0) (0,1) (3,4) (6,1) (4,0) +yellow (0,1) (2,2) (4,1) (2,0) + + + + + + + + + + + + + + + + diff --git a/labs/inheritance/cp2/output_simple.txt b/labs/inheritance/cp2/output_simple.txt new file mode 100644 index 0000000..c4eda00 --- /dev/null +++ b/labs/inheritance/cp2/output_simple.txt @@ -0,0 +1,8 @@ +7 Polygon(s): blue brown grey olive orange purple red +3 Triangle(s): brown purple red +2 IsoscelesTriangle(s): purple red +1 EquilateralTriangle(s): red +3 Quadrilateral(s): blue olive orange +2 Rectangle(s): blue orange +1 Square(s): blue +3 Shape(s) with all equal sides: blue grey red diff --git a/labs/inheritance/cp2/simple.txt b/labs/inheritance/cp2/simple.txt new file mode 100644 index 0000000..353859c --- /dev/null +++ b/labs/inheritance/cp2/simple.txt @@ -0,0 +1,7 @@ +blue (0,0) (0,1) (1,1) (1,0) +brown (2,0) (0,1) (3,3) +grey (1,0) (1.5,0.866) (1,1.732) (2,1.732) (2.5,0.866) (2,0) +olive (0,0) (1,2) (6,3) (4,0) +orange (0,0) (0,3) (2,3) (2,0) +purple (0,0) (0,2) (3,1) +red (0,0) (0.5,0.866) (1,0) diff --git a/labs/inheritance/cp2/simple_main.cpp b/labs/inheritance/cp2/simple_main.cpp new file mode 100644 index 0000000..d3ebe2d --- /dev/null +++ b/labs/inheritance/cp2/simple_main.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "polygons.h" + +// helper function prototypes +Polygon* CreatePolygon(const std::string &name, const std::vector &points); +void OutputStats(const std::vector &polygons, std::ofstream &ostr); + +// ------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) { + + // command line arguments & opening files + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " input.txt output.txt" << std::endl; + exit(1); + } + std::ifstream istr(argv[1]); + if (!istr) { + std::cerr << "ERROR: could not open " << argv[1] << std::endl; + exit(1); + } + std::ofstream ostr(argv[2]); + if (!ostr) { + std::cerr << "ERROR: could not open " << argv[2] << std::endl; + exit(1); + } + + // the master container of polygons + std::vector polygons; + + // read the input file one line at a time + std::string line; + while (getline(istr,line)) { + std::stringstream ss(line); + std::string name, token; + if (!(ss >> name)) continue; + std::vector points; + while (ss >> token) { + std::stringstream ss2(token); + char c; + double x,y; + ss2 >> c; + assert (c == '('); + ss2 >> x; + ss2 >> c; + assert (c == ','); + ss2 >> y; + ss2 >> c; + assert (c == ')'); + points.push_back(Point(x,y)); + } + assert (points.size() >= 3); + Polygon* p = CreatePolygon(name,points); + // add the new polygon to the master container + polygons.push_back(p); + } + + // write out the statistics + OutputStats(polygons,ostr); + + // delete the dynamically allocated polygons + for (int i = 0; i < polygons.size(); i++) { + delete polygons[i]; + } +} + +// ------------------------------------------------------------------------------ + +// This function determines the most specific type of shape that can +// be created from the sequence of points. It does this by process of +// elimination. Note that the order in which it attempts to create +// the shapes is important. + +Polygon* CreatePolygon(const std::string &name, const std::vector &points) { + Polygon *answer = NULL; + try{ + answer = new EquilateralTriangle(name,points); + } + catch (int) { + try { + answer= new IsoscelesTriangle(name,points); + } + catch (int) { + try { + answer= new Triangle(name,points); + } + catch (int) { + try { + answer= new Square(name,points); + } + catch (int) { + try { + answer= new Rectangle(name,points); + } + catch (int) { + try { + answer= new Quadrilateral(name,points); + } + catch (int) { + answer= new Polygon(name,points); + } + } + } + } + } + } + assert (answer != NULL); + return answer; +} + +// ------------------------------------------------------------------------------ + +// This function prints the output. C++ macros are used to abbreviate +// some repetitive code. The function call-like macros are actually +// replaced using substitution by the preprocessor before the code is +// given to the compiler. (You are not required to understand the +// details of the macros. You do not need to edit this code.) + +void OutputStats(const std::vector &polygons, std::ofstream &ostr) { + + // define and initialize variables +# define InitializeCount(type) std::vector all_##type + InitializeCount(Polygon); + InitializeCount(Triangle); + InitializeCount(IsoscelesTriangle); + InitializeCount(EquilateralTriangle); + InitializeCount(Quadrilateral); + InitializeCount(Rectangle); + InitializeCount(Square); + std::vector equal_sides; + + // count & record the names of shapes in each category + for (std::vector::const_iterator i = polygons.begin(); i!=polygons.end(); ++i) { +# define IncrementCount(type) if (dynamic_cast (*i)) all_##type.push_back((*i)->getName()) + IncrementCount(Polygon); + IncrementCount(Triangle); + IncrementCount(IsoscelesTriangle); + IncrementCount(EquilateralTriangle); + IncrementCount(Quadrilateral); + IncrementCount(Rectangle); + IncrementCount(Square); + if ((*i)->HasAllEqualSides()) equal_sides.push_back((*i)->getName()); + } + + + // output data for each category, sorted alphabetically by the shape's name +# define PrintVector(vecname) std::sort((vecname).begin(),(vecname).end()); \ + for (unsigned int j = 0; j < (vecname).size(); j++) { ostr << " " << (vecname)[j]; } ostr << std::endl +# define PrintCount(type) do { ostr << all_##type.size() << " " #type"(s): "; PrintVector(all_##type); } while (0) + PrintCount(Polygon); + PrintCount(Triangle); + PrintCount(IsoscelesTriangle); + PrintCount(EquilateralTriangle); + PrintCount(Quadrilateral); + PrintCount(Rectangle); + PrintCount(Square); + ostr << equal_sides.size() << " Shape(s) with all equal sides: "; + PrintVector(equal_sides); +} + +// ------------------------------------------------------------------------------ diff --git a/labs/inheritance/cp2/utilities.h b/labs/inheritance/cp2/utilities.h new file mode 100644 index 0000000..e0d1ec6 --- /dev/null +++ b/labs/inheritance/cp2/utilities.h @@ -0,0 +1,120 @@ +// ========================================= +// +// IMPORTANT NOTE: DO NOT EDIT THIS FILE +// +// ========================================= + +#ifndef _UTILITIES_H_ +#define _UTILITIES_H_ + +#include +#include +#include + +// epsilon values used in comparing the edge lengths & angles between +// edges note that these values are dependent on the precision of +// the coordinates and the overall scale of the objects +#define DISTANCE_EPSILON 0.0001 +#define ANGLE_EPSILON 0.1 + +// ----------------------------------------------------------------- + +// Stores a 2D coordinate +class Point { +public: + Point(double _x, double _y) : x(_x),y(_y) {} + double x; + double y; +}; + +// Stores a 2D vector, constructed from 2 Points +class Vector { +public: + Vector(const Point &a, const Point &b) { dx = b.x-a.x; dy = b.y-a.y; } + double Length() const { return sqrt(dx*dx+dy*dy); } + void Normalize() { + // make this a unit vector (length = 1) + double length = Length(); + if (length < DISTANCE_EPSILON) throw std::string("LENGTH = 0"); + assert (length > DISTANCE_EPSILON); + dx /= length; + dy /= length; + } + // representation + double dx; + double dy; +}; + + +inline std::ostream& operator<< (std::ostream &ostr, const Vector &v) { + ostr << "<" << v.dx << "," << v.dy << ">"; + return ostr; +} + + +// ----------------------------------------------------------------- + +// calculate the length of an edge, the distance between 2 points +inline double DistanceBetween(const Point &a, const Point &b) { + Vector v(a,b); + return v.Length(); +} + +// Calculate the angle at vertex b in degrees, that is, the angle +// between edges ab and bc. This will return a positive number +// measured as the clockwise rotation in the xy plane from point c to +// point a (rotating around point b). +inline double Angle(const Point &a, const Point &b, const Point &c) { + // make unit vector along each of the edges + Vector ba(b,a); ba.Normalize(); + Vector bc(b,c); bc.Normalize(); + // calculate the angle in radians + double dot_product = ba.dx * bc.dx + ba.dy * bc.dy; + + if (dot_product < -1 || dot_product > 1) throw std::string("DOT PRODUCT RANGE"); + + assert (dot_product >= -1 && dot_product <= 1); + float perpDot = ba.dx * bc.dy - ba.dy * bc.dx; + // using atan2 to ensure than we get a signed answer. + double angle_in_radians = atan2(perpDot, dot_product); + // convert to degrees + double answer = angle_in_radians * 180.0 / M_PI; + if (answer < 0) { + answer += 360; + } + assert (answer >= 0 && answer <= 360); + return answer; +} + +// returns true if these two vectors are parallel +inline bool Parallel(const Vector &a, const Vector &b) { + Vector a2 = a; a2.Normalize(); + Vector b2 = b; b2.Normalize(); + double dot_product = a2.dx * b2.dx + a2.dy * b2.dy; + // parallel vectors have dot product == 1 + if (fabs(dot_product) > 1-DISTANCE_EPSILON) return true; + return false; +} + + +// ----------------------------------------------------------------- +// simple functions for angles & sides + +inline bool EqualSides(double a, double b) { + return (fabs(a-b) < DISTANCE_EPSILON); +} + +inline bool EqualAngles(double a, double b) { + assert (a >= 0.0 && a < 360.0); + assert (b >= 0.0 && b < 360.0); + return (fabs(a-b) < ANGLE_EPSILON); +} + +inline bool RightAngle(double a) { + assert (a >= 0.0 && a < 360.0); + return (fabs(a-90.0) < ANGLE_EPSILON); +} + +// ----------------------------------------------------------------- + +#endif diff --git a/labs/inheritance/cp3/main.cpp b/labs/inheritance/cp3/main.cpp new file mode 100644 index 0000000..9c7028e --- /dev/null +++ b/labs/inheritance/cp3/main.cpp @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "polygons.h" + +// helper function prototypes +Polygon* CreatePolygon(const std::string &name, const std::vector &points); +void OutputStats(const std::vector &polygons, std::ofstream &ostr); + +// ------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) { + + // command line arguments & opening files + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " input.txt output.txt" << std::endl; + exit(1); + } + std::ifstream istr(argv[1]); + if (!istr) { + std::cerr << "ERROR: could not open " << argv[1] << std::endl; + exit(1); + } + std::ofstream ostr(argv[2]); + if (!ostr) { + std::cerr << "ERROR: could not open " << argv[2] << std::endl; + exit(1); + } + + // the master container of polygons + std::vector polygons; + + // read the input file one line at a time + std::string line; + while (getline(istr,line)) { + std::stringstream ss(line); + std::string name, token; + if (!(ss >> name)) continue; + std::vector points; + while (ss >> token) { + std::stringstream ss2(token); + char c; + double x,y; + ss2 >> c; + assert (c == '('); + ss2 >> x; + ss2 >> c; + assert (c == ','); + ss2 >> y; + ss2 >> c; + assert (c == ')'); + points.push_back(Point(x,y)); + } + assert (points.size() >= 3); + Polygon* p = CreatePolygon(name,points); + // add the new polygon to the master container + polygons.push_back(p); + } + + // write out the statistics + OutputStats(polygons,ostr); + + // delete the dynamically allocated polygons + for (int i = 0; i < polygons.size(); i++) { + delete polygons[i]; + } +} + +// ------------------------------------------------------------------------------ + +// This function determines the most specific type of shape that can +// be created from the sequence of points. It does this by process of +// elimination. Note that the order in which it attempts to create +// the shapes is important. + +Polygon* CreatePolygon(const std::string &name, const std::vector &points) { + Polygon *answer = NULL; + try{ + answer = new EquilateralTriangle(name,points); + } + catch (int) { + try { + answer= new IsoscelesRightTriangle(name,points); + } + catch (int) { + try { + answer= new RightTriangle(name,points); + } + catch (int) { + try { + answer= new IsoscelesTriangle(name,points); + } + catch (int) { + try { + answer= new Triangle(name,points); + } + catch (int) { + try { + answer= new Square(name,points); + } + catch (int) { + try { + answer= new Rectangle(name,points); + } + catch (int) { + try { + answer= new Rhombus(name,points); + } + catch (int) { + try { + answer= new Parallelogram(name,points); + } + catch (int) { + try { + answer= new Kite(name,points); + } + catch (int) { + try { + answer= new Trapezoid(name,points); + } + catch (int) { + try { + answer= new Quadrilateral(name,points); + } + catch (int) { + answer= new Polygon(name,points); + } + } + } + } + } + } + } + } + } + } + } + } + assert (answer != NULL); + return answer; +} + +// ------------------------------------------------------------------------------ + +// This function prints the output. C++ macros are used to abbreviate +// some repetitive code. The function call-like macros are actually +// replaced using substitution by the preprocessor before the code is +// given to the compiler. (You are not required to understand the +// details of the macros. You do not need to edit this code.) + +void OutputStats(const std::vector &polygons, std::ofstream &ostr) { + + // define and initialize variables +# define InitializeCount(type) std::vector all_##type + InitializeCount(Polygon); + InitializeCount(Triangle); + InitializeCount(IsoscelesTriangle); + InitializeCount(RightTriangle); + InitializeCount(IsoscelesRightTriangle); + InitializeCount(EquilateralTriangle); + InitializeCount(Quadrilateral); + InitializeCount(Trapezoid); + InitializeCount(Kite); + InitializeCount(Parallelogram); + InitializeCount(Rhombus); + InitializeCount(Rectangle); + InitializeCount(Square); + std::vector equal_sides; + std::vector equal_angles; + std::vector right_angle; + + // count & record the names of shapes in each category + for (std::vector::const_iterator i = polygons.begin(); i!=polygons.end(); ++i) { +# define IncrementCount(type) if (dynamic_cast (*i)) all_##type.push_back((*i)->getName()) + IncrementCount(Polygon); + IncrementCount(Triangle); + IncrementCount(IsoscelesTriangle); + IncrementCount(RightTriangle); + IncrementCount(IsoscelesRightTriangle); + IncrementCount(EquilateralTriangle); + IncrementCount(Quadrilateral); + IncrementCount(Trapezoid); + IncrementCount(Kite); + IncrementCount(Parallelogram); + IncrementCount(Rhombus); + IncrementCount(Rectangle); + IncrementCount(Square); + if ((*i)->HasAllEqualSides()) equal_sides.push_back((*i)->getName()); + if ((*i)->HasAllEqualAngles()) equal_angles.push_back((*i)->getName()); + if ((*i)->HasARightAngle()) right_angle.push_back((*i)->getName()); + } + + // output data for each category, sorted alphabetically by the shape's name +# define PrintVector(vecname) std::sort((vecname).begin(),(vecname).end()); \ + for (unsigned int j = 0; j < (vecname).size(); j++) { ostr << " " << (vecname)[j]; } ostr << std::endl +# define PrintCount(type) do { ostr << all_##type.size() << " " #type"(s): "; PrintVector(all_##type); } while (0) + PrintCount(Polygon); + PrintCount(Triangle); + PrintCount(IsoscelesTriangle); + PrintCount(RightTriangle); + PrintCount(IsoscelesRightTriangle); + PrintCount(EquilateralTriangle); + PrintCount(Quadrilateral); + PrintCount(Trapezoid); + PrintCount(Kite); + PrintCount(Parallelogram); + PrintCount(Rhombus); + PrintCount(Rectangle); + PrintCount(Square); + ostr << equal_sides.size() << " Shape(s) with all equal sides: "; + PrintVector(equal_sides); + ostr << equal_angles.size() << " Shape(s) with all equal angles: "; + PrintVector(equal_angles); + ostr << right_angle.size() << " Shape(s) with a right angle: "; + PrintVector(right_angle); +} + +// ------------------------------------------------------------------------------ diff --git a/labs/inheritance/cp3/quads.txt b/labs/inheritance/cp3/quads.txt new file mode 100644 index 0000000..2bdf20c --- /dev/null +++ b/labs/inheritance/cp3/quads.txt @@ -0,0 +1,9 @@ +blue (0,0) (0,1) (1,1) (1,0) +orange (0,0) (0,3) (2,3) (2,0) +yellow (0,1) (2,2) (4,1) (2,0) +magenta (0,0) (1,1) (4,1) (3,0) +pink (1,0) (0,2) (4,2) (3,0) +teal (1,0) (0,3) (1,4) (2,3) +olive (0,0) (1,2) (6,3) (4,0) + + diff --git a/labs/inheritance/cp3/triangles.txt b/labs/inheritance/cp3/triangles.txt new file mode 100644 index 0000000..b623368 --- /dev/null +++ b/labs/inheritance/cp3/triangles.txt @@ -0,0 +1,9 @@ +red (0,0) (0.5,0.866) (1,0) +green (0,0) (0,1) (1,0) +cyan (0,0) (0,2) (1,0) +purple (0,0) (0,2) (3,1) +brown (2,0) (0,1) (3,3) + + + + diff --git a/labs/inheritance/images/multiple_inheritance.png b/labs/inheritance/images/multiple_inheritance.png new file mode 100644 index 0000000..3212bbb Binary files /dev/null and b/labs/inheritance/images/multiple_inheritance.png differ diff --git a/labs/inheritance/images/single_inheritance.png b/labs/inheritance/images/single_inheritance.png new file mode 100644 index 0000000..9ab5261 Binary files /dev/null and b/labs/inheritance/images/single_inheritance.png differ