/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
#include "StdAfx.h"
#include <stdlib.h>
#include "mg/Position_list.h"
#include "mg/CCisects.h"
#include "mg/CSisects.h"
#include "mg/LBRep.h"
#include "mg/RLBRep.h"
#include "mg/SurfCurve.h"
#include "mg/BSumCurve.h"
#include "mg/Transf.h"
#include "mg/Plane.h"
#include "mg/Sphere.h"
#include "mg/Cylinder.h"
#include "mg/SBRep.h"
#include "mg/RSBRep.h"
#include "mg/BSumSurf.h"
#include "mg/Tolerance.h"

#if defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using namespace std;

//MGSphere is a Sphere in 3D space. Sphere f(u,v) is expressed
// by two ellipses EL1(m_ellipseu) and EL2(m_ellipsev) as:
//f(u,v) = C+(M*cos(u)+N*sin(u))*cos(v)+B*sin(v), or
//f(u,v) = C+EL1(u)*cos(v)+B*sin(v),
//where EL1=M*cos(u)+N*sin(u), and EL2=C+N*cos(v)+B*sin(v).
//Here M is the major axis of EL1, N is the minor axis of EL1, N is
//also the major axis of EL2, and B=(unit vector of (M*N))*(N.len()),
//which is the minor axis of EL2. (M,N,B) make a orthonormal system.
//v is the angle with M axis in the (B,M) plane, and u is the angle with M in the (M,N) plane.
//v=0 parameter line makes the ellipse C+EL1, and u=pai/2 parameter line
//makes the ellipse EL2.

// MGSphereクラスは３次元空間における球を表すクラスである。

/////////////Constructor コンストラクタ////////////

// Construct a  whole sphere from the center and the radius.
MGSphere::MGSphere(
	const MGPosition& cntr,	// Sphere center.
	double radius) 		// Sphere radius.
:MGSurface(){
	MGVector M=mgX_UVEC*radius;
	MGVector N=mgY_UVEC*radius;
	MGVector B=mgZ_UVEC*radius;
	m_ellipseu=MGEllipse(mgORIGIN,M,N,MGInterval(0.,mgDBLPAI));
	m_ellipsev=MGEllipse(cntr,N,B,MGInterval(-mgHALFPAI,mgHALFPAI));
}

// Construct a  whole sphere from the center and the radius.
//Let MGUnit_vector N(B*M), M2(N*B). Then (M2,N,B) makes a orthonormal system,
//and this sphere is parameterized as:
//F(u,v)=cntr+radis*cos(v)(M*cos(u)+N*sin(u))+radis*sin(v)*B.
MGSphere::MGSphere(
	const MGPosition& cntr,	// Sphere center.
	double radius,			// Sphere radius.
	const MGUnit_vector& B,	//axis
	const MGVector& M //reference direciotn that is approximately perpendiculat to B.
):MGSurface(){
	MGVector B2=B*radius;
	MGUnit_vector N2U=B*M; MGVector N2=N2U*radius;
	MGUnit_vector M2U=N2U*B; MGVector M2=M2U*radius;
	m_ellipseu=MGEllipse(mgORIGIN,M2,N2,MGInterval(0.,mgDBLPAI));
	m_ellipsev=MGEllipse(cntr,N2,B2,MGInterval(-mgHALFPAI,mgHALFPAI));
}

// Construct a Sphere by changing this space dimension or
// ordering the coordinates.
MGSphere::MGSphere(
	int dim,				// New space dimension.
	const MGSphere& sphr,	// Original Sphere.
	int start1, 		// Destination order of new Surface.
	int start2) 		// Source order of original Surface.
:MGSurface(sphr),
m_ellipseu(dim,sphr.m_ellipseu,start1,start2),
m_ellipsev(dim,sphr.m_ellipsev,start1,start2){
	invalidateBox();
}

