// Copyright 2000
// College of Computer Science
// Northeastern University Boston MA 02115

// This software may be used for educational purposes as long as this copyright
// notice is retained at the top of all files

// Should this software be modified, the words "Modified from Original" must be
// included as a comment below this notice

// All publication rights are retained.  This software or its documentation may
// not be published in any media either in whole or in part.

///////////////////////////////////////////////////////////////////////////////

// Basic2D.h
// Basic 2-dimensional types with double precision coordinates

///////////////////////////////////////////////////////////////////////////////

#ifndef BASIC2D_H_
#define BASIC2D_H_

#include "GeoTypes.h"					// point & rect in short coordinates

///////////////////////////////////////////////////////////////////////////////

class EXPORT Point2D {

	friend class PointData;
	friend class RectData;
	friend class Rect2D;

protected:

	double x;		// x coordinate
	double y;		// y coordinate
	
public:
	
	// default constructor
	
	Point2D() : x(0), y(0) { };
	

	// explicit constructors
	
	Point2D(double X, double Y) : x(X), y(Y) {  };


	Point2D(const PointData& P) : x(P.x), y(P.y) { };


	// destructor

	~Point2D() { };


	// Set operations

	Point2D& Set(double X, double Y) {
		x = X;
		y = Y;
		return *this;
	};
	

	Point2D& SetX(double X) {
		x = X;
		return *this;
	};
	

	Point2D& SetY(double Y) {
		y = Y;
		return *this;
	};
	

	Point2D& Set(const Point2D& P) {
		x = P.x;
		y = P.y;
		return *this;
	};
	

	Point2D& Set() { return Set(0, 0); };


	// Get operation
	
	void Get(double& X, double& Y) const {
		X = x;
		Y = y;
	};


	// Round to PointData

	PointData Round() const {
		short X = SafeRoundToShort(x);
		short Y = SafeRoundToShort(y);

		return PointData(X, Y);
	};


	// vector operators


	// operator+		P + Q
	
	friend Point2D operator+ (const Point2D& P, const Point2D& Q) {
		return Point2D(P.x + Q.x, P.y + Q.y);
	};
	

	// operator+=		this point += P
	
	Point2D& operator+= (const Point2D& P) {
		x += P.x;
		y += P.y;
		
		return *this;
	};
	

	// operator-		P - Q
	
	friend Point2D operator- (const Point2D& P, const Point2D& Q) {
		return Point2D(P.x - Q.x, P.y - Q.y);
	};
	

	// operator-=		this point -= P
	
	Point2D& operator-= (const Point2D& P) {
		x -= P.x;
		y -= P.y;
		
		return *this;
	};
	

	// operator*		s * P for double s

	friend Point2D operator* (double s, const Point2D& P)
		{ return Point2D(s * P.x, s * P.y); };


	// operator*		P * s for double s

	friend Point2D operator* (const Point2D& P, double s)
		{ return Point2D(P.x * s, P.y * s); };


	// operator*=		this point *= s for double s

	Point2D& operator*= (double s) {
		x *= s;
		y *= s;

		return *this;
	};


	// operator/		P / s for double s
	//
	// if s == 0 then throw a string exception

	friend Point2D operator/ (const Point2D& P, double s) {
		if (s == 0)
			throw string("Division by zero");

		return Point2D(P.x / s, P.y / s);
	};


	// operator/=		this point /= s for double s
	//
	// if s == 0 then throw a string exception

	Point2D& operator/= (double s) {
		if (s == 0)
			throw string("Division by zero");

		x /= s;
		y /= s;

		return *this;
	};


	// zero test

	friend bool IsZero(const Point2D& P)
		{ return (P.x == 0) && (P.y == 0); };


	// operator==

	friend bool operator== (const Point2D& P, const Point2D& Q)
		{ return IsZero(P - Q); };
	

	// operator!=

	friend bool operator!= (const Point2D& P, const Point2D& Q)
		{ return ! IsZero(P - Q); };
	

	// geometric operations


	// offset point by dx and dy

	Point2D& Offset(double dx, double dy) {
		x += dx;
		y += dy;

		return *this;
	};


