//------------------------------------------------------------
// $Id: sphereObject.cc,v 1.3 2000/05/23 09:13:45 yamauchi Exp $
//------------------------------------------------------------
//	sphere object implementation
//	1996/3/21 
//	(C) Copyright Hitoshi Yamauchi
//	球
//------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "constants.h"
#include "vector.h"
#include "sphereObject.h"

sphereObjectClass::
sphereObjectClass(void)
{
    // Commented objects are already initialized by their constructor.
    // mCenter;
    this->mRadius = -1.0;
}

sphereObjectClass::
sphereObjectClass(int objectID, int materialID, 
		  const Vector3& center, double radius, const Vector3& spcpos)
{
    // objBaseClass
    this->mObjectID	= objectID;
    this->mSpcPos	= Vec3toInt3(spcpos);
    this->mMaterialID	= materialID;
    
    // this class
    this->mCenter = center;
    if(radius <= 0.0)
    {
	cerr << "Error! : constructed with illegal radius (<= 0.0)" << endl;
	commMpi2_exit(2);
    }
    this->mRadius = radius;
}

sphereObjectClass::
sphereObjectClass(const sphereObjectClass& rhs)
{
    // objBaseClass
    this->mObjectID	= rhs.mObjectID;
    this->mSpcPos	= rhs.mSpcPos;
    this->mMaterialID	= rhs.mMaterialID;
    
    // this class
    this->mCenter 	= rhs.mCenter;
    this->mRadius	= rhs.mRadius;
}

const sphereObjectClass& sphereObjectClass::
operator=(const sphereObjectClass& rhs)
{
    if(this != &rhs)
    {
	// objBaseClass
	this->mObjectID		= rhs.mObjectID;
	this->mSpcPos		= rhs.mSpcPos;
	this->mMaterialID	= rhs.mMaterialID;
	
	// this class
	this->mCenter 	= rhs.mCenter;
	this->mRadius	= rhs.mRadius;
    }
    return(*this);    
}

//------------------------------
// 物体までの距離(if ret PlusInfty =  not hit)
//   ただし，部分空間内部で hit しない場合には no hit とする
//------------------------------
double sphereObjectClass::
getDistance(const Ray *const apRay, Vector3 *pHitPos)
{
    Vector3 Lvec  = this->mCenter - apRay->getPos();
    double  Ldist = Lvec.getNorm(); // 球の中心と視点との距離
    // ray direction は正規化されているはず
    assert(fabs((apRay->getDir()).getNorm() - 1.0) < 1e-5); 
    double distance = PlusInfty;

    if(Ldist > this->mRadius)
    {				
	double  LRd = Lvec.dInn_prd(apRay->getDir());
	// 視点は球の外部にある
	if(LRd <= 0.0)
	{
	    // 球の方向は ray の方向と 90 度以上ずれているため交差しない。
	    return(PlusInfty); // 90 度は含めた no hit
	}
	else
	{
	    double td2 = 
		this->mRadius * this->mRadius - (Ldist * Ldist) + (LRd * LRd);
	    if(td2 < 0.0)	// この不等号は <= であろうか?
	    {
		return(PlusInfty); // no Hit
	    }else
	    {
		distance = LRd - sqrt(td2);
		(*pHitPos) = apRay->getPos() + distance * apRay->getDir();
	    }
	}
    }
    else if(Ldist < this->mRadius)
    {
	// 視点は球の内部にある
	double  LRd = Lvec.dInn_prd(apRay->getDir());
	double td2 = 
	    this->mRadius * this->mRadius - (Ldist * Ldist) + (LRd * LRd);
	distance = LRd + sqrt(td2);	
	(*pHitPos)      = apRay->getPos() + distance * apRay->getDir();
    }
    else if(apRay->isInside() == YES)
    {
	//tokunaga change:
	//透過光の場合 （ 球の表面裏に視点がある ）
	double  LRd = Lvec.dInn_prd(apRay->getDir());
	distance = 2.0 * sqrt(LRd * LRd);
	(*pHitPos) = apRay->getPos() + distance * apRay->getDir();
    }
    else
    {
	// 視点は球に接っしている。これは外れとする。
	return(PlusInfty);	// no Hit
    }

    // no hit の場合には PlusInfty で return している．ここでは 
    // distance は PlusInfty ではない．
    assert(distance < PlusInfty);

    // ここで hitpos が部分空間内部にあるのか判定し，部分空間内部に存
    // 在しない場合には，他の空間で hit しているので no hit とする．
    // しかし，部分空間情報や，cellLength についての情報は物体が持って
    // いるわけではないので，これは PE で判断することにする 
    //     IntVec3 spcidx(apRay->getSpcPos());
    //     for(int i = 0; i < 3; i++)
    //     {
    // 	Vector3 defmin;
    // 	defmin = this->mSpcParam.getDefSpaceMin();
    // 	if(((int)((pHitPos->get(i) - defmin.get(i)) / cellLen)) != 
    // 	   spcidx.get(i))
    // 	{
    // 	    distance = PlusInfty; // 同一部分空間内部で hit しなかった
    // 	    break;
    // 	}
    //     }

    return(distance);
}

