/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
/** @file QSReshape.cpp
 *  Implementation for class %QSReshape. 
 */
#include "stdafx.h"
#include "mg/SBRep.h"
#include "QS/QSReshape.h"
#include "QS/QSEval.h"

//--------------------------------------------------
// 2004 新接続四辺面対応
//--------------------------------------------------
QSReshape::QSReshape(const MGLBRep* perims[4]){
	assert(perims[0]);	assert(perims[1]);
	assert(perims[2]);	assert(perims[3]);
	std::copy(perims, perims+4, __perimcrv);
}

QSReshape::QSReshape(const MGCurve* perims[4]){
	assert(perims[0]);	assert(perims[1]);
	assert(perims[2]);	assert(perims[3]);
	std::copy(perims, perims+4, __perimcrv);
}

QSReshape::QSReshape(MGLBRep* perims[4]){
	assert(perims[0]);	assert(perims[1]);
	assert(perims[2]);	assert(perims[3]);
	std::copy(perims, perims+4, __perimcrv);
}

QSReshape::QSReshape(MGCurve* perims[4]){
	assert(perims[0]);	assert(perims[1]);
	assert(perims[2]);	assert(perims[3]);
	std::copy(perims, perims+4, __perimcrv);
}

QSReshape::QSReshape(
	const MGLBRep* vmin,
	const MGLBRep* umax,
	const MGLBRep* vmax,
	const MGLBRep* umin
	){
	assert(vmin);	assert(umax);
	assert(vmax);	assert(umin);
	__perimcrv[0] = vmin;
	__perimcrv[1] = umin;
	__perimcrv[2] = umax;
	__perimcrv[3] = vmax;
}

QSReshape::~QSReshape(){
}

// Obtains abscissa of data point for the isocurves.
void QSReshape::abscissa(
	const MGSBRep& srf, 
	MGKnotVector&  t,
	MGNDDArray&    tau
) const{
	const MGKnotVector ttmp(srf.knot_vector_v(), 4);
	t = ttmp;

	MGCurve* umax = srf.perimeter_curve(1);
	MGCurve* umin = srf.perimeter_curve(3);

	// Subdivide each knot span with MGCurve::calc_div_num.
	for(int i = ttmp.order()-1; i < ttmp.bdim(); i++){
		MGInterval iv(ttmp[i], ttmp[i+1]);
		int divu1 = umax->calc_div_num(iv);
		int divu0 = umin->calc_div_num(iv);
		int ndiv = (divu0 > divu1) ? divu0 : divu1;

		double dt = iv.length().value() / ndiv;
		double prm = ttmp[i];
		for(int j = 1; j < ndiv; j++){
			prm += dt;
			t.add_data(prm);
		}
	}
	delete umax;
	delete umin;
	tau.buildByKnotVector(t);
}

// Creates a surface from control points of moved isocurves.
MGSBRep QSReshape::create_surface(){
	// Construct MGSPointSeq
	const int nu = __knotu.bdim();
	const int nv = __knotv.bdim();
	MGSPointSeq sp(nu, nv, 3);
	for(int i = 0; i < nu; i++){
		for(int j = 0; j < nv; j++){
			sp.store_at(i, j, __ucrvs[j].coef(i));
		}
	}
	assert(sp.length_u() == __knotu.bdim());
	assert(sp.length_v() == __knotv.bdim());

	MGSBRep srf;
	srf.buildSBRepFromMemberData(std::move(sp), __knotu, __knotv);
	mgTolSetLineZero lineZeroSet(MGTolerance::line_zero()*.5);
	srf.remove_knot();
	return srf;
}

