//
//	Copyright © 2005 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#include "StdAfx.h"
//#include "VWPoint2D.h"


using namespace VWFC::Math;



// default epsilon for operator==
double	VWPoint2D::sEpsilon		= 0.0000001;

VWPoint2D::VWPoint2D()
{
	this->x		= 0;
	this->y		= 0;
}

VWPoint2D::VWPoint2D(const WorldPt& src)
{
	this->x		= src.x;
	this->y		= src.y;
}

VWPoint2D::VWPoint2D(double x, double y)
{
	this->x		= x;
	this->y		= y;
}


VWPoint2D::VWPoint2D(const VWPoint2D& src)
{
	this->x		= src.x;
	this->y		= src.y;
}

VWPoint2D::~VWPoint2D()
{
}

void VWPoint2D::SetPoint(const WorldPt& pt)
{
	this->x		= pt.x;
	this->y		= pt.y;
}

void VWPoint2D::SetPoint(double x, double y)
{
	this->x		= x;
	this->y		= y;
}

void VWPoint2D::SetAngle(double radians)
{
	x = cos(radians);
	y = sin(radians);
}

void VWPoint2D::SetPointByAngle(double degAng, double length)
{
	this->SetAngle( degAng * kRadiansPerDegree );
	(*this)		*= length;
}

VWPoint2D& VWPoint2D::operator=(const VWPoint2D& src)
{
	this->SetPoint( src.x, src.y );
	return *this;
}

VWPoint2D& VWPoint2D::operator=(const WorldPt& src)
{
	this->SetPoint( src.x, src.y );
	return *this;
}

bool VWPoint2D::operator==(const VWPoint2D& p) const
{
	return this->Equal( p, VWPoint2D::sEpsilon );
}

bool VWPoint2D::operator!=(const VWPoint2D& p) const
{
	return ! this->Equal( p, VWPoint2D::sEpsilon );
}

VWPoint2D VWPoint2D::operator+(const VWPoint2D& p) const
{
	return VWPoint2D(x + p.x, y + p.y);
}

VWPoint2D VWPoint2D::operator-(const VWPoint2D& p) const
{
	return VWPoint2D(x - p.x, y - p.y);
}

VWPoint2D VWPoint2D::operator*(double scalar) const
{
	return VWPoint2D((x * scalar), (y * scalar));
}

VWPoint2D VWPoint2D::operator/(double scalar) const
{
	return VWPoint2D((x / scalar), (y / scalar));
}

VWPoint2D VWPoint2D::operator-(void) const
{
	return VWPoint2D(-x, -y);
}

VWPoint2D& VWPoint2D::operator+=(const VWPoint2D& p)
{
	x += p.x;    y += p.y;    return *this;
}

VWPoint2D& VWPoint2D::operator-=(const VWPoint2D& p)
{
	x -= p.x;    y -= p.y;    return *this;
}

VWPoint2D& VWPoint2D::operator*=(double scalar)
{
	x *= scalar; y *= scalar; return *this;
}

VWPoint2D& VWPoint2D::operator/=(double scalar)
{
	x /= scalar; y /= scalar; return *this;
}

// dot product
double VWPoint2D::operator%(const VWPoint2D& p) const
{
	return this->x * p.x + this->y * p.y;
}

VWPoint2D::operator const WorldPt&() const
{
	// fool the compiler to allow us to modify member functoin of this class
	WorldPt*	pPt	= (WorldPt*) & fPoint;
	pPt->Set( x, y );
	return (*pPt);
}


//////////////////////////////////////////////////////////////////////////
// math functions
//////////////////////////////////////////////////////////////////////////
double VWPoint2D::Magnitude() const
{
	return sqrt( x*x + y*y );
}

// return true if the specified point is equal within the given epsilon to this point
bool VWPoint2D::Equal(const VWPoint2D& pt, double dEpsilon) const
{
	double	xx	= (x - pt.x);
	double	yy	= (y - pt.y);
	return ((xx*xx) + (yy*yy)) < dEpsilon*dEpsilon;
}

// calculate the distance from the specified point to this point
double VWPoint2D::DistanceTo(const VWPoint2D& pt) const
{
	return VWPoint2D::Distance( this->x, this->y, pt.x, pt.y );
}

double VWPoint2D::DistanceTo(double x, double y) const
{
	return VWPoint2D::Distance( this->x, this->y, x, y );
}

// rotate this point at specified angle in degrees and center point
void VWPoint2D::RotateAtDeg(const VWPoint2D& centerPt, double_gs dDegAngle)
{
	double_gs	dAng	= dDegAngle * kRadiansPerDegree;

	double		cenX	= centerPt.x;
	double		cenY	= centerPt.y;

	WorldPt		p;
	p.x					= this->x - cenX;
	p.y					= this->y - cenY;

	this->x				= cos( dAng ) * p.x + sin( dAng ) * p.y;
	this->y				= -sin( dAng ) * p.x + cos( dAng ) * p.y;

	this->x				+= cenX;
	this->y				+= cenY;
}

// rotate this point at specified angle in radians and center point
void VWPoint2D::RotateAtRad(const VWPoint2D& centerPt, double_gs dRadAngle)
{
	double		cenX	= centerPt.x;
	double		cenY	= centerPt.y;

	WorldPt		p;
	p.x					= this->x - cenX;
	p.y					= this->y - cenY;

	this->x				= cos( dRadAngle ) * p.x + sin( dRadAngle ) * p.y;
	this->y				= -sin( dRadAngle ) * p.x + cos( dRadAngle ) * p.y;

	this->x				+= cenX;
	this->y				+= cenY;
}