	// offset point by point P
	// same as this point += P

	Point2D& Offset(const Point2D& P) {
		x += P.x;
		y += P.y;

		return *this;
	};


	// the following vector math functions return double

	friend double DotProduct (const Point2D& P, const Point2D& Q)
		{ return P.x * Q.x + P.y * Q.y; };


	friend double Length (const Point2D& P)
		{ return sqrt(DotProduct(P, P)); };


	friend double Distance (const Point2D& P, const Point2D& Q)
		{ return Length(P - Q); };


	friend double AngleInRadians (const Point2D& P, const Point2D& Q);


	friend double AngleInDegrees (const Point2D& P, const Point2D& Q)
		{ return radians_to_degrees * AngleInRadians(P, Q); };


	// IOTools member functions

	// New data is always stored in object
	// When appropriate, object is also returned by reference

	Point2D& Request(						// respond-or-default
		const string&		prompt,			// prompt to user
		const Point2D&		answer			// default answer
	);


	Point2D& Request(						// mandatory response
		const string&		prompt			// prompt to user
	);


	bool Reading(							// respond-or-decline
		const string&		prompt			// prompt to user
	);
	
	
	// member functions for conversion of string to Point2D

	// at the start position string should contain 2 short values: x y
	// optionally x y may be separated by commas as in: x, y
	
	Point2D& FromString(
		const string&	source,			// string to read
		int				start,			// start position to read
		int&			next,			// next  position to read
		int&			error			// string IO error code
	);


	Point2D& FromString(
		const char*		start,			// start position to read
		const char*&	next,			// next  position to read
		int&			error			// string IO error code
	);


	// member function for conversion of Point2D to string

	// these functions will call the simple and fancy Format functions
	// in IOTools.h

	string ToString() const;

	string ToString(int width, int precision, bool fixed = true) const;
	

	// see below for regular Point2D functions that parallel procedural IOTools
};

///////////////////////////////////////////////////////////////////////////////

class EXPORT Rect2D {

	friend class PointData;
	friend class RectData;
	friend class Point2D;

protected:

	double x1;		// x coordinate of corner 1
	double y1;		// y coordinate of corner 1
	double x2;		// x coordinate of corner 2
	double y2;		// y coordinate of corner 2
	
public:

	// default constructor

	Rect2D() : x1(0), y1(0), x2(0), y2(0) { };
	

	// explicit constructors

	Rect2D(double X1, double Y1, double X2, double Y2) :
		x1(X1), y1(Y1), x2(X2), y2(Y2) { };
	

	Rect2D(const Point2D& P1, const Point2D& P2) :
		x1(P1.x), y1(P1.y), x2(P2.x), y2(P2.y) { };
	

	Rect2D(const RectData& R) :
		x1(R.x1), y1(R.y1), x2(R.x2), y2(R.y2) { };


	// destructor

	~Rect2D() { };


	// set operations
	
	Rect2D& Set(double X1, double Y1, double X2, double Y2) {
		x1 = X1;
		y1 = Y1;
		x2 = X2;
		y2 = Y2;
		return *this;
	};
	

	Rect2D& Set(const Point2D& P1, const Point2D& P2) {
		P1.Get(x1, y1);
		P2.Get(x2, y2);
		return *this;
	};
	

	Rect2D& Set(const Rect2D& R) { return Set(R.x1, R.y1, R.x2, R.y2); };


	Rect2D& Set() { return Set(0, 0, 0, 0); };


	// get operations
	
	void Get(double& X1, double& Y1, double& X2, double& Y2) const {
		X1 = x1;
		Y1 = y1;
		X2 = x2;
		Y2 = y2;
	};

	
	void Get(Point2D& P1, Point2D& P2) const {
		P1.Set(x1, y1);
		P2.Set(x2, y2);
	};
	

	// Round to RectData

	RectData Round() const {
		short X1 = SafeRoundToShort(x1);
		short Y1 = SafeRoundToShort(y1);
		short X2 = SafeRoundToShort(x2);
		short Y2 = SafeRoundToShort(y2);

		return RectData(X1, Y1, X2, Y2);
	};


