/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/* ***************************************************** */
/********************************************************************/
// mgcalc_ellipse.cpp
#include "stdafx.h"
#include "Calc/ellipse.h"
#include "mg/Ellipse.h"
#include "mg/Plane.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

namespace mgcalc{
	namespace{
		const MGInterval range_0_2pi(0., mgDBLPAI);
	}
	
	// 焦点と方向と終点を与えて放物線を生成する
	std::unique_ptr<MGEllipse> create_ellipse(
		const MGPosition& F0,
		const MGPosition& F1,
		const MGPosition& P
		){
		if(F0 == F1 || P.is_collinear(F0, F1)){
			return std::unique_ptr<MGEllipse>(nullptr);
		}
		// 楕円の中心は2焦点間の中点
		const MGPosition& origin = (F0 + F1) * .5;
		// 正の実数 a を 線分 F0-P と F1-P の長さの和の半分とする
		double a = (F0.distance(P) + F1.distance(P)) *.5;
		// 正の実数 L は 2焦点間の距離の半分
		double L = F0.distance(origin);

		// major axis を計算する
		const MGVector major = a * (F1 - origin) / L;
		ASSERT(MGAEqual(major.len(), a));

		// major axis を回転することで minor axis の向きを計算する
		MGTransf trans;
		MGVector normal = MGPlane(F0, F1, P).normal();
		trans.set_rotate_3D(normal, mgHALFPAI, origin);
		MGPosition tmp(origin + major);
		tmp *= trans;
		
		MGVector minor = tmp - origin;
		// 長さを調整する
		minor /= minor.len();
		minor *= sqrt(abs(a * a - L * L));

		// 楕円を生成して終了
		return std::unique_ptr<MGEllipse>(new MGEllipse(origin, major, minor, range_0_2pi));
	}

	// 矩形に内接する楕円を生成する
	std::unique_ptr<MGEllipse> create_ellipse(
		const MGPlane&    plane,
		const MGPosition& corner1,
		const MGPosition& corner2
		){
		// 1. plane を法線方向に持ち上げる
		MGPlane pl(plane);
		pl += (corner1 - pl.center()).project(pl.normal());

		// 2. corner2 を今の平面に面直投影する
		MGPosition corner2t = pl.eval(pl.closest(corner2));

		// 3.MGEllipse コンストラクタを用いて楕円を生成する
		MGPosition org = (corner1 + corner2t) * .5;
		MGVector diag  = corner2t - corner1;
		MGVector u = diag.project(pl.u_deriv()) * .5;
		if(u.is_zero_vector()){
			return std::unique_ptr<MGEllipse>(nullptr);
		}
		MGVector v = diag.project(pl.v_deriv()) * .5;
		if(v.is_zero_vector()){
			return std::unique_ptr<MGEllipse>(nullptr);
		}
		return std::unique_ptr<MGEllipse>(new MGEllipse(org, u, v, range_0_2pi));
	}
} // namespace mgcalc