//Whole sphere(u parameter range is from 0 to 2 pai) around minor axis of
//the input ellipse. The parameter range of the ellipse must be within the range
//from -pai/2 to pai/2, and the range becomes the v parameter range of the sphere.
//The input ellipse makes u=const(u=pai/2) v-paramarter line.
MGSphere::MGSphere(
	const MGEllipse& ellipse	//ellispe  of the Sphere
):MGSurface(),m_ellipsev(ellipse){
	m_ellipsev.limit(MGInterval(-mgHALFPAI,mgHALFPAI));
	const MGVector& N=m_ellipsev.major_axis();
	const MGVector& B=m_ellipsev.minor_axis();
	MGUnit_vector M=N*B;
	m_ellipseu=MGEllipse(mgORIGIN,M*(N.len()),N,MGInterval(0.,mgDBLPAI));
}

//Sphere(u parameter range is urange) around minor axis of the input ellipse.
//The parameter range of the ellipse must be in the range from -pai/2 to pai/2,
//and the range becomes the v parameter range of the sphere.
//The input ellipse makes u=const(u=pai/2) v-paramarter line.
MGSphere::MGSphere(
	const MGEllipse& ellipse,	//ellispe  of the Sphere
	MGInterval urange
):MGSurface(),m_ellipsev(ellipse){
	m_ellipsev.limit(MGInterval(-mgHALFPAI,mgHALFPAI));
	const MGVector& N=m_ellipsev.major_axis();
	const MGVector& B=m_ellipsev.minor_axis();
	MGUnit_vector M=N*B;
	m_ellipseu=MGEllipse(mgORIGIN,M*(N.len()),N,urange);
}

//////////Operator overload 演算子の多重定義/////////////

MGSphere& MGSphere::operator=(const MGGel& gel2){
	const MGSphere* gel2_is_this=dynamic_cast<const MGSphere*>(&gel2);
	if(gel2_is_this)
		operator=(*gel2_is_this);
	return *this;
}
MGSphere& MGSphere::operator=(MGGel&& gel2){
	MGSphere* gel2_is_this=dynamic_cast<MGSphere*>(&gel2);
	if(gel2_is_this)
		operator=(std::move(*gel2_is_this));
	return *this;
}

//Translation of the Sphere
MGSphere MGSphere::operator+ (const MGVector& vec)const{
	MGSphere sphr(*this);
	sphr+=vec;
	return sphr;
}
MGSphere operator+ (const MGVector& v, const MGSphere& sphr){
	return sphr+v;
}

//Translation of the Sphere
MGSphere& MGSphere::operator+= (const MGVector& vec){
	m_ellipsev+=vec;
	m_box+=vec;
	return *this;
}

//Translation of the Sphere
MGSphere MGSphere::operator- (const MGVector& vec) const{
	MGSphere sphr(*this);
	sphr-=vec;
	return sphr;
}

//Translation of the Sphere
MGSphere& MGSphere::operator-= (const MGVector& vec){
	m_ellipsev-=vec;
	m_box-=vec;
	return *this;
}

//柱面のスケーリングを行い，柱面を作成する。
//Scaling of the Sphere by a double.
MGSphere MGSphere::operator* (double s) const{
	MGSphere sphr(*this);
	sphr*=s;
	return sphr;
}

//Scaling of the Sphere by a double.
MGSphere operator* (double scale, const MGSphere& sphr){
	return sphr*scale;
}

//Scaling of the Sphere by a double.
MGSphere& MGSphere::operator*= (double s){
	m_ellipsev*=s;
	m_ellipseu*=s;
	invalidateBox();
	return *this;
}

//Transformation of the Sphere by a matrix.
MGSphere MGSphere::operator* (const MGMatrix& mat) const{
	MGSphere sphr(*this);
	sphr*=mat;
	return sphr;
}

//Transformation of the Sphere by a matrix.
MGSphere& MGSphere::operator*= (const MGMatrix& mat){
	m_ellipseu*=mat;
	m_ellipsev*=mat;
	invalidateBox();
	return *this;
}

//Transformation of the Sphere by a MGTransf.
MGSphere MGSphere::operator* (const MGTransf& tr) const{
	MGSphere sphr(*this);
	sphr*=tr;
	return sphr;
}