bool QSReshape::find_perim_isect(
	const MGCurve& imgcrv,
	MGVector&      dirs,
	MGVector&      dire,
	MGVector&      imgs,
	MGVector&      imge,
	int&           peris,
	int&           perie
) const{
	assert(__perimcrv[0]);	assert(__perimcrv[1]);
	assert(__perimcrv[2]);	assert(__perimcrv[3]);

	peris = perie = -1; // indicates whether or not found.
	MGPosition start = imgcrv.start_point();
	MGPosition end = imgcrv.end_point();
	MGPosition poscls(3), poscle(3);

	for(int i = 0; i < 4; i++){
		if(peris == -1){
			double clss = __perimcrv[i]->closest(start);
		
			poscls = __perimcrv[i]->eval(clss);
		
			if(start == poscls){
				assert(peris == -1);
				peris = i;
				dirs = __perimcrv[i]->eval(clss, 1);
				imgs = imgcrv.eval(imgcrv.param_s(), 1);
				if(perie != -1) break;
			}
		}

		if(perie == -1){
			double clse = __perimcrv[i]->closest(end);
			poscle = __perimcrv[i]->eval(clse);

			if(end == poscle){
				assert(perie == -1);
				perie = i;
				dire = __perimcrv[i]->eval(clse, 1);
				imge = imgcrv.eval(imgcrv.param_e(), 1);
				if(peris != -1) break;
			}
		}
	}
	return peris != -1 && perie != -1;
}

bool QSReshape::is_u_iso(
	const MGCurve& imgcrv,
	const MGVector& dps,
	const MGVector& dpe,
	const MGVector& dis,
	const MGVector& die,
	int start_perim,
	int end_perim
) const{
	assert(start_perim != -1);
	assert(end_perim != -1);

	if(abs(start_perim - end_perim) == 2){
		if(start_perim == 0 || end_perim == 0){
			// perim[0],perim[2]
			return true; 
		}else{
			// perim[1],perim[3]
			return false;
		}
	}

	double theta1 = dis.angle(dps);
	double theta2 = die.angle(dpe);

	if(fabs(theta1 - mgHALFPAI) < 0.17){
		switch(start_perim){
		case 0:
		case 2:
			return true;
		case 1:
		case 3:
			return false;
		}
	}else if(fabs(theta2 - mgHALFPAI) < 0.17){
		switch(end_perim){
		case 0:
		case 2:
			return true;
		case 1:
		case 3:
			return false;
		}
	}

//	return (fabs(theta1 - mgHALFPAI) + theta2) < (fabs(theta2 - mgHALFPAI) + theta1);
	if(start_perim == 0 && end_perim == 1){
		return theta1 > theta2;
	}else if(start_perim == 1 && end_perim == 0){
		return theta1 > theta2;
	}else if(start_perim == 1 && end_perim == 2){
		theta2 = mgHALFPAI - theta2;
		return theta1 < theta2;
	}else if(start_perim == 2 && end_perim == 0){
		theta2 = mgHALFPAI - theta2;
		return theta1 > theta2;
	}else if(start_perim == 2 && end_perim == 3){
		return theta1 < theta2;
	}else if(start_perim == 3 && end_perim == 2){
		return theta1 < theta2;
	}else if(start_perim == 0 && end_perim == 3){
		theta1 = mgHALFPAI - theta1;
		return theta1 > theta2;
	}else if(start_perim == 3 && end_perim == 0){
		theta1 = mgHALFPAI - theta1;
		return theta1 < theta2;
	}
	return true;
}

void QSReshape::move_isocurve(const MGCurve& imgcrv){
	// Find the first guess parameter by MGCurve::closest().
	MGPosition guess = __ucrvs.begin()->closest(imgcrv);

	// Work MGCurve::perp_guess() hard.
	std::vector<MGLBRep>::iterator itr = __ucrvs.begin();
	MGPosition sandt(3);
	for(; itr != __ucrvs.end(); ++itr){
		if(itr->order() < 4){
			itr->change_order(4);
		}

		if(!itr->perp_guess(itr->param_s(), itr->param_e(), imgcrv, 
			imgcrv.param_s(), imgcrv.param_e(),
			guess(0), guess(1), sandt)){

			// Precisely computation.
			sandt = itr->closest(imgcrv);
		}
		// Reshape an isocurve.
		double fixed[2] = {itr->param_s(), itr->param_e()};
		itr->move(1, sandt(0), imgcrv.eval(sandt(1)), fixed);

		// Good guess because imgcrv is smooth.
		guess(0) = sandt(0); 
		guess(1) = sandt(1);
	}
}

