//------------------------------------------- // Filename : Quad2.C // Author : DL Frey // Date : 2/20/03 // Project : CMSC 202 Spring 2003 Project 2 // Section : 1234 // SSN : xxx-xx-1234 // // This file provides the implementation for // the Quadrilateral class required for project 2 // The Quadrilateral uses Point objects to store // x- and y-coordinates of its corners // // Per the project specification, the implementation // of this class assumes // 1. the base (m_lowerLeft to m_lowerLeft) is parallel to the x-axis // 2. for trapezoids, the parallel sides are also // paralled to one of the axes //--------------------------------------------- // // This version of Quadrialteral.C supports the notion // of "well-formed" Quadriatlerals. // A new boolean private data member 'wellFormed' keeps // the current state of the Quadrilateral. This state // is re-evaluated by calling the new boolean function // IsWellFormed() whenever a corner is moved (ie every // time a corner's mutator is called). // The Quadrilateral's initial state is determined // in the default constructor. // Each method (IsXXXX(), Area(), Permiter() now has // being well formed as a PreCondition // //----------------------------------------------------------- // Notes to the student // 1. default constructor // the compiler will automatically call the default // constructor for each Point, which will set the // x- and y-coordinates of each point to (0, 0) // However, rather than rely on "knowing" this, // the constructor calls IsWellFormed() rather // than just setting wellFormed to false // 2. mutators // mutators use Point's assigment operator so there // is no need to assign the x- and y-coordinates separately //------------------------------------------------------------- #include "Quad2.H" #include "Point.H" using namespace std; // Default constructor Quadrilateral::Quadrilateral ( void ) { wellFormed = IsWellFormed(); } // mutator for UpperLeft corner void Quadrilateral::SetUpperLeft (const Point& p) { m_upperLeft = p; wellFormed = IsWellFormed(); } // mutator for UpperRight corner void Quadrilateral::SetUpperRight (const Point& p) { m_upperRight = p; wellFormed = IsWellFormed(); } // mutator for LowerRight corner void Quadrilateral::SetLowerRight (const Point& p) { m_lowerRight = p; wellFormed = IsWellFormed(); } // mutator for LowerLeft corner void Quadrilateral::SetLowerLeft (const Point& p) { m_lowerLeft = p; wellFormed = IsWellFormed(); } // accessor for UpperLeft corner const Point& Quadrilateral::GetUpperLeft ( void ) const { return m_upperLeft; } // accessor for LowerLeft corner const Point& Quadrilateral::GetLowerLeft ( void ) const { return m_lowerLeft; } // accessor for UpperRight corner const Point& Quadrilateral::GetUpperRight ( void ) const { return m_upperRight; } // accessor for LowerRight corner const Point& Quadrilateral::GetLowerRight ( void ) const { return m_lowerRight; } //-------------------------------------------------- // bool IsParallelogram // Since we know that the base of the Quadrilateral // is parallel to the x-axis, we can use the // vertical and horizontal distances between the // points to determine parallelism rather than // the more complex Distance() calculation //-------------------------------------------------- bool Quadrilateral::IsParallelogram ( void ) const { return wellFormed && (HorizontalDistance (m_lowerLeft, m_lowerRight) == HorizontalDistance(m_upperLeft, m_upperRight) && (VerticalDistance (m_upperLeft, m_lowerLeft) == VerticalDistance(m_upperRight, m_lowerRight))); } //------------------------------------------------ // IsRectangle // a rectangle is a parallelogram with a right // angle. one right angle + parallelogram // implies all right angles. // // A corner makes a right angle if it has // the same X as the corner "above" it and // the same Y as the corner "next" to it // Without loss of generality, check the lower left corner //--------------------------------------------------- bool Quadrilateral::IsRectangle ( void ) const { return IsParallelogram() // and also a right angle && m_lowerLeft.GetX() == m_upperLeft.GetX() && m_lowerLeft.GetY() == m_lowerLeft.GetY(); } //----------------------------------------------- // IsSquare // A rectangle with all sides the same length. // Not necessary to check them all... just check // one horizontal side with one vertical side // once again, since the horizontal sides are // parallel to x-axis (and since it's a rectangle // the vertical sides are parallel to the y-axis), // we can use the horizontal/vertical calculations // rather than the more complex Distance() // ---------------------------------------------- bool Quadrilateral::IsSquare(void) const { return IsRectangle() && HorizontalDistance (m_lowerLeft, m_lowerRight) == VerticalDistance (m_lowerLeft, m_upperRight); } //----------------------------------------------------- // IsTrapezoid // not a parllelogram, but one // one set of sides are parallel // Since either the horizontal bases are // parallel to x-axis or vertical sides are // parallel to y-axis, we can check horizontal/vertical // distances for parallelism //----------------------------------------------------- bool Quadrilateral::IsTrapezoid( void ) const { return wellFormed && !IsParallelogram() && (VerticalDistance(m_upperLeft, m_lowerLeft) == VerticalDistance (m_upperRight, m_lowerLeft) || HorizontalDistance(m_lowerLeft, m_lowerRight) == HorizontalDistance(m_upperRight, m_upperLeft)); } //--------------------------------- // IsIrregular // any quadrilateral that's not // one of the others //--------------------------------- bool Quadrilateral::IsIrregular ( void ) const { return wellFormed && !IsParallelogram() && !IsTrapezoid(); } //------------------------------------------------------ // Area // determines the type of Quarilateral // and calculates it's area. // Per project 2 description, the area of // an irregular Quadrilateral is returned as 0.0 //----------------------------------------------------- double Quadrilateral::Area ( void ) const { if (!wellFormed) return 0.0; else if (IsTrapezoid()) return TrapezoidArea(); // parallelogram, rectangle, square all the same else if (IsParallelogram()) return HorizontalDistance(m_lowerLeft, m_lowerRight) * VerticalDistance ( m_lowerLeft, m_upperLeft); else // irregular quadrilateral return 0.0; } //----------------------------------------- // TrapezoidArea // private method called by Area() for // trapezoids. // Area = 1/2 * (sum of parallel sides) * height //---------------------------------------- double Quadrilateral::TrapezoidArea ( void ) const { double sides; double height; // determine which sides are parallel if (VerticalDistance(m_upperLeft, m_lowerLeft ) == VerticalDistance(m_upperRight, m_lowerLeft)) { // "top" and "bottom" parallel (and assumed parallel to x-axis) sides = Distance(m_lowerLeft, m_lowerRight) + Distance(m_upperLeft, m_upperRight); height = VerticalDistance (m_lowerLeft, m_upperLeft); } else { // sides parallel (and assumed parallel to y-axis) sides = Distance (m_lowerLeft, m_upperLeft) + Distance(m_lowerRight, m_upperRight); height = HorizontalDistance(m_lowerLeft, m_lowerRight); } return 0.5 * sides * height; } //--------------------------------------------- // Perimeter // returns the perimeter of any quadrilateral // by adding the lengths of the sides // wellFormed not required //--------------------------------------------- double Quadrilateral::Perimeter ( void ) const { return Distance (m_lowerLeft, m_upperLeft) + Distance (m_upperLeft, m_upperRight) + Distance (m_upperRight, m_lowerRight) + Distance (m_lowerRight, m_lowerLeft); } //------------------------------------------ // IsWellFormed() // A private boolean function that determines // if the corners really are correctly // positioned relative to each other. // // It's possible (likely?) that some of these // checks are redundant, but this way there's // less chance for mistake // // Also, for project 2 only, determines that // the lower base (from LL to LR) is parallel // to the x-axis //------------------------------------------- bool Quadrilateral::IsWellFormed ( void ) const { bool wellFormed = true; // UR must be right of UL and above LR if (m_upperRight.GetX() <= m_upperLeft.GetX() || (m_upperRight.GetY() >= m_lowerRight.GetY())) wellFormed = false; // UL must be left of UR and above LR if (m_upperLeft.GetX() <= m_upperRight.GetX() || (m_upperLeft.GetY() <= m_lowerRight.GetY())) wellFormed = false; // LL must be below UL and to the left of LR if (m_lowerLeft.GetY() >= m_upperLeft.GetY() || (m_lowerLeft.GetX() >= m_lowerRight.GetX())) wellFormed = false; // LR must be below UR and right of LL if (m_lowerRight.GetY() >= m_upperRight.GetY() || (m_lowerRight.GetX() <= m_lowerLeft.GetX())) wellFormed = false; // for project 2 only if (m_lowerLeft.GetY() != m_lowerRight.GetY()) wellFormed = false; return wellFormed; }