//Transformation of the Sphere by a MGTransf.
MGSphere& MGSphere::operator*= (const MGTransf& tr){
	m_ellipseu*=tr.affine();
	m_ellipsev*=tr;
	invalidateBox();
	return *this;
}

//Comparison between Sphere and a surface.
bool MGSphere::operator==(const MGSphere& sphr)const{
	if(m_ellipsev!=sphr.m_ellipsev)
		return false;
	if(m_ellipseu!=sphr.m_ellipseu)
		return false;

	return true;
}
bool MGSphere::operator<(const MGSphere& gel2)const{
	if(m_ellipseu==gel2.m_ellipseu)
		return m_ellipsev<gel2.m_ellipsev;
	return m_ellipseu<gel2.m_ellipseu;
}
bool MGSphere::operator==(const MGGel& gel2)const{
	const MGSphere* gel2_is_this=dynamic_cast<const MGSphere*>(&gel2);
	if(gel2_is_this)
		return operator==(*gel2_is_this);
	return false;
}
bool MGSphere::operator<(const MGGel& gel2)const{
	const MGSphere* gel2_is_this=dynamic_cast<const MGSphere*>(&gel2);
	if(gel2_is_this)
		return operator<(*gel2_is_this);
	return false;
}

////////////Member function メンバ関数///////////

//Compute this surface's box
void MGSphere::compute_box(MGBox& bx)const{
	bx.set_null();

	box_vconst(bx,param_s_v());
	box_vconst(bx,param_e_v());
	box_uconst(bx,param_s_u());
	box_uconst(bx,param_e_u());

	const MGVector& m=M();
	const MGVector& n=N();

	for(int i=0; i<3; i++){

	double ni=n[i], mi=m[i];
	double sqrtmini=sqrt(mi*mi+ni*ni);
	if(MGMZero(sqrtmini))
		continue;

	double ani=fabs(ni), ami=fabs(mi);
	double ui;
	if(ani<=ami)
		ui=asin(ani/sqrtmini);
	else
		ui=acos(ami/sqrtmini);

	if(m_ellipseu.in_RelativeRange_of_radian(ui)){
		double u=m_ellipseu.RelativeRange_in_radian(ui);
		box_uconst(bx,u);
	}
	if(m_ellipseu.in_RelativeRange_of_radian(-ui)){
		double u=m_ellipseu.RelativeRange_in_radian(-ui);
		box_uconst(bx,u);
	}
	if(m_ellipseu.in_RelativeRange_of_radian(ui-mgPAI)){
		double u=m_ellipseu.RelativeRange_in_radian(ui-mgPAI);
		box_uconst(bx,u);
	}
	if(m_ellipseu.in_RelativeRange_of_radian(mgPAI-ui)){
		double u=m_ellipseu.RelativeRange_in_radian(mgPAI-ui);
		box_uconst(bx,u);
	}

	}
}

// 入力のパラメータ範囲の曲線部分を囲むボックスを返す。
//Box that includes limitted Sphere by box.
MGBox MGSphere::box_limitted(
	const MGBox& uvrange	// Parameter Range of the surface.
) const{
	MGSphere sphr(*this);
	sphr.m_ellipseu.limit(uvrange[0]);
	sphr.m_ellipsev.limit(uvrange[1]);
	return sphr.box();
}

//Compute box of u=const parameter line.
//The box will be or-ed to the input box bx.
//u must be the value in radian.
void MGSphere::box_uconst(MGBox& bx, double u)const{
	MGVector MN(m_ellipseu.eval(u));
	double v0=m_ellipsev.gp_to_radian(m_ellipsev.param_s());
	double v1=m_ellipsev.gp_to_radian(m_ellipsev.param_e());
	MGEllipse elv(C(),MN,B(),MGInterval(v0,v1));
	bx|=elv.box();
}