// return perpendicular point of this point
VWPoint2D VWPoint2D::Perp() const
{
	return WorldPt( this->y, -this->x );
}

// normalize this vector
VWPoint2D& VWPoint2D::Normalize()
{
	double		len	= this->Magnitude();
	if ( len > 0.0 )
	{
		this->x	/= len;
		this->y	/= len;
	}
	return *this;
}

// set this point to the perpendicullar of the specified point
void VWPoint2D::Perp(const VWPoint2D& pt)
{
	this->x		= pt.y;
	this->y		= - pt.x;
}

// get the angle of this point's vector from -180 to 180 deg
double VWPoint2D::CalcAng180() const
{
	double				sinAng, cosAng, a;
	WorldPt3			w( 1.0, 0.0, 0.0 );
	// If the vector is a NULL vector, the result doesn't matter.
	double	 magnitude = this->Magnitude();
	if ( DoubleIsNearlyZero( magnitude ) )
		cosAng = 0;
	else
		cosAng = ::DotProduct( WorldPt3( this->x, this->y, 0 ), w ) / magnitude;
		
	if ( cosAng != 0.0 ) {
		sinAng	= Sqrt( 1.0 - sqr( cosAng ) );
		a		= atan( sinAng / cosAng ) * kDegreesPerRadian;
		if ( this->x < 0.0 ) {
			if ( this->y > 0.0 )
				a += 180.0;
			else if ( this->y < 0.0 )
				a = - ( 180.0 + a );
			if ( a == 0.0 )
				a = 180.0;
		}
		else if ( this->x > 0.0 && this->y < 0.0 )
			a = -a;
	}
	else
		a = this->y > 0.0 ? 90.0 : -90.0;

	return a;
}

// get the angle of this point's vector from 0 to 360 deg
double VWPoint2D::CalcAng360() const
{
	double	angle = this->CalcAng180();
	if ( angle < 0 ) 
		angle = angle + 360;

	return angle;
}

// set this points as the center of a circle passing thru 3 given points
void VWPoint2D::ThreePtCenter(const VWPoint2D& pt1, const VWPoint2D& pt2, const VWPoint2D& pt3)
{
	VWPoint2D		chord1_mid		= ((pt1 + pt2) / 2);
	VWPoint2D		chord2_mid		= ((pt2 + pt3) / 2);
	VWPoint2D		spoke1_end		= chord1_mid + (pt1 - pt2).Perp();
	VWPoint2D		spoke2_end		= chord2_mid + (pt2 - pt3).Perp();
	
	WorldPt			temp_pt( 0, 0 );

	Boolean			bParallel, bIntOnLines;
	::GS_LineLineIntersect( gCBP, chord1_mid, spoke1_end, chord2_mid, spoke2_end, bParallel, bIntOnLines, temp_pt );

	this->SetPoint( temp_pt.x, temp_pt.y );
}

// translates this point into a coordinate system defined by 2 other points.
void VWPoint2D::RelativeCoords(const VWPoint2D& pt1, const VWPoint2D& pt2)
{
	VWPoint2D	p		= (*this) - pt1;
	double_gs	ang		= p.CalcAng180() - (pt2 - pt1).CalcAng180();
	this->SetPointByAngle( ang, p.Magnitude() );
}

// Cross product magnitude is primarily used for determining the left/right orientation of two vectors.
double VWPoint2D::CrossProductMagnitude(const VWPoint2D& pt) const
{
	return this->x * pt.y - this->y * pt.x;
}

//The mathematical definition of Dot Product is:
//		|a||b|cos theta or 
//		the magnitude of 'a' times the magnitude of 'b' times the cosine of the angle between 'a' and 'b'
double VWPoint2D::DotProduct(const VWPoint2D& pt) const
{
	return this->x * pt.x + this->y * pt.y;
}

// whether the current point is on the left of vector provided as parameter
bool VWPoint2D::IsPointOnTheRightOfVector(const VWPoint2D& vec) const
{
	// Perp() generates a vector on the right of the current.
	// If the point provided as parameter lies on the current vector then
	// it is reported as being situated on the right side of the vector.
	const double dSign = MathUtils::Sign( vec.Perp() % *this );

	return (dSign > 0.0);
}

//////////////////////////////////////////////////////////////////////////
// static point functoins
//////////////////////////////////////////////////////////////////////////
double VWPoint2D::TriArea(const VWPoint2D& pt1, const VWPoint2D& pt2, const VWPoint2D& pt3)
{
	return (pt1.x*pt2.y + pt2.x*pt3.y + pt3.x*pt1.y - pt1.y*pt2.x - pt2.y*pt3.x - pt3.y*pt1.x) / 2;
}

// calculated the are of the specified triangle
double VWPoint2D::TriArea(	double pt1x, double pt1y,
							double pt2x, double pt2y,
							double pt3x, double pt3y)
{
	return (pt1x*pt2y + pt2x*pt3y + pt3x*pt1y - pt1y*pt2x - pt2y*pt3x - pt3y*pt1x) / 2;
}

// calculated the distance between specified two points
double VWPoint2D::Distance(double x1, double y1, double x2, double y2)
{
	double	x	= x2 - x1;
	double	y	= y2 - y1;
	return sqrt( x*x + y*y );
}