	// geometric operations


	// test if rect is empty

	friend bool IsEmpty (const Rect2D& R)
		{ return (R.x1 == R.x2) || (R.y1 == R.y2); };


	// offset rect by dx and dy

	Rect2D& Offset(double dx, double dy) {
		x1 += dx;
		y1 += dy;
		x2 += dx;
		y2 += dy;

		return *this;
	};


	// offset rect by point P

	Rect2D& Offset(const Point2D& P) {
		x1 += P.x;
		y1 += P.y;
		x2 += P.x;
		y2 += P.y;

		return *this;
	};


	// expand rect by dx and dy

	Rect2D& Expand(double dx, double dy) {
		x1 -= dx;
		y1 -= dy;
		x2 += dx;
		y2 += dy;

		return *this;
	};


	// shrink rect by dx and dy

	Rect2D& Shrink(double dx, double dy) { return Expand(-dx, -dy); };


	// inset is a synonym for shrink

	Rect2D& Inset(double dx, double dy)  { return Expand(-dx, -dy); };


	// order the rect coordinates
	// guarantees x1 <= x2 and y1 <= y2

	Rect2D& Order() {
		SortPair(x1, x2);
		SortPair(y1, y2);

		return *this;
	};


	// grow rect to include point x, y

	Rect2D& Grow(double x, double y) {
		ExpandInterval(x1, x2, x);
		ExpandInterval(y1, y2, y);

		return *this;
	};


	// grow rect to include point P

	Rect2D& Grow(const Point2D& P) { return Grow(P.x, P.y); };


	// test if point x, y is in rect

	bool PointInRect(double x, double y) const {
		return IsBetween(x1, x2, x) && IsBetween(y1, y2, y);
	};


	// test if point P is in rect

	bool PointInRect(const Point2D& P) const {
		return PointInRect(P.x, P.y);
	};


	// union = smallest rect containing R and S

	friend Rect2D Union (Rect2D R, Rect2D S) {
		// compute union

		R.Order();
		S.Order();

		double x1 = Minimum(R.x1, S.x1);
		double y1 = Minimum(R.y1, S.y1);
		double x2 = Maximum(R.x2, S.x2);
		double y2 = Maximum(R.y2, S.y2);

		return Rect2D(x1, y1, x2, y2);
	};


	// intersection = rect formed by set intersection of R and S

	friend Rect2D Intersection (Rect2D R, Rect2D S) {
		// compute intersection

		R.Order();
		S.Order();

		double x1 = Maximum(R.x1, S.x1);
		double y1 = Maximum(R.y1, S.y1);
		double x2 = Minimum(R.x2, S.x2);
		double y2 = Minimum(R.y2, S.y2);

		// empty intersection adjustments

		if (x1 > x2)
			x1 = x2 = (x1 + x2) / 2;	// make rect trivial in x-direction

		if (y1 > y2)
			y1 = y2 = (y1 + y2) / 2;	// make rect trivial in y-direction

		return Rect2D(x1, y1, x2, y2);
	};


	// sides in absolute value

	void Sides(double& xside, double& yside) const {
		xside = AbsValue(x1 - x2);
		yside = AbsValue(y1 - y2);
	};


	// area
	
	double Area () const {
		double xside;
		double yside;
		Sides(xside, yside);

		return xside * yside;
	};

	
	// perimeter
	
	double Perimeter() const {
		double xside;
		double yside;
		Sides(xside, yside);

		return 2 * (xside + yside);
	};


	// IOTools member functions

	// New data is always stored in object
	// When appropriate, object is also returned by reference

	Rect2D& Request(						// respond-or-default
		const string&		prompt,			// prompt to user
		const Rect2D&		answer			// default answer
	);


	Rect2D& Request(						// mandatory response
		const string&		prompt			// prompt to user
	);


	bool Reading(							// respond-or-decline
		const string&		prompt			// prompt to user
	);
	
	
	// member functions for conversion of string to Rect2D

	// at the start position string should contain 4 double values: x1 y1 x2 y2
	// optionally x1 y1 x2 y2 may be separated by commas as in: x1, y1, x2, y2
	