//Compute box of v=const parameter line.
//The box will be or-ed to the input box bx.
//v must be the value in radian.
void MGSphere::box_vconst(MGBox& bx, double v)const{
	double vR=m_ellipsev.gp_to_radian(v);
	double cosv=cos(vR), sinv=sin(vR);
	MGEllipse elu(m_ellipseu*cosv+(C()+B()*sinv));
	bx|=elu.box();
}

//Changing this object's space dimension.
void MGSphere::change_dimension(
	int sdim,		// new space dimension
	int start1, 		// Destination order of new object.
	int start2) 		// Source order of this object.
{
	m_ellipseu.change_dimension(sdim,start1,start2);
	m_ellipsev.change_dimension(sdim,start1,start2);
	invalidateBox();
}

//Change parameter range, be able to change the direction by providing
//t1 greater than t2.
MGSphere& MGSphere::change_range(
	int is_u,				//if true, (t1,t2) are u-value. if not, v.
	double t1,				//Parameter value for the start of original. 
	double t2				//Parameter value for the end of original. 
){
	if(is_u)
		m_ellipseu.change_range(t1,t2);
	else
		m_ellipsev.change_range(t1,t2);
	return *this;
}

//Compute the closest point parameter value (u,v) of this surface
//from a point.
MGPosition MGSphere::closest(const MGPosition& point) const{
	MGPosition_list list=perps(point);
	size_t n=list.size();
	if(n==2) return list.front();
	else if(n==1){
		MGPosition& Q=list.front();
		const MGPosition& C=sphere_center();
		if((Q-C)%(point-C)>0.) return Q;
	}

	//Compute using closest_on_perimeter().
	return closest_on_perimeter(point);
}

//Compute the closest point on a perimeter of the surface. The point is returned
//as the parameter value (u,v) of this surface.
MGPosition MGSphere::closest_on_perimeter(const MGPosition& point)const{
	MGPosition uv1, uv;
	double dist1,dist;
	int pnum=perimeter_num();
	MGCurve* perimtr;
	for(int i=0; i<pnum; i++){
		if(i==0 && degenerate_at_v0()) continue;
		if(i==2 && degenerate_at_v1()) continue;
		perimtr=perimeter_curve(i);
		uv1=perimeter_uv(i,perimtr->closest(point));
		dist1=(point-eval(uv1)).len();
		if(uv.is_null()){dist=dist1; uv=uv1;}
		else if(dist1<dist){dist=dist1; uv=uv1;}
		delete perimtr;
	}
	return uv;
}

//Compute the sphere parameter vaule (u,v) whose world coordinate is P,
//given a point P on the surface.
//Computed (u,v) may be outside real parameter range
//(sphere is regarded as a whole one).
void MGSphere::compute_uv(const MGPosition& P, double&u, double&v)const{
	assert(sphere());//******Currently this is valid only for sphere*******
	MGVector CP(P,sphere_center());//Vector from the center to P.
	const MGVector& Ms=M();
	const MGVector& Ns=N();
	const MGVector& Bs=B();
	v=mgHALFPAI-CP.angle(Bs);
	if(CP.orthogonal(Ms)){
		u=mgHALFPAI;
	}else{
		MGVector CPM=Bs*CP*Bs;//CPM is the projection vector of CP onto (M, N) plane.
		u=CPM.angle2pai(Ms,Bs);
	}
	u=m_ellipseu.radian_to_gp(u);
	v=m_ellipsev.radian_to_gp(v);
}

//Construct new surface object by copying to newed area.
//User must delete this copied object by "delete".
MGSphere* MGSphere::clone() const{
	return new MGSphere(*this);
}

//Construct new surface object by changing
//the original object's space dimension.
//User must delete this copied object by "delete".
MGSphere* MGSphere::copy_change_dimension(
	int sdim,		// new space dimension
	int start1, 		// Destination order of new line.
	int start2) 		// Source order of this line.
const{
	return new MGSphere(sdim,*this,start1,start2);
}

