/********************************************************************/
/* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
// tangentCurve.cpp : MGCurveとMGSurfaceからTangent Planeを作る関数
//
#include "MGCLStdAfx.h"
#include "mg/LBRep.h"
#include "mg/Surface.h"
#include "mg/Position.h"
#include "mg/SurfCurve.h"
#include "mg/Interval.h"
#include "mg/Tolerance.h"

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

MGLBRep TP_from_world_curve(
	const MGSurface& srf,	//接続面
	const MGCurve& crv,		//接続曲線
	int order,			//作成するカーブのオーダ
	int& error)	//エラーコード  0:OK, !=0 error code when constructing LBRep.
{
	if(order < 2)return MGLBRep();
	const MGKnotVector& tempKnotVector = crv.knot_vector();
	MGKnotVector knotVector(tempKnotVector, order);	//指定オーダーのノットベクトルに作り替える
	int i;
	for(i = tempKnotVector.order() - 1; i < tempKnotVector.bdim(); i++){
		double	tpara = 0.0,					//テンポラリ
				spara = tempKnotVector(i),		//スパンの始点
				epara = tempKnotVector(i + 1);	//スパンの終点
		if(epara - spara < crv.param_error())continue;	//マルチノットのときの処理(RLBRepのみ)
		//1スパンの分割数を決定する
		MGInterval interval(spara, epara);
		int ndiv = crv.calc_div_num(interval);			//オフセット曲線の分割数
		double shortspan = (epara - spara) / ndiv;
		tpara = spara;
		for (int j = 0; j < ndiv; j++){knotVector.add_data(tpara); tpara += shortspan;}
	}

	// onを使うより perps_guessを使ったほうが早いので
	// 先に第一点を求めておく
	MGPosition guess_prm(2), on_prm(2);
	MGPosition crv_pt(crv.start_point()), srf_tan(srf.sdim());
	MGPosition uv_min(srf.param_s_u(), srf.param_s_v()), 
			uv_max(srf.param_e_u(), srf.param_e_v());
	srf.on(crv_pt, guess_prm);
	//制御点を生成する
	MGNDDArray dataPoint;
	dataPoint.update_from_knot(knotVector);
	int len = dataPoint.length();
	MGBPointSeq bp1(len, srf.sdim());
	for(int j = 0; j < len; j++){
		srf.perp_guess(uv_min, uv_max, 
			crv.eval(dataPoint(j)),guess_prm, on_prm);
		MGPosition pos = srf.unit_normal(on_prm);
		guess_prm = on_prm;
		bp1.store_at(j, pos);
	}

	//精度十分の曲線を生成する
	MGLBRep brep;
	brep = MGLBRep(dataPoint, bp1, knotVector);
	//余分なKnotを削除
	MGTolerance::push();
	// 2.0* sinθ/2 ≒ θ とする。
	MGTolerance::set_line_zero(MGTolerance::angle_zero());
	brep.remove_knot();
	MGTolerance::pop();
	return brep;
}

MGLBRep TP_from_parameter_curve(
	const MGSurface& srf,	//接続面
	const MGCurve& pcrv,	//接続曲線(接続面上のパラメータカーブ)
	const MGCurve& wcrv,	//接続曲線(世界座標上のカーブ)
	int order,			//作成するカーブのオーダ
	int& error)	//エラーコード  0:OK, !=0 error code when constructing LBRep.
{
	// WorldCurveから十分細かいKnotVectorを作成する。
	if(order < 2)return MGLBRep();
	const MGKnotVector& tempKnotVector = wcrv.knot_vector();
	MGKnotVector knotVector(tempKnotVector, order);	//指定オーダーのノットベクトルに作り替える
	int i;
	for(i = tempKnotVector.order() - 1; i < tempKnotVector.bdim(); i++){
		double	tpara = 0.0,					//テンポラリ
				spara = tempKnotVector(i),		//スパンの始点
				epara = tempKnotVector(i + 1);	//スパンの終点
		if(epara - spara < wcrv.param_error())continue;	//マルチノットのときの処理(RLBRepのみ)
		//1スパンの分割数を決定する
		MGInterval interval(spara, epara);
		int ndiv = wcrv.calc_div_num(interval);			//オフセット曲線の分割数を代用
		double shortspan = (epara - spara) / ndiv;
		tpara = spara;
		for (int j = 0; j < ndiv; j++){knotVector.add_data(tpara); tpara += shortspan;}
	}

	//制御点を生成する
	// SurfCurve(src,pcrv)のon()で
	// wcrvの或るパラメータtに対応するpcrvのパラメータ値ptを求め
	// ptからSurfaceのuv値およびNormalを求める
	MGNDDArray dataPoint;
	dataPoint.update_from_knot(knotVector);
	int len = dataPoint.length();
	MGBPointSeq bp1(len, srf.sdim());
	MGSurfCurve scrv(srf, pcrv);
	for(int j = 0; j < len; j++){
		double pt;
		scrv.on(wcrv.eval(dataPoint(j)),pt);
		MGPosition pos = srf.unit_normal(pcrv.eval(pt));
		bp1.store_at(j, pos);
	}

	// 冗長なLBRepを作る
	MGLBRep brep;
	brep = MGLBRep(dataPoint, bp1, knotVector);

	//余分なノットを落とす
	MGTolerance::push();
	// 2.0* sinθ/2 ≒ θ とする。
	MGTolerance::set_line_zero(MGTolerance::angle_zero());
	brep.remove_knot();
	MGTolerance::pop();
	return brep;
}