#include "stdafx.h"
#include "geometry.h"


const ushort Cube::triangleIndices[] =
{
	0,3,1, 0,2,3, //back
	7,5,1, 7,1,3, //right
	4,5,6, 5,7,6, //front
	2,0,4, 2,4,6, //left
	4,1,5, 4,0,1, //bottom
	2,7,3, 2,6,7  //top
};

const ushort Cube::quadIndices[] = 
{
	0,1,3,2, //front
	1,5,7,3, //right
	5,4,6,7, //back
	4,0,2,6, //right
	0,4,5,1, //bottom
	2,3,7,6  //top
};

const ushort Cube::lineIndices[] = 
{
	0,1,1,3,3,2,2,0, //front
	0,4,2,6,1,5,3,7, //sides
	4,5,5,7,7,6,6,4, //back
};

Cube::Cube(float sideLength)
	: m_sidelength(sideLength)
{
	float h = sideLength / 2.0f;

	m_vertices = new QVector3D[Corner::COUNT];
	for(uchar i = 0;i<Corner::COUNT;++i)
	{
		m_vertices[i] = QVector3D(
			i & 0x1U ? h:-h,
			i & 0x2U ? h:-h,
			i & 0x4U ? h:-h);
	}
}

Cube::~Cube()
{
	delete m_vertices;
}

const QVector3D * const Cube::getVertices() const
{
	return m_vertices;
}

QVector<QVector4D> Cube::transform(const QMatrix4x4& mat) const
{
	//call with dummy vectors
	return transform(mat,QVector4D(),QVector4D());
}

QVector<QVector4D> Cube::transform(const QMatrix4x4& mat,QVector4D& minPos,QVector4D& maxPos) const
{
	QVector<QVector4D> vec;
	vec.reserve(Cube::Corner::COUNT);

	const float min_flt = std::numeric_limits<float>::lowest();
	const float max_flt = std::numeric_limits<float>::max();
	minPos = QVector4D(max_flt,max_flt,max_flt,max_flt);
	maxPos = QVector4D(min_flt,min_flt,min_flt,min_flt);

	for(int i = 0;i<Cube::Corner::COUNT;++i)
	{
		QVector4D tf = mat * QVector4D(m_vertices[i],1.0f);
		vec.append(tf);

		if(minPos.x() > tf.x())
			minPos.setX(tf.x());
		if(minPos.y() > tf.y())
			minPos.setY(tf.y());
		if(minPos.z() > tf.z())
			minPos.setZ(tf.z());
		if(minPos.w() > tf.w())
			minPos.setW(tf.w());

		if(maxPos.x() < tf.x())
			maxPos.setX(tf.x());
		if(maxPos.y() < tf.y())
			maxPos.setY(tf.y());
		if(maxPos.z() < tf.z())
			maxPos.setZ(tf.z());
		if(maxPos.w() < tf.w())
			maxPos.setW(tf.w());
	}

	return vec;
}

QVector<QVector3D> Cube::transform3D(const QMatrix4x4& mat,QVector3D& minPos,QVector3D& maxPos) const
{
	QVector<QVector3D> vec;
	vec.reserve(Cube::Corner::COUNT);

	const float min_flt = std::numeric_limits<float>::lowest();
	const float max_flt = std::numeric_limits<float>::max();
	minPos = QVector3D(max_flt,max_flt,max_flt);
	maxPos = QVector3D(min_flt,min_flt,min_flt);

	for(int i = 0;i<Cube::Corner::COUNT;++i)
	{
		QVector3D tf = mat * m_vertices[i];
		vec.append(tf);

		if(minPos.x() > tf.x())
			minPos.setX(tf.x());
		if(minPos.y() > tf.y())
			minPos.setY(tf.y());
		if(minPos.z() > tf.z())
			minPos.setZ(tf.z());

		if(maxPos.x() < tf.x())
			maxPos.setX(tf.x());
		if(maxPos.y() < tf.y())
			maxPos.setY(tf.y());
		if(maxPos.z() < tf.z())
			maxPos.setZ(tf.z());
	}

	return vec;
}

bool LineIntersectPlane(const QVector3D& lineStart, const QVector3D& lineVec, const QVector3D& planePoint,const QVector3D& planeNormal, float& d)
{
	//custom zero tolerance for stability
	const float ZERO_TOLERANCE = 1e-6f;


	float denom = QVector3D::dotProduct(lineVec,planeNormal);
	if(abs(denom)<ZERO_TOLERANCE)
	{
		//if denominator is zero, the line is parallel to the plane
		//there either is no intersection at all, or at all points of the line
		//we handle both cases as no intersection
		d = 0.0f;
		return false;
	}

	float nom = QVector3D::dotProduct(planePoint - lineStart, planeNormal);
	
	d = nom / denom;
	if(d<0.0f || d > 1.0f)
	{
		//the intersection point lies on the ray, but outside the range
		return false;
	}

	return true;
}