//Ask if this sphere has the degenerate point at v=min, or v=max.
bool MGSphere::degenerate_at_v0()const{//at v=min
	double v0=m_ellipsev.gp_to_radian(m_ellipsev.param_s());
	return MGREqual_base(v0,-mgHALFPAI,mgPAI);
}
bool MGSphere::degenerate_at_v1()const{//at v=max
	double v1=m_ellipsev.gp_to_radian(m_ellipsev.param_e());
	return MGREqual_base(v1,mgHALFPAI,mgPAI);
}
// 与えられた点との距離を返却する。
//Return the distace between Sphere and the point.
double MGSphere::distance(const MGPosition& point) const{
	return (eval(closest(point))-point).len();
}

//Evaluate surface data.
MGVector MGSphere::eval(
	double u, double v		// Parameter value of the surface.
	, int ndu			// Order of derivative along u.
	, int ndv			// Order of derivative along v.
)const{
	MGVector Fu=m_ellipseu.eval(u,ndu);
	double vr=m_ellipsev.gp_to_radian(v);
	double cosv=cos(vr), sinv=sin(vr);
	const MGVector& Bs=B();
	if(ndu==0 && ndv==0)
		return C()+Fu*cosv+Bs*sinv;

	MGVector F;
	div_t result=div(ndv, 4);
	if(ndu==0){
		switch(result.rem){
		case 0: F=  Fu*cosv+Bs*sinv; break;
		case 1: F= -Fu*sinv+Bs*cosv; break;
		case 2: F= -Fu*cosv-Bs*sinv; break;
		case 3: F=  Fu*sinv-Bs*cosv; break;
		}
	}else{
		switch(result.rem){
		case 0: F=  Fu*cosv; break;
		case 1: F= -Fu*sinv; break;
		case 2: F= -Fu*cosv; break;
		case 3: F=  Fu*sinv; break;
		}
	}
	return F;
}

// Exchange parameter u and v.
//This is not allowed.
MGSurface& MGSphere::exchange_uv(){ assert(false);return *this;}

//Modify the original Surface by extrapolating the specified perimeter.
//The extrapolation is C2 continuous if the order >=4.
//The extrapolation is done so that extrapolating length is "length"
//at the position of the parameter value "param" of the perimeter.
MGSphere& MGSphere::extend(
	int perimeter,	//perimeter number of the Surface.
					// =0:v=min, =1:u=max, =2:v=max, =3:u=min.
	double param,	// parameter value of above perimeter.
	double length,	//chord length to extend at the parameter param of the perimeter.
	double dk){  //Coefficient of how curvature should vary at
//    extrapolation start point. When dk=0, curvature keeps same, i.e.
//    dK/dS=0. When dk=1, curvature becomes zero at length extrapolated point,
//    i.e. dK/dS=-K/length at extrapolation start point.
//    (S=parameter of arc length, K=Curvature at start point)
//    That is, when dk reaches to 1 from 0, curve changes to flat.

	assert(0<=perimeter && perimeter<4);
	
	bool ise=true;//starting perimeter
	MGEllipse* el_to_extend;
	int ndu=0,ndv=0;
	if(perimeter==1 || perimeter==3){	// Extrapolate to u-direction
		el_to_extend=&m_ellipseu;
		if(perimeter==1)
			ise=false;//ending perimeter
		ndu=1;
	}else{
		// Extrapolate to v-direction
		el_to_extend=&m_ellipsev;
		if(perimeter==2)
			ise=false;//ending perimeter
		ndv=1;
	}
	MGPosition uv=perimeter_uv(perimeter,param);//Surface parameter value of param.
	double vlen=eval(uv,ndu,ndv).len();
	if(MGMZero(vlen))
		return *this;

	double slen=length/vlen;
	el_to_extend->extend(slen,ise);
	if(ndv)
		el_to_extend->limit(MGInterval(-mgHALFPAI, mgHALFPAI));
		//When extension is done about m_ellisev, the parameter range is limitted.

	return *this;
}

bool MGSphere::in_range(double u, double v) const{
	return m_ellipseu.in_range(u) && m_ellipsev.in_range(v);
}