	Rect2D& FromString(
		const string&	source,			// string to read
		int				start,			// start position to read
		int&			next,			// next  position to read
		int&			error			// string IO error code
	);


	Rect2D& FromString(
		const char*		start,			// start position to read
		const char*&	next,			// next  position to read
		int&			error			// string IO error code
	);


	// member function for conversion of Rect2D to string

	// these functions will call the simple and fancy Format functions
	// in IOTools.h

	string ToString() const;

	string ToString(int width, int precision, bool fixed = true) const;
	

	// see below for regular Rect2D functions that parallel procedural IOTools
};

///////////////////////////////////////////////////////////////////////////////

// required declaration of Array<Point2D>
// to satisfy Visual C++ DLL rules

#if defined(CORE_PLATFORM_WIN32)
#pragma warning(disable: 4231)
#endif	// end WIN32 specific

TEMPLATE_PREFIX template class EXPORT Array<Point2D>;

///////////////////////////////////////////////////////////////////////////////

class EXPORT Polygon2D : public Array<Point2D> {

public:

	Polygon2D(int size = 1) : Array<Point2D>(size) { };


	~Polygon2D() { };

	
	// Append and Remove using Point2D data

	bool Append(const Point2D& P) {
		return Array<Point2D>::Append(P);
	}
	
	
	bool Remove(Point2D& P) {
		return Array<Point2D>::Remove(P);
	}
	
	
	// Append and Remove using x, y data

	bool Append(double x, double y) {
		Point2D P(x, y);

		return Array<Point2D>::Append(P);
	};


	bool Remove(double& x, double& y) {
		Point2D P;

		if (Array<Point2D>::Remove(P)) {
			P.Get(x, y);
			return true;
		}
		else
			return false;
	};


	// ClearPolygon makes all data invalid

	Polygon2D& ClearPolygon() {
		Array<Point2D>::ValidateNone();
		return *this;
	};


	// ClosePolygon appends a point at the end equal to the first point
	// Does nothing if polygon is empty

	Polygon2D& ClosePolygon() {
		if (ValidSize() > 0)
			Array<Point2D>::Append(Access(0));

		return *this;
	};


	// vector operators
	// only +=, -=, *=, /= are supported ... these modify the polygon


	// operator+=		add P to all points in polygon

	Polygon2D& operator+= (const Point2D& P) {
		int i;
		int size = ValidSize();

		for (i = 0; i < size; i++)
			Access(i) += P;

		return *this;
	};


	// operator-=		subtract P from all points in polygon

	Polygon2D& operator-= (const Point2D& P) {
		int i;
		int size = ValidSize();

		for (i = 0; i < size; i++)
			Access(i) -= P;

		return *this;
	};


	// operator*=		this point *= s for short s

	Polygon2D& operator*= (double s) {
		int i;
		int size = ValidSize();

		for (i = 0; i < size; i++)
			Access(i) *= s;

		return *this;
	};


	// operator/=		this point /= s for short s
	//
	// if s == 0 then throw a string exception

	Polygon2D& operator/= (double s) {
		if (s == 0)
			throw string("Division by zero");

		int i;
		int size = ValidSize();

		for (i = 0; i < size; i++)
			Access(i) /= s;

		return *this;
	};


	// geometric operations


	// offset point by point P
	// same as this point += P

	Polygon2D& Offset(const Point2D& P) {
		return (*this += P);
	};


	// offset point by dx and dy

	Polygon2D& Offset(double dx, double dy) {
		Point2D P(dx, dy);
		return (*this += P);
	};

}; // Polygon2D

///////////////////////////////////////////////////////////////////////////////

// The following inline functions implement the procedural interface
// for Point2D and Rect2D objects.


inline void SetPoint2D(Point2D& P, double x, double y)
	{ P.Set(x, y); }


inline void GetPoint2D(const Point2D& P, double& x, double& y)
	{ P.Get(x, y); }


inline Point2D MakePoint2D(double x, double y)
	{ return Point2D(x, y); }


inline void OffsetPoint2D(Point2D& P, double dx, double dy)
	{ P.Offset(dx, dy); }