// Main routine.
bool QSReshape::reshape(
	const MGSBRepTP& tp,
	const MGCurve&   blu,
	const MGCurve&   blv,
	MGSBRep&         srf,
	const MGCurve*   imgcrv
){
	bool u_isocurve = true;
	if(imgcrv){
		MGVector dirs, dire, imgs, imge;
		int peris = -1, perie = -1;
		bool ret = find_perim_isect(*imgcrv, dirs, dire, imgs, imge, peris, perie);
		if(!ret){
			int err = 0;
			srf.buildByBlendWithTP(__perimcrv, tp, &blu, &blv);
			return false;
		}
		u_isocurve = is_u_iso(*imgcrv, dirs, dire, imgs, imge, peris, perie);
	}

	// Without an image curve, create a surface.
	int err = 0;
	
	srf.buildByBlendWithTP(__perimcrv, tp, &blu, &blv);
	if(err)
		return false;

	if(imgcrv){
		if(!u_isocurve) srf.exchange_uv();
		__knotu = srf.knot_vector_u();
		set_isocurve(srf);
		move_isocurve(*imgcrv);
		srf = create_surface();

		if(!u_isocurve){
			srf.exchange_uv();
		}
	}
	return true;
}

bool QSReshape::reshape(
	const MGSBRepTP& tp,
	MGSBRep& srf,
	const MGCurve* imgcrv
){
	bool u_isocurve = true;
	if(imgcrv){
		MGVector dirs, dire, imgs, imge;
		int peris = -1, perie = -1;
		bool ret = find_perim_isect(*imgcrv, dirs, dire, imgs, imge, peris, perie);
		if(!ret){
			srf.buildFromSidesBoolSumWithTP(__perimcrv, tp);
			return false;
		}
		u_isocurve = is_u_iso(*imgcrv, dirs, dire, imgs, imge, peris, perie);
	}

	// Without an image curve, create a surface.
	srf.buildFromSidesBoolSumWithTP(__perimcrv, tp);
	if(imgcrv){
		if(!u_isocurve) srf.exchange_uv();
		__knotu = srf.knot_vector_u();
		set_isocurve(srf);
		move_isocurve(*imgcrv);
		srf = create_surface();

		if(!u_isocurve){
			srf.exchange_uv();
		}
	}
	return true;
}

// 2004 新接続四辺面対応
bool QSReshape::reshape(
	MGSBRepVecTP& subtps,  // 部分TP曲線列
	MGSBRep&              srf,
	const MGCurve*        imgcrv
){
	bool u_isocurve = true;
	if(imgcrv){
		MGVector dirs, dire, imgs, imge;
		int peris = -1, perie = -1;
		bool ret = find_perim_isect(*imgcrv, dirs, dire, imgs, imge, peris, perie);
		if(!ret){
			int err = 0;
			// 
			srf.buildFromSidesBoolSumWithTP(__perimcrv, subtps);
			return false;
		}
		u_isocurve = is_u_iso(*imgcrv, dirs, dire, imgs, imge, peris, perie);
	}

	// Without an image curve, create a surface.
	int err = 0;

	srf.buildFromSidesBoolSumWithTP(__perimcrv, subtps);
	if(imgcrv){
		if(!u_isocurve) srf.exchange_uv();
		__knotu = srf.knot_vector_u();
		set_isocurve(srf);
		move_isocurve(*imgcrv);
		srf = create_surface();

		if(!u_isocurve){
			srf.exchange_uv();
		}
	}
	return true;
}

// Obtains u curves on qsurf.
void QSReshape::set_isocurve(const MGSBRep& qsurf){
	MGNDDArray tau;
	abscissa(qsurf, __knotv, tau);

	int ntau=tau.length();
	__ucrvs.reserve(ntau);
	for(int j = 0; j < ntau; j++){
		MGLBRep isocrv = qsurf.parameter_line(false, tau[j]);
		__ucrvs.push_back(isocrv);
	}
}