#define MGSphere_isect_div_num 12.
//isect_dt computes incremental values of u and v direction for the intersection
//computation at parameter position (u,v).
void MGSphere::isect_dt(
	double u, double v, double& du, double& dv,
	double acuRatio		//acuracy ratio.
)const{
	double u0=m_ellipseu.param_s(); u0=m_ellipseu.gp_to_radian(u0);
	double u1=m_ellipseu.param_e(); u1=m_ellipseu.gp_to_radian(u1);
	double v0=m_ellipsev.param_s(); v0=m_ellipsev.gp_to_radian(v0);
	double v1=m_ellipsev.param_e(); v1=m_ellipsev.gp_to_radian(v1);
	double alfa=isect_dt_coef(0);
	double ualfa=alfa*(mgDBLPAI/(u1-u0))/MGSphere_isect_div_num;
	double valfa=alfa*(mgDBLPAI/(v1-v0))/MGSphere_isect_div_num;
	du=m_ellipseu.param_span()*ualfa*acuRatio;
	dv=m_ellipsev.param_span()*valfa*acuRatio;
}

//"isect1_incr_pline" is a dedicated function of isect_start_incr, will get
// shortest parameter line necessary to compute intersection.
MGCurve* MGSphere::isect_incr_pline(
	const MGPosition& uv,	//last intersection point.
	int kdt,				//Input if u=const v-parameter line or not.
							// true:u=const, false:v=const.
	double du, double dv,//Incremental parameter length.
	double& u,				//next u value will be output
	double& v,				//next v value will be output
	int incr		//Incremental valuse of B-coef's id.
) const{
	//Compute necessary sub-interval length of parameter line of this surface.
	if(kdt){
		u=m_ellipseu.range(uv[0]+du); v=uv[1];
		//Compute u=const v-parameter line of this surface in pline.
		return parameter_curve(kdt,u);
	}else{
		v=m_ellipsev.range(uv[1]+dv); u=uv[0];
		//Compute v=const u-parameter line in pline.
		return parameter_curve(kdt,v);
	}
}

//Compute the intersection line of this and the plane pl.
MGSSisects MGSphere::intersectPl(const MGPlane& pl)const{
	assert(sphere());//******Currently this is valid only for sphere*******
	MGSSisects list(this,&pl);
	if(fabs(pl.distance(C()))>radius()) return list;

	MGPosition_list uvuv_list;
	intersect12Boundary(pl,uvuv_list);
	if(!uvuv_list.size()) uvuv_list=intersectInner(pl);
	//Compute intersection line using isect_with_surf.
	return isect_with_surf(uvuv_list,pl);
}

//Intersection of Surface and a straight line.
MGCSisects MGSphere::isectSl(
	const MGStraight& sl,
	const MGBox& uvbox //indicates if this surface is restrictied to the parameter
					//range of uvbox. If uvbox.is_null(), no restriction.
)const{
	if(!sphere())
		return MGSurface::isectSl(sl,uvbox);

	MGCSisects list(&sl,this);

	double r=radius();
	if(sl.distance(C())>r) return list;
	if(sl.straight_type()==MGSTRAIGHT_SEGMENT){
		if((sl.start_point()-C()).len()<r && (sl.end_point()-C()).len()<r)return list;
	}
	MGStraight sl2(sl);sl2.unlimit();
	double tsl;
	sl2.perp_point(C(),tsl);
	//tsl=parameter of sl where the center of the sphere is perpendicular to the sl.
	MGPosition Q=sl2.eval(tsl);
	double d=(Q-C()).len();
	double l=sqrt(r*r-d*d);
	MGUnit_vector sldir(sl2.direction());
	MGPosition P1=Q+sldir*l, P2=Q-sldir*l;
		//Two intersection points with the infinite straight line .
	double t1,t2, u,v;
	if(sl.on(P1,t1)){
		compute_uv(P1,u,v);
		if(in_range(u,v)) list.append(P1,t1,MGPosition(u,v));
	}
	if(sl.on(P2,t2)){
		compute_uv(P2,u,v);
		if(in_range(u,v)) list.append(P2,t2,MGPosition(u,v));
	}
	return list;
}