// ostream& operator<<(ostream& s, const sphereObjectClass& rhs)
// {
//     s << "f " << rhs.mObjectID << rhs.mSpcPos;
//     return (s);
// }

Vector3 sphereObjectClass::
getFlatRadiosityColor(const Vector3& hitpoint)
{
    // NIN
    return(Vector3(0.0, 0.0, 0.0));
}

Vector3 sphereObjectClass::
getShadedRadiosityColor(const Vector3& hitpoint)
{
    // NIN
    return(Vector3(0.0, 0.0, 0.0));
}

void sphereObjectClass::
print(const char* pHead)
{
    cout << pHead << " sph " << this->getObjectID() << " ";
    cout << this->mCenter << " " << this->mRadius << " ";
    cout << this->mSpcPos << endl;
}


// ここでは type (radiosity 放射，未放射など)は関係ない．
void sphereObjectClass::
getMpi2Form(int type, char *pRes)
{
    // sph object_num 中心 半径 s1 s2 s3
    sprintf(pRes, "sph %d %g %g %g  %g  %d %d %d",
	    this->getObjectID(),
	    this->mCenter.get(Vector3::X),
	    this->mCenter.get(Vector3::Y),
	    this->mCenter.get(Vector3::Z),
	    this->mRadius,
	    this->mSpcPos.get(IntVec3::X),
	    this->mSpcPos.get(IntVec3::Y),
	    this->mSpcPos.get(IntVec3::Z));
    return;
    type = type; // for Wall gcc 2.7.3
}


int sphereObjectClass::
who(void) const
{
    return(SPHERE_OBJ_E);
}

YorN sphereObjectClass::
checkSpcPos(const Vector3 &partmin, const Vector3 &subSpcSize, 
	    int subdivnum) const
{
    // check spcpos num
    int i;    
    for(i = 0; i < 3; i++)
    {
	if((this->mSpcPos.get(i) < 0) || (this->mSpcPos.get(i) >= subdivnum))
	{
	    cout << "Error! :sph(sphere): spcpos is out of define space." 
		<< endl;
	    return(NO);
	}
    }

    // check spcpos range
    int centerSpcpos[3];
    for(i = 0; i < 3; i++)
    {
	// 球面中心空間位置
	centerSpcpos[i] = 
	    int((this->mCenter.get(i) - partmin.get(i)) / subSpcSize.get(i));
	// 半径による空間位置範囲
	int radSpcposBias = int(this->mRadius / subSpcSize.get(i)) + 1;
	if((this->mSpcPos.get(i) < centerSpcpos[i] - radSpcposBias) ||
	   (this->mSpcPos.get(i) > centerSpcpos[i] + radSpcposBias))
	{
	    static const char *s[] = { "x", "y", "z", };
	    cout << "Error! : sph(sphere): spcpos " << this->mSpcPos 
		<< " is not in legal spcpos : " << s[i] << endl;
	    cout << "center " << this->mCenter << endl;
	    cout << "radius " << this->mRadius << endl;
	    cout << "centerpos[" << i << "] = " << centerSpcpos[i] << endl;
	    cout << "rad bias " << radSpcposBias << endl;
	    cout << "spbspace size " << subSpcSize << endl;
	    cout << "partmin " << partmin << endl;
	    return(NO);
	}
    }
    return(YES);
}

//------------------------------------------------------------
// for 2 dim draw funcs
//------------------------------------------------------------
#ifdef FOR_2DIM_OUT
int sphereObjectClass::
get2dimPoiNumbers(void) const
{
    return(2);
}
void sphereObjectClass::
get2dimPoint(int idx, double *x, double *y, double *z)
{
    switch(idx)
    {
    case 0:
	(*x) = this->mCenter.get(Vector3::X);
	(*y) = this->mCenter.get(Vector3::Y);
	(*z) = this->mCenter.get(Vector3::Z);
	break;
    case 1:
	(*x) = this->mRadius;
	(*y) = -1.0;
	(*z) = -1.0;
	break;
    default:
	cerr << "Error! : sphere must be called 0 or 1 idx." << endl;
	commMpi2_exit(1);
	break;
    }
}
#endif