inline void OffsetPoint2D(Point2D& P, const Point2D& Q)
	{ P.Offset(Q); }


inline void SetRect2D(Rect2D& R, double x1, double y1, double x2, double y2)
	{ R.Set(x1, y1, x2, y2); }


inline void SetRect2D(Rect2D& R, const Point2D& P, const Point2D& Q)
	{ R.Set(P, Q); }


inline void GetRect2D(const Rect2D& R, double& x1, double& y1, double& x2, double& y2)
	{ R.Get(x1, y1, x2, y2); }


inline void GetRect2D(const Rect2D& R, Point2D& P, Point2D& Q)
	{ R.Get(P, Q); };


inline Rect2D MakeRect2D(double x1, double y1, double x2, double y2)
	{ return Rect2D(x1, y1, x2, y2); }


inline Rect2D MakeRect2D(const Point2D& P, const Point2D& Q)
	{ return Rect2D(P, Q); }


inline void OffsetRect2D(Rect2D& R, double dx, double dy)
	{ R.Offset(dx, dy); }


inline void OffsetRect2D(Rect2D& R, const Point2D& P)
	{ R.Offset(P); }


inline void ExpandRect2D(Rect2D& R, double dx, double dy)
	{ R.Expand(dx, dy); }


inline void ShrinkRect2D(Rect2D& R, double dx, double dy)
	{ R.Shrink(dx, dy); }


inline void InsetRect2D(Rect2D& R, double dx, double dy)
	{ R.Inset(dx, dy); }


inline void OrderRect2D(Rect2D& R)
	{ R.Order(); }


inline void GrowRect2D(Rect2D& R, double x, double y)
	{ R.Grow(x, y); }


inline void GrowRect2D(Rect2D& R, const Point2D& P)
	{ R.Grow(P); }


inline bool PointInRect(const Rect2D& R, double x, double y)
	{ return R.PointInRect(x, y); }


inline bool PointInRect(const Rect2D& R, const Point2D& P)
	{ return R.PointInRect(P); }


// This set of functions is patterned after procedural IOTools


inline Point2D RequestPoint2D(			// respond-or-default
	const string&		prompt,			// prompt to user
	const Point2D&		answer			// default answer
) {
	Point2D response;

	return response.Request(prompt, answer);
}
	
	
inline Point2D RequestPoint2D(			// mandatory response
	const string&		prompt			// prompt to user
) {
	Point2D response;

	return response.Request(prompt);
}
	
	
inline bool ReadingPoint2D(				// respond-or-decline
	const string&		prompt,			// prompt to user
	Point2D&			answer			// user answer if provided
) {
	return answer.Reading(prompt);
}
	
	
inline Point2D StringToPoint2D(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// string IO error code
) {
	Point2D response;

	return response.FromString(source, start, next, error);
}
	
	
inline Point2D StringToPoint2D(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// string IO error code
) {
	Point2D response;

	return response.FromString(start, next, error);
}
	
	
inline ostream& operator<< (ostream& os, const Point2D& data) {
	os << data.ToString(14, 6, true);
	return os;
}


inline Rect2D RequestRect2D(			// respond-or-default
	const string&		prompt,			// prompt to user
	const Rect2D&		answer			// default answer
) {
	Rect2D response;

	return response.Request(prompt, answer);
}
	
	
inline Rect2D RequestRect2D(			// mandatory response
	const string&		prompt			// prompt to user
) {
	Rect2D response;

	return response.Request(prompt);
}
	
	
inline bool ReadingRect2D(				// respond-or-decline
	const string&		prompt,			// prompt to user
	Rect2D&				answer			// user answer if provided
) {
	return answer.Reading(prompt);
}
	
	
inline Rect2D StringToRect2D(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// string IO error code
) {
	Rect2D response;

	return response.FromString(source, start, next, error);
}
	
	
inline Rect2D StringToRect2D(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// string IO error code
) {
	Rect2D response;

	return response.FromString(start, next, error);
}
	
	
inline ostream& operator<< (ostream& os, const Rect2D& data) {
	os << data.ToString(14, 6, true);
	return os;
}

#endif // BASIC2D_H_