//Negate the normal of the Sphere.
void MGSphere::negate(
	int is_u)// Negate along u-direction if is_u is ture,
			// else along v-direction.
{
	if(is_u)
		m_ellipseu.negate();
	else
		m_ellipsev.negate();
}

//Obtain parameter value if this surface is negated by "negate()".
//Negate along u-direction if is_u is ture,
// else along v-direction.
MGPosition MGSphere::negate_param(const MGPosition& uv, int is_u)const{
	MGPosition uvnew(uv);
	if(is_u)
		uvnew(0)=m_ellipseu.negate_param(uv[0]);
	else
		uvnew(1)=m_ellipsev.negate_param(uv[1]);
	return uvnew;
}

//C1連続曲面の一定オフセット関数
//オフセット方向は、ノーマル方向を正とする。トレランスはline_zero()を使用している。
//戻り値は、オフセット面のオートポインターが返却される。
//Surface offset. positive offset value is offset normal direction.
//the radius of curvature must be larger than offset value.
//line_zero() is used.
std::unique_ptr<MGSurface> MGSphere::offset_c1(
	double ofs_value,	//オフセット量
	int& error//エラーコード 0:成功 -1:面におれがある*
			// -2:曲率半径以上のオフセット不可 -3:面生成コンストラクタエラー
)const{
	assert(sphere());//******Currently this is valid only for sphere*******

	if(!outgoing())
		ofs_value*=-1.;

	double r=radius();
	double scl=(r+ofs_value)/r;
	std::unique_ptr<MGSphere> surf(new MGSphere(*this));
	surf->m_ellipseu*=scl;
	surf->m_ellipsev*=scl;
	error=0;
	return std::unique_ptr<MGSurface>(surf.release());
}

// 指定点が面上にあるか調べる。（面上ならばtrue）
//Test if a point is on the Sphere. If on the Sphere, return true.
bool MGSphere::on(
	const MGPosition& point,	//A point to test 指定点
	MGPosition& puv				//Parameter value of the Sphere will be
								//returned.
)const{
	puv=closest(point);
	return ((point-eval(puv)).len()<=MGTolerance::wc_zero());
}

// Output virtual function.
//Output to ostream メンバデータを標準出力に出力する。
std::ostream& MGSphere::toString(std::ostream &outpt) const{
	outpt<<"MGSphere::"<<this<<",";
	MGSurface::toString(outpt);
	outpt<<" ,m_ellipseu="<<m_ellipseu<<std::endl;
	outpt<<" ,m_ellipsev="<<m_ellipsev;
	return outpt;
}

//test if the surface normal is outgoing from the center or not.
//If the sphere normao is outgoing, retrun true.
bool MGSphere::outgoing()const{
	MGPosition uvmid=param_mid();
	MGVector nrml=normal(uvmid);
	MGVector CP=eval(uvmid)-C();// the vector from center to mid point.
	return CP%nrml>0.;
}

//Obtain parameter space error.
double MGSphere::param_error() const{
	double uerror=m_ellipseu.param_error();
	double verror=m_ellipsev.param_error();
	return sqrt(uerror*uerror+verror*verror);
}

// パラメータ範囲を返す。
//Return parameter range of the Sphere(Infinite box).
MGBox MGSphere::param_range() const{
	return MGBox(m_ellipseu.param_range(), m_ellipsev.param_range());
}

