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

#include "StdAfx.h"
//#include "VWCircle2D.h"

using namespace VWFC::Math;

VWCircle2D::VWCircle2D()
{
	m_dRadius		= 0;
}

VWCircle2D::VWCircle2D(const VWPoint2D& center, double radius)
{
	m_ptCenter		= center;
	m_dRadius		= radius;
}

VWCircle2D::VWCircle2D(const VWPoint2D& a, const VWPoint2D& b, const VWPoint2D& c)
{
	m_ptCenter.ThreePtCenter( a, b, c );
	m_dRadius		= m_ptCenter.DistanceTo( a );
}

VWCircle2D::VWCircle2D(const VWCircle2D& src)
{
	m_ptCenter		= src.m_ptCenter;
	m_dRadius		= src.m_dRadius;
}

VWCircle2D::~VWCircle2D()
{
}

VWCircle2D&	VWCircle2D::operator=(const VWCircle2D& src)
{
	m_ptCenter		= src.m_ptCenter;
	m_dRadius		= src.m_dRadius;
	return *this;
}

void VWCircle2D::SetCircle(const VWPoint2D& center, double radius)
{
	m_ptCenter		= center;
	m_dRadius		= radius;
}

void VWCircle2D::SetCircle(const VWPoint2D& center)
{
	m_ptCenter		= center;
}

void VWCircle2D::SetCircle(double radius)
{
	m_dRadius		= radius;
}

void VWCircle2D::SetCircle(const VWPoint2D& a, const VWPoint2D& b, const VWPoint2D& c)
{
	m_ptCenter.ThreePtCenter( a, b, c );
	m_dRadius		= m_ptCenter.DistanceTo( a );
}

VWPoint2D VWCircle2D::GetCenter() const
{
	return m_ptCenter;
}

double VWCircle2D::GetRadius() const
{
	return m_dRadius;
}

void VWCircle2D::GetCircle(VWPoint2D& outCenter, double& outRadius)
{
	outCenter		= m_ptCenter;
	outRadius		= m_dRadius;
}

// rotate this point at specified angle in degrees and center point
void VWCircle2D::RotateAtDeg(const VWPoint2D& centerPt, double_gs dDegAngle)
{
	m_ptCenter.RotateAtDeg( centerPt, dDegAngle );
}

// rotate this point at specified angle in radians and center point
void VWCircle2D::RotateAtRad(const VWPoint2D& centerPt, double_gs dRadAngle)
{
	m_ptCenter.RotateAtRad( centerPt, dRadAngle );
}

// move the center of the cirle with specified values
void VWCircle2D::Move(double cx, double cy)
{
	m_ptCenter		+= VWPoint2D( cx, cy );
}

// intersect this circle with a line
bool VWCircle2D::Intersect(const VWLine2D& line, double dEpsilon, WorldPt&/*out*/ outPt1, WorldPt&/*out*/ outPt2)
{
	VWPoint2D	begPt				= line.GetStart();
	VWPoint2D	endPt				= line.GetEnd();

	bool		bIntersLineCircle	= false;
	if ( Abs( begPt.x - endPt.x ) < dEpsilon )	{ // Line is vertical
		double_gs	q1 = m_dRadius*m_dRadius - (begPt.x - m_ptCenter.x)*(begPt.x - m_ptCenter.x);
		if ( q1 >= dEpsilon ) {
			if ( q1 < dEpsilon )
				q1 = 0;

			bIntersLineCircle	= true;

			outPt1.x = begPt.x;
			outPt1.y = m_ptCenter.y + sqrt( q1 );

			outPt2.x = endPt.x;
			outPt2.y = m_ptCenter.y - sqrt( q1 );
		}
	}
	else {		// Line is not vertical
		// slope of the line
		double_gs	m	= (endPt.y - begPt.y) / (endPt.x - begPt.x);
		// y-intercept of the line
		double_gs	e	= begPt.y - m * begPt.x;
		double_gs	a	= 1 + m*m;
		double_gs	b	= 2*m*(e - m_ptCenter.y) - 2*m_ptCenter.x;
		double_gs	c	= (e - m_ptCenter.y)*(e - m_ptCenter.y) - m_dRadius*m_dRadius + m_ptCenter.x*m_ptCenter.x;
		double_gs	q1	= b*b - 4 * a * c;
		if ( q1 > -dEpsilon ) {
			if ( q1 < dEpsilon )
				q1 = 0;

			bIntersLineCircle	= true;

			outPt1.x = (-b + sqrt(q1)) / (2*a);
			outPt1.y = m*outPt1.x + e;

			outPt2.x = (-b - sqrt(q1)) / (2*a);
			outPt2.y = m*outPt2.x + e;
		}
	}

	return bIntersLineCircle;
}

// intersect this circle with another circle
bool VWCircle2D::Intersect(const VWCircle2D& circle, double dEpsilon, WorldPt&/*out*/ outPt1, WorldPt&/*out*/ outPt2)
{
	bool		bIntersCircleCircle;
	double_gs	d					= circle.m_ptCenter.DistanceTo( m_ptCenter );
	if ( d < dEpsilon ) 
		bIntersCircleCircle		= false;
	else //Circles are concentric -- too many intersections.
	if ( (d - dEpsilon) > (m_dRadius + circle.m_dRadius) )
		bIntersCircleCircle		= false;
	else // Circles do not intersect.
	if ( MathUtils::Equalish(d, m_dRadius + circle.m_dRadius, dEpsilon) ) { // Circles are tangent, and intersect at only one point.
		bIntersCircleCircle		= true;
		outPt1	= m_ptCenter + ((circle.m_ptCenter - m_ptCenter) * m_dRadius).Normalize();
		outPt2	= circle.m_ptCenter + ((m_ptCenter - circle.m_ptCenter) * circle.m_dRadius).Normalize();
		outPt1	= (outPt1 + outPt2) / 2;
		outPt2	= outPt1;
	}
	else { // Circles intersect at two points.
		bIntersCircleCircle		= true;

		double_gs a			= (m_dRadius*m_dRadius - circle.m_dRadius*circle.m_dRadius + d*d) / (d * 2);
		double_gs h			= sqrt(max(a*a, m_dRadius*m_dRadius) - min(a*a, m_dRadius*m_dRadius));
		VWPoint2D temp_pt	= m_ptCenter + ((circle.m_ptCenter - m_ptCenter).Normalize() * a);
		VWPoint2D temp_v	= (circle.m_ptCenter - m_ptCenter).Normalize().Perp() * h;
		outPt1				= temp_pt + temp_v;
		outPt2				= temp_pt - temp_v;
	}

	return bIntersCircleCircle;
}
