//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * レイ距離実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Geometry/Distance/RayDistance.h"

namespace Lamp{

//------------------------------------------------------------------------------
// 点
//------------------------------------------------------------------------------
// 点距離の二乗
float RayDistance::squaredDistance(const Ray& ray, const Vector3& point){
	const Vector3& direction = ray.getDirection();
	Vector3 distance = point - ray.getOrigin();
	float t = distance.dotProduct(direction);
	// tが-だったら投影しない
	if(t > 0.f){
		t /= direction.getSquaredLength();
		distance -= t * direction;
//	}else{
//		t = 0.f;
	}
	return distance.getSquaredLength();
}
//------------------------------------------------------------------------------
// レイ
//------------------------------------------------------------------------------
// レイ距離の二乗
float RayDistance::squaredDistance(const Ray& ray0, const Ray& ray1){
	const Vector3& origin0 = ray0.getOrigin();
	const Vector3& direction0 = ray0.getDirection();
	const Vector3& origin1 = ray1.getOrigin();
	const Vector3& direction1 = ray1.getDirection();
	Vector3 direction2 = origin0 - origin1;
	float dot00 = direction0.dotProduct(direction0);
	float dot01 = direction0.dotProduct(direction1);
	float dot11 = direction1.dotProduct(direction1);
	float dot02 = direction0.dotProduct(direction2);
	float dot12 = direction1.dotProduct(direction2);
	float determ = dot00 * dot11 - dot01 * dot01;
	float sValue, tValue, sDenom, tDenom;
	sDenom = tDenom = determ;

	// レイが平行かチェック
	if(determ <= Math::epsilon){
		sValue = 0.f;
		sDenom = 1.f;
		tValue = dot12;
		tDenom = dot11;
	}else{
		sValue = dot01 * dot12 - dot11 * dot02;
		tValue = dot00 * dot12 - dot01 * dot02;
	}

	// s補正
	if(sValue < 0.f){
		sValue = 0.f;
		tValue = dot12;
		tDenom = dot11;
	}

	// t補正
	if(tValue < 0.f){
		tValue = 0.f;
		if((-dot02) < 0.f){
			sValue = 0.f;
		}else{
			sValue = -dot02;
			sDenom = dot00;
		}
	}

	float s, t;
	if(Math::abs(sDenom) > Math::epsilon){ s = sValue / sDenom; }
	else{ s = 0.f; }
	if(Math::abs(tDenom) > Math::epsilon){ t = tValue / tDenom; }
	else{ t = 0.f; }

	Vector3 direction =
		(origin0 + (s * direction0)) - (origin1 + (t * direction1));
	return direction.getSquaredLength();
}
//------------------------------------------------------------------------------
// セグメント
//------------------------------------------------------------------------------
// セグメント距離の二乗
float RayDistance::squaredDistance(const Ray& ray, const Segment& segment){
	const Vector3& origin0 = ray.getOrigin();
	const Vector3& direction0 = ray.getDirection();
	const Vector3& origin1 = segment.getOrigin();
	const Vector3& direction1 = segment.getDirection();
	Vector3 direction2 = origin0 - origin1;
	float dot00 = direction0.dotProduct(direction0);
	float dot01 = direction0.dotProduct(direction1);
	float dot11 = direction1.dotProduct(direction1);
	float dot02 = direction0.dotProduct(direction2);
	float dot12 = direction1.dotProduct(direction2);
	float determ = dot00 * dot11 - dot01 * dot01;
	float sValue, tValue, sDenom, tDenom;
	sDenom = tDenom = determ;

	// セグメントが平行かチェック
	if(determ <= Math::epsilon){
		sValue = 0.f;
		sDenom = 1.f;
		tValue = dot12;
		tDenom = dot11;
	}else{
		sValue = dot01 * dot12 - dot11 * dot02;
		tValue = dot00 * dot12 - dot01 * dot02;
	}

	// s補正
	if(sValue < 0.f){
		sValue = 0.f;
		tValue = dot12;
		tDenom = dot11;
	}

	// t補正
	if(tValue < 0.f){
		tValue = 0.f;
		float tmpS = (-dot02);
		if(tmpS < 0.f){
			sValue = 0.f;
		}else{
			sValue = tmpS;
			sDenom = dot00;
		}
	}else if(tValue > tDenom){
		tValue = tDenom;
		float tmpS = dot01 - dot02;
		if(tmpS < 0.f){
			sValue = 0.f;
		}else{
			sValue = tmpS;
			sDenom = dot00;
		}
	}

	float s, t;
	if(Math::abs(sDenom) > Math::epsilon){ s = sValue / sDenom; }
	else{ s = 0.f; }
	if(Math::abs(tDenom) > Math::epsilon){ t = tValue / tDenom; }
	else{ t = 0.f; }

	Vector3 direction =
		(origin0 + (s * direction0)) - (origin1 + (t * direction1));
	return direction.getSquaredLength();
}
//------------------------------------------------------------------------------
// 球
//------------------------------------------------------------------------------
// 球距離の二乗
float RayDistance::squaredDistance(const Ray& ray, const Sphere& sphere){
	Assert(false);
	return 0.f;
}
//------------------------------------------------------------------------------
// 三角
//------------------------------------------------------------------------------
// 三角距離の二乗
float RayDistance::squaredDistance(const Ray& ray, const Triangle& triangle){
	Assert(false);
	return 0.f;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