// Compute parameter curve.
//Returned is newed area pointer, and must be freed by delete.
MGCurve* MGSphere::parameter_curve(
	int is_u	//Indicates x is u-value if is_u is true.
	, double x	//Parameter value.
				//The value is u or v according to is_u.
)const{
	if(is_u){
		MGVector MN(m_ellipseu.eval(x));
		double v0=m_ellipsev.param_s();
		double v1=m_ellipsev.param_e();
		double v0R=m_ellipsev.gp_to_radian(v0);
		double v1R=m_ellipsev.gp_to_radian(v1);
		MGEllipse* el=new MGEllipse(C(),MN,B(),MGInterval(v0R,v1R));
		el->change_range(v0,v1);
		return el;
	}else{
		double vR=m_ellipsev.gp_to_radian(x);
		double cosv=cos(vR), sinv=sin(vR);
		MGEllipse* el=new MGEllipse(m_ellipseu*cosv+(C()+B()*sinv));
		return el;
	}
}

//Compute part of the surface limitted by the parameter range bx.
//bx(0) is the parameter (us,ue) and bx(1) is (vs,ve).
//That is u range is from us to ue , and so on.
MGSphere* MGSphere::part(
	const MGBox& uvbx,
	int multiple	//Indicates if start and end knot multiplicities
					//are necessary. =0:unnecessary, !=0:necessary.
)const{
	MGSphere* sphr=new MGSphere(*this);
	sphr->m_ellipseu.limit(uvbx[0]);
	sphr->m_ellipsev.limit(uvbx[1]);
	return sphr;
}

// i must be < perimeter_num().
//When perimeter_num()==0, this function is undefined.
MGCurve* MGSphere::perimeter_curve(int i) const{
	if(i==0){
		return parameter_curve(0,param_s_v());
	}else if(i==2){
		return parameter_curve(0,param_e_v());
	}else if(i==1){
		return parameter_curve(1,param_e_u());
	}else{
		return parameter_curve(1,param_s_u());
	}
}

// 与えられた点にもっとも近い面上の垂直のパラメータ値を返却する。
//Return the nearest perpendicular point of the Sphere from P.
// Function's return value is whether point is obtained(1) or not(0)
int MGSphere::perp_point(
	const MGPosition& P,// 与えられた点
	MGPosition& uv,		//Parameter value of the Sphere will be output
	const MGPosition* uvguess	// guess
)const{
	MGPosition_list list=perps(P);
	if(list.size()){
		uv=list.front();//1st one is the nearer point from P.
		return 1;
	}
	return 0;
}

//Return all(actually at most two) foots of perpendicular straight lines from P.
//When two points are output, the nearer point will be output first
//in MGPosition_list.
MGPosition_list MGSphere::perps(
	const MGPosition& point				// Point in a space(指定点)
) const{
	assert(sphere());
	MGPosition_list list;
	const MGPosition& cntr=sphere_center();
	MGVector dir=point-cntr;
	double len=dir.len();
	if(MGMZero(len)){//when point is on the center of the sphere.
		list.append(MGPosition(param_s_u(), param_s_v()));
		list.append(MGPosition(param_e_u(), param_e_v()));
		return list;
	}
	double r=radius();
	dir*=r/len;
	MGPosition P1=cntr+dir, P2=cntr-dir;//The point on the sphere.
	double u,v;
	compute_uv(P1,u,v); if(in_range(u,v)) list.append(MGPosition(u,v));
	compute_uv(P2,u,v); if(in_range(u,v)) list.append(MGPosition(u,v));
	return list;
}

// 入力パラメータをパラメータ範囲でまるめて返却する。
//Round the input uv into parameter range of the Sphere, 
//return the same value as input.
MGPosition MGSphere::range(const MGPosition& uv) const{
	return MGPosition(m_ellipseu.range(uv[0]), m_ellipsev.range(uv[1]));
}

//Return the space dimension.
int MGSphere::sdim() const{return 3;}

//メンバデータを読み込む関数
void MGSphere::ReadMembers(MGIfstream& buf){
	MGSurface::ReadMembers(buf);
	m_ellipsev.ReadMembers(buf);
	m_ellipseu.ReadMembers(buf);
}

//メンバデータを書き込む関数
void MGSphere::WriteMembers(MGOfstream& buf) const{
	MGSurface::WriteMembers(buf);
	m_ellipsev.WriteMembers(buf);
	m_ellipseu.WriteMembers(buf);
}
