/********************************************************************/
/* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
// MGOpenGLView.cpp : インプリメンテーション ファイル
//
#include "MGCLStdAfx.h"
#include "mg/Tolerance.h"
#include "mg/Position.h"
#include "mg/AttribedGel.h"
#include "mg/DnameControl.h"
#include "mg/Transf.h"
#include "mg/Straight.h"
#include "mg/CSisect.h"
#include "mg/Group.h"
#include "mg/GelPositions.h"
#include "mg/CParam_list.h"
#include "topo/Edge.h"
#include "topo/Loop.h"
#include "topo/Face.h"
#include "topo/Shell.h"
#include "mgGL/OpenGLView.h"
#include "mgGL/GLAttrib.h"
#include "mgGL/SysGLList.h"
#include "mgGL/glViewAttrib.h"
#include "mgGL/VBO.h"
#include "mgGL/glslprogram.h"

using namespace std;

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

namespace{
	int DISPNAME_BASE_ID=1;
}
unsigned OpenGLStartDisplayName(){
	return DISPNAME_BASE_ID;
}


/////////////////////////////////////////////////////////////////////////////
// MGOpenGLView
namespace{
	GLuint glErr;
	const glm::vec3 ORIGIN(0.,0.,0.);
};

MGOpenGLView::MGOpenGLView(
	bool perspective	//indicates if the view is pespective or not.
):m_parent_glView(0), m_viewAttrib(perspective),
m_width(0), m_height(0),
m_hRC(0),m_hDC(0),m_smooth(.001f), m_pick_aperture(5.),
m_display_list(0),m_dpi(96.0f){
//Smoothness is about 100 division of the screen's width curve:m_smooth(.01).
//Pick aperture is 5 pixels:m_pick_aperture(5.).

	draw_param().set_span_length(span_length());
	//set_defalut_colors();
	for(int i=0; i<3; i++)
		m_center_current[i]=0.;
}

//Construct from MGglViewAttrib.
MGOpenGLView::MGOpenGLView(
	const MGglViewAttrib& glatr
):m_parent_glView(0),
m_width(0), m_height(0),m_hRC(0),m_hDC(0),
m_display_list(0),m_viewAttrib(glatr),
m_smooth(.001f),m_pick_aperture(5.),m_dpi(96.0f){
//Smoothness is about 100 division of the screen's width curve:m_smooth(.01).
//Pick aperture is 5 pixels:m_pick_aperture(5.).

	draw_param().set_span_length(span_length());
	//set_defalut_colors();
	for(int i=0; i<3; i++)
		m_center_current[i]=(float)glatr.m_center[i];

}

MGOpenGLView::~MGOpenGLView(){
	::wglMakeCurrent(NULL,  NULL);
	/// OpenGLのレンダリングコンテキストの開放のみ行うこと。
}

//Copy the informations of glview2 into this.
//Data that is not copied from glview2 are:
//m_hRC(Rendering context), m_hDC(Device Context).
//m_hRC and m_hDC can be set using setDCRC().
//m_sysgllist must be made by invoking openGL's
//display list generation functions.
void MGOpenGLView::copy(const MGOpenGLView& glview2){
	m_Bcolor=glview2.m_Bcolor;
	m_Gcolor=glview2.m_Gcolor;
	m_smooth=glview2.m_smooth;
	m_pick_aperture=glview2.m_pick_aperture;
	m_viewAttrib=glview2.m_viewAttrib;
	setLookAtMat();

	m_hRC=glview2.m_hRC;
	m_hDC=glview2.m_hDC;

}
void MGOpenGLView::copy(const MGglViewAttrib& glatr){
	m_viewAttrib=glatr;
	setLookAtMat();

	const MGVector& upV=glatr.view_up_vector();
	MGUnit_vector XAxis=upV*eye_position();
	for(int i=0; i<3; i++){
		m_XAxis_current[i]=(float)XAxis[i];
		m_center_current[i]=(float)glatr.m_center[i];
		m_up_vector_current[i]=(float)upV[i];
	}
	draw_param().set_span_length(span_length());
}

//Return display list name.
mgVBO* MGOpenGLView::display_list(){
	if(!has_parent_OpenGLView())
		return m_display_list;
	return get_parent_OpenGLView()->display_list();
}

const MGDrawParam& MGOpenGLView::draw_param()const{
	return mgVBOElement::getDrawParam();
}
MGDrawParam& MGOpenGLView::draw_param(){
	return mgVBOElement::getDrawParam();
}

///Set up drawing environment.
void MGOpenGLView::setupDrawEnv(const MGColor& backColor, bool selection){
	if(m_width>0 && !selection)
		glViewport(0,0,m_width,m_height);
	const float* Bcolr=backColor.color();
    glClearColor(Bcolr[0], Bcolr[1], Bcolr[2], Bcolr[3]);
	glClearDepth(1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	if(selection){
		glDisable(GL_BLEND);
	}else{
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	}

	glEnable(GL_POLYGON_OFFSET_FILL);// ポリゴンオフセットフィルを設定
	glPolygonOffset(1., 1.);
}

void MGOpenGLView::execDefaultStaticAttrib(){
	glDisable(GL_CULL_FACE);// 両面を描画
	mgGLSL::execStaticColorAttrib(Gcolor());
	mgGLSL::execStaticLineWidth(1.f);
	mgGLSL::execStaticLineStipple(0,0);
	mgGLSL::execLightMode(0);//Light Off
}

//Draw the scene defined in this view including the current objects as hilighted.
void MGOpenGLView::drawScene(const MGPickObjects* pobjs){
	makeRCCurrent();
	if(has_display_list()){

	setupDrawEnv(Bcolor());
	glm::mat4 modelViewMatSave=m_viewAttrib.m_modelViewMat;//Save

	bool initialize=true;
	setProjectionMat2GLSL(initialize);//Set the projection matrix.
	setModelViewMat2GLSL();//Set the model matrix.
	setProjModelViewMat2GLSL();
	setNdcMat2GLSL();
	setDpiFactor2GLSL();

	mgGLSLProgram* glsl=mgGLSLProgram::getCurrentGLSLProgram();
	glsl->setFuncType(mgGLSL::standard);

//1. Construction plane drawing
	execDefaultStaticAttrib();
	MGConstructionPlane& cpl=m_viewAttrib.cplane();
	if(cpl.enabled())
		cpl.draw();
//	glClear(GL_DEPTH_BUFFER_BIT);
	
//2. Target objects drawing
	execDefaultStaticAttrib();
	MGOpenGLView* gltarget=this;
	if(m_parent_glView)
		gltarget=m_parent_glView;
	gltarget->m_display_list->draw(viewMode());

//3. the system generated display drawing.
	execDefaultStaticAttrib();
	gltarget->m_sysgllist.draw_list(viewMode());

//4.draw command specific pictures of parent OpenGLView.
	drawCommandDrawer(gltarget->m_command_drawersCommon);


//5.draw command specific pictures of this OpenGLView.
	drawCommandDrawer(m_command_drawersSpecific);

//6. current objects highlighting.
	highlight(pobjs);

	m_viewAttrib.m_modelViewMat=modelViewMatSave;//Restore.
	::SwapBuffers(m_hDC);

	}//if(has_display_list())
	glFinish();
}

///Draw std::list<mgVBO*>.
void MGOpenGLView::drawCommandDrawer(
	std::list<mgVBO*>& drawers
){
	execDefaultStaticAttrib();
	std::list<mgVBO*>::iterator i=drawers.begin(), ie=drawers.end();
	for(; i!=ie; i++){
		mgVBO* drawer=*i;
		if(drawer)
			drawer->draw();
	}
}

//Highlight pobjs.
void MGOpenGLView::highlight(const MGPickObjects* pobjsP){
	if(!pobjsP)
		return;

	int ld=line_density();
	GLboolean depthEnabled=glIsEnabled(GL_DEPTH_TEST);
	GLboolean blendEnabled=glIsEnabled(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);

	const MGPickObjects& pobjs=*pobjsP;
	int nHL=pobjs.size();
	for(int i=0; i<nHL; i++){
		pobjs[i].hilight_using_display_list(ld);
	}

	if(depthEnabled)
		glEnable(GL_DEPTH_TEST);
	if(blendEnabled)
		glEnable(GL_BLEND);
}

///Constructglm::lookAtMatrix from eye_position() and view_up_vector().
void MGOpenGLView::setLookAtMat(){
	const MGPosition& eyeP=eye_position(); glm::vec3 eye(eyeP[0],eyeP[1],eyeP[2]);
	const MGVector& upP=view_up_vector(); glm::vec3 up(upP[0],upP[1],upP[2]);
	m_lookAtMat=glm::lookAt(eye,ORIGIN,up);
}

//get ModelView matrix of OpenGL.
void MGOpenGLView::get_model_matrix(
	glm::mat4& modelMat	//double modelMat[16] ///<OpenGL's model matrix
)const{
	glm::mat4 viewMat=m_lookAtMat*m_viewAttrib.m_modelViewMat;
	const MGPosition& cntr=center(); glm::vec3 cntr2(-cntr[0], -cntr[1], -cntr[2]);
	modelMat=glm::translate(viewMat,cntr2)*m_viewAttrib.m_PreCenterMat;//glTranslated;
}

///get projection matrix, given the viewport data 
void MGOpenGLView::get_projection_matrix(
	const int vp[4],///<viewport data ={left, bottom, widht, height}
	glm::mat4& projMat	///<OpenGL's projection matrix
)const{
	double height2=view_volume_height()*.5;
	double wide2=height2*double(vp[2])/double(vp[3]);
	float left=float(m_viewAttrib.m_cx-wide2), right=float(m_viewAttrib.m_cx+wide2),
		bottom=float(m_viewAttrib.m_cy-height2), top=float(m_viewAttrib.m_cy+height2);

	float znear=(float)view_volume_near(), zfar=(float)view_volume_far();
	if(is_perspective()){
		projMat=glm::frustum(left,right,bottom,top,znear,zfar);
	}else{
		projMat=glm::ortho(left,right,bottom,top,znear,zfar);
	}
}

//get viewport of OpenGL.
void MGOpenGLView::get_viewport(
	glm::ivec4& vp//int vp[4]	//OpenGL's projection matrix
)const{
	get_viewport(&vp[0]);
}
void MGOpenGLView::get_viewport(
	int vp[4]
		///<(vp[0],vp[1]) is (left,bottom) coordinates.
		///<(vp[2],vp[3]) is (width, height) of the viewport.
)const{
	vp[0]=0;
	vp[1]=0;
	vp[2]=m_width;
	vp[3]=m_height;
}

///get window(m_width, m_height);
void MGOpenGLView::get_window(int& width, int& height)const{
	width=m_width;
	height=m_height;
}

//Get the surface parameter value uv(u,v) where screen coordinate (sx,sy) is projected on.
//If no projection points are found, the nearest point to a perimeter of surf will
//be returned.
bool MGOpenGLView::get_surface_parameter_glv(
	const MGFSurface& surf,
	int sx, int sy,	//Screen coordinates. (left, bottom) is (0,0).
	MGPosition& uv	//surface parameter (u,v) where (sx,sy) is projected on will be returned.
)const{
	MGStraight sl;
	unproject_to_sl_glv(sx,sy,sl);
	MGCSisect_list csis=surf.isect(sl);
	if(csis.size()){
		uv=csis.front().param_surface();
		return true;
	}
	uv=surf.closest_on_boundary(sl);
	return false;
}

//Test if this has a display list to draw.
bool MGOpenGLView::has_display_list()const{
	if(m_display_list)
		return true;
	if(!has_parent_OpenGLView())
		return false;
	return get_parent_OpenGLView()->has_display_list();
}

//Initialize the viewing environment.
//When the eye position is necessary to update, setEyePositionUpVector() must be invoked
//before initializeViewingEnvironmentByBox since the current eye position direction is used.
//Initialization is done by the parameter box.
void MGOpenGLView::initializeViewingEnvironmentByBox(
	const MGBox& box
){
	m_viewAttrib.compute_viewing_environment(box);
	setLookAtMat();
	const MGPosition& cntr=center();
	for(int i=0; i<3; i++)
		m_center_current[i]=(float)cntr[i];
	draw_param().set_span_length(span_length());
}

//Update the center and the scale of the view.
///The pespectiveness and the cplane are unchanged.
void MGOpenGLView::updateCenterScalle(
	const MGPosition& center,
	double diameter///<diameter of the view. This is set to m_diameter.
		///<diameter of the sphere that sorround the model.
		///<If diameter<=0. the current diameter is not updated.
){
	for(int i=0; i<3; i++)
		m_center_current[i]=(float)center[i];
	if(diameter<=0.)
		diameter=m_viewAttrib.diameter();
	m_viewAttrib.compute_viewing_environment(center,diameter);
	setLookAtMat();
}

//Initialize the viewing environment to view objects projected onto a plane.
///The pespectiveness is unchanged.
//center of the object is the origin of the plane.
void MGOpenGLView::update_viewing_environment(
	const MGPlane& plane,
	double diameter//diameter of the view. This is set to m_diameter.
					///<diameter of the sphere that sorround the model.
){
	const MGPosition& cntr=plane.root_point();
	for(int i=0; i<3; i++)
		m_center_current[i]=(float)cntr[i];

	if(diameter<=0.)
		diameter=m_viewAttrib.diameter();
	MGPosition eyeP(0.,0.,1.);
	MGVector upVector(0.,1.,0.);
	setEyePositionUpVector(eyeP,upVector);
	m_viewAttrib.compute_viewing_environment(cntr,diameter);
	setLookAtMat();

	MGMatrix mat;
	mat.set_xy_axis(plane.u_deriv(), plane.v_deriv());

	glm::mat4 glmat;//double glmat[16];
	mat.convert_to_glMatrix(glmat);
	m_viewAttrib.m_modelViewMat*=glmat;//glMultMatrixd(glmat);
	m_viewAttrib.m_cplane.set_plane(plane);
}

//locate the screen coordinate (x,y) in the  3D world coordinate.
//(x, y)'s origin is (left, bottom) of the screen.
MGPosition MGOpenGLView::locate_glv(int x, int y, MGPosition* uv)const{
	MGStraight sl;
	unproject_to_sl_glv(x,y,sl);
	const MGConstructionPlane& pl=cplane();
	MGPosition xyz,uv2;
	xyz=pl.locate(sl,uv2);
	if(uv)
		*uv=uv2;
	return xyz;
}

//construct the construction plane along with its display list.
void MGOpenGLView::make_construction_plane(
	const MGPosition& mid,	//center of the construction plane.
	const MGVector& uderi,	//u-axis vector of the construction plane.
	const MGVector& vderi,	//v-axis vector of the construction plane.
	double uspan,			//span length between the lines along u-axis.
	double vspan,			//span length between the lines along v-axis.
	int ulnum,			//number of lines to draw along u-axis.
	int vlnum			//number of lines to draw along v-axis.
){
	m_viewAttrib.m_cplane.set_grid_data(MGPlane(uderi,vderi,mid), uspan,vspan,ulnum,vlnum);
}

//Make openGL display list in this glview.
void MGOpenGLView::make_display_list(const MGContext& ctx,const MGGroup& grp){
//Generate OpenGL display list.
	if(has_parent_OpenGLView())
		return;
	setDrawParam(ctx);
	m_display_list=grp.dlist_name();
	grp.make_display_list(viewMode());
}

#define IS_SELECTION true
static const MGColor NullColor(0.,0.,0.,0.);
//Function's return value is the number of hit objects.
int MGOpenGLView::pick_to_select_buf(
	const float center[2],///<Screen coordinates. (left, bottom) is (0,0).
	const float delta[2],///<(aperturex, aperturey).
	mgVBO* display_list,	//display list that includes pick objects.
	std::set<unsigned>& selected///Selected data will be returned. This data consist of
			///the data set by selectName.
){
	glm::ivec4 viewportOld;
	get_viewport(viewportOld);//glGetIntegerv(GL_VIEWPORT,&viewportOld[0]);//Current viewport

	glm::vec2 c2(center[0],center[1]);
	glm::vec2 delta2(delta[0],delta[1]);
	glm::mat4 projMatSave=m_projMat;//Save//glPushMatrix();
	m_projMat=glm::pickMatrix(c2,delta2,viewportOld);
	setProjectionMat2GLSL();

	glm::mat4 modelViewMatSave=m_viewAttrib.m_modelViewMat;//Save//glPushMatrix();
	setModelViewMat2GLSL();//set_model_matrix
	setProjModelViewMat2GLSL();
	setNdcMat2GLSL();
	setDpiFactor2GLSL();

	//Set the target selection viewport.
	delta2*=.5;
	glm::vec2 lowerLeft=c2-delta2;;
	int viewport[4]=
		{int(lowerLeft[0]+.5), int(lowerLeft[1]+.5),GLsizei(delta[0]), GLsizei(delta[1])};
	int& x=viewport[0];
	int& y=viewport[1];
	int& w=viewport[2];
	int& h=viewport[3];

	int xOld=viewportOld[0];
	int yOld=viewportOld[1];
	int wOld=viewportOld[2];
	int hOld=viewportOld[3];

	if(x<xOld)
		x=xOld;
	if((x+w)>(xOld+wOld))
		w=xOld+wOld-x;
	if(y<yOld)
		y=yOld;
	if((y+h)>(yOld+hOld))
		h=yOld+hOld-y;

	if(h<=0 || w<=0)
		return 0;

	glViewport(x,y,w,h);
	glDrawBuffer(GL_BACK);
	//if((glErr=glGetError()) != GL_NO_ERROR){
	//	CString msg(gluErrorString(glErr));
	//	COUT<<"MGOpenGLView::pick_to_select_buf::glDrawBuffer::"<<(TCAST)msg<<std::endl;
	//}
	setupDrawEnv(NullColor,IS_SELECTION);

	//Target objects drawing
	display_list->selectionDraw(viewMode());
	glReadBuffer(GL_BACK);
	extractSelected(viewport,selected);

	m_viewAttrib.m_modelViewMat=modelViewMatSave;//Restore.//glPopMatrix();
	m_projMat=projMatSave;//glPopMatrix();
	glViewport(xOld,yOld,wOld,hOld);
	return (int)selected.size();
}

//Pick objects in the display list generated by make_display_list.
//Function's return value is MGPickObject vector in m_CurrentObjects member data.
//All the objects which were inside the pick aperture will be output.
//This data can be accessed using current_object(), or current_PickObject().
//pick will invoke makeRCCurrent();
MGPickObjects MGOpenGLView::pick_glv(
	const float center[2],///<Screen coordinates. (left, bottom) is (0,0).
	float aperturex,///<specifies pick aperture of x and y.
	float aperturey,///<When <=0. value is specified, default value(the value
			///<obtained by pick_aperture() will be used.
	const MGAbstractGels& objtypes
){
	MGPickObjects pobjs;
	if(!has_display_list())
		return pobjs;

	if(aperturex<=0.)
		aperturex=pick_aperture();
	if(aperturey<=0.)
		aperturey=pick_aperture();
	float aperture[2]={aperturex,aperturey};

	std::set<unsigned> selected;
	makeRCCurrent();
	mgVBO* vbo=display_list();
	int objnum=pick_to_select_buf(center,aperture,vbo,selected);
	if(!objnum)
		return pobjs;

	MGDNameControl& dnc=getDNameControlInstance();
	std::vector<MGPickObject> shels;//To exclude the same shell picking, shell's pickObject are stored.

	std::set<unsigned>::iterator i=selected.begin(), iend=selected.end();
	for(; i!=iend; i++){
		mgVBO* pickedi=dnc.VBO_from_dlistName(*i);
		if(!pickedi)
			continue;
		MGAttribedGel* objA=pickedi->gel();//Lowest name is MGObject.
		if(!objA)
			continue;
		MGObject* obj=dynamic_cast<MGObject*>(objA);//Lowest object pointer.
		if(!obj)
			continue;//This must not happen(Lowest name is MGObject).

		std::vector<mgVBO*> vbos;
		if(!pickedi->buildVBOHierarchy(*vbo,vbos))
			continue;//This must not happen.

		size_t n=vbos.size();assert(n>=2);
		MGAttribedGel* grpA=vbo->gel();assert(grpA);
		MGGroup* grp=static_cast<MGGroup*>(grpA);//Top MGGroup.
		MGPickObject pobj(grp,obj);
		for(size_t k=1; k<=n-2; k++){
			mgVBO* vbok=vbos[k];
			MGAttribedGel* gl=vbok->gel();//Lower gel is MGGroup or MGShell.
			pobj.append_lower_gel(gl);
		}
		if(!pobj.is_shell_face()){
			if(obj->type_is(objtypes))
				pobjs.push_back(pobj);
		}else{
			//When is_shell_face.
			MGShell* shelli=pobj.get_shell_of_shell_face();
			if(!shelli->type_is(objtypes))
				continue;

			//Exclude the same shell and employ the 1st face in the shell.
			int nzs=(int)shels.size(), j;
			for(j=0; j<nzs; j++)
				if(shels[j].get_shell_of_shell_face()==shelli)
					break;
			if(j==nzs)//if the same shell not found
				shels.push_back(pobj);
			else//if found
				continue;
		}
	}
	size_t nshel=shels.size();
	for(size_t j=0; j<nshel; j++)
		pobjs.push_back(shels[j]);

	return pobjs;
}

class mgPerimeterSelection: public mgVBO{
public:	
const MGSurface& m_surf;
const MGDrawParam& m_dparam;
mgPerimeterSelection(const MGSurface& surf,const MGDrawParam& para)
:m_surf(surf),m_dparam(para){};

///m_gelの描画データ作成のみをおこなう。
///すでに作成済みであっても強制的に再作成を行う。
///m_gel=0のときはなにもしない。
void make_display_list(MGCL::VIEWMODE viewMode=MGCL::DONTCARE);

///描画関数selectionDraw()は、Object選択のための表示処理をする。
///通常のdrawとの相違:///Colorとしてm_bufferIDを用い、size処理以外の
///attributesの処理(normal, texture, color)をしない。
void selectionDraw(MGCL::VIEWMODE viewMode=MGCL::DONTCARE);
};

void mgPerimeterSelection::make_display_list(MGCL::VIEWMODE viewMode){
	clearElements(mgVBO::BOTH);
	
	int nperi=m_surf.perimeter_num();
	int ldensity=m_dparam.line_desity_wire_face();
	for(int i=0; i<nperi; i++){
		std::unique_ptr<MGCurve> peri(m_surf.perimeter_curve(i));
		peri->drawWire(*this,ldensity);
	}
	setDirty(false);
}
void mgPerimeterSelection::selectionDraw(MGCL::VIEWMODE viewMode){
	if(!is_made())
		make_display_list();

	mgGLSLProgram* glsl=mgGLSLProgram::getCurrentGLSLProgram();
	glsl->setFuncType(mgGLSL::Select);

	execModelTypeAttrib();

	size_t n=m_elements.size();
	for(unsigned i=0; i<n; i++){
		mgGLSL::setColorAsSelectionName(i+1);
		mgVBOElement* elmi=m_elements[i];
		elmi->selectionDraw(MGCL::WIRE);
	}
}

//Pick a perimeter of the surface surf. That is, obtain the perimeter number
//that passes input (sx,sy) when drawn in the current view matrix.
//Function's return value is perimeter number picked.
//When no perimeters are picked, -1 will be returned.
int MGOpenGLView::pick_perimeter_glv(
	const MGSurface& surf,
	int sx, int sy,	///<Screen coordinates. (left, bottom) is (0,0).
	MGPosition* uv,	//surface parameter (u,v) nearest to (sx,sy) will be returned.
	float aperturex,//specifies pick aperture of x and y.
	float aperturey//When <=0. value is specified, default value(the value
			//obtained by pick_aperture() will be used.
){
	mgPerimeterSelection periSel(surf,draw_param());

	int perimeter=-1;
	if(aperturex<=0.) aperturex=pick_aperture();
	if(aperturey<=0.) aperturey=pick_aperture();
	std::set<unsigned> selected;
	float center[2]={(float)sx,(float)sy};
	float delta[2]={aperturex,aperturey};
	pick_to_select_buf(center,delta,&periSel,selected);		
	int objnum=	(int)selected.size();
	if(objnum>0){
		perimeter=*(selected.begin())-1;				
		if(uv){ // if parameter is needed
			double t;
			std::unique_ptr<MGCurve> peri(surf.perimeter_curve(perimeter));
			get_near_position(peri.get(),center,t);
			*uv=surf.perimeter_uv(perimeter,t);
		}
	}
	return perimeter;
}

class mgEdgeSelection: public mgVBO{
public:	
const MGLoop& m_loop;
const MGDrawParam& m_dparam;
mgEdgeSelection(const MGLoop& loop,const MGDrawParam& para)
:m_loop(loop),m_dparam(para){};

///m_gelの描画データ作成のみをおこなう。
///すでに作成済みであっても強制的に再作成を行う。
///m_gel=0のときはなにもしない。
void make_display_list(MGCL::VIEWMODE viewMode=MGCL::DONTCARE);

///描画関数selectionDraw()は、Object選択のための表示処理をする。
///通常のdrawとの相違:///Colorとしてm_bufferIDを用い、size処理以外の
///attributesの処理(normal, texture, color)をしない。
void selectionDraw(MGCL::VIEWMODE viewMode=MGCL::DONTCARE);
};
void mgEdgeSelection::make_display_list(MGCL::VIEWMODE viewMode){
	int ldensity=m_dparam.line_desity_wire_face();
	int nedge=m_loop.number_of_edges();
	for(int j=0; j<nedge; j++){
		const MGEdge* edge2=m_loop.edge(j);
		MGEdge& be=*(edge2->make_binder_with_curve());
		MGTrimmedCurve cij=be.trimmed_curve();
		cij.drawWire(*this,ldensity);
	}
	setDirty(false);
}
void mgEdgeSelection::selectionDraw(MGCL::VIEWMODE viewMode){
	if(!is_made())
		make_display_list();

	mgGLSLProgram* glsl=mgGLSLProgram::getCurrentGLSLProgram();
	glsl->setFuncType(mgGLSL::Select);

	execModelTypeAttrib();

	size_t n=m_elements.size();
	for(unsigned i=0; i<n; i++){
		mgGLSL::setColorAsSelectionName(i+1);
		mgVBOElement* elmi=m_elements[i];
		elmi->selectionDraw(MGCL::WIRE);
	}
}

//Pick an edge of the face f. That is, obtain the edge number
//that passes input (sx,sy) when drawn in the current view matrix.
//Function's return value is the edge pointer picked.
//When no edges are picked, null will be returned.
const MGEdge* MGOpenGLView::pick_edge_glv(
	const MGFace& f,
	int sx, int sy,	///<Screen coordinates. (left, bottom) is (0,0).
	MGPosition* uv,	//surface parameter (u,v) nearest to (sx,sy) will be returned.
	float aperturex,//specifies pick aperture of x and y.
	float aperturey//When <=0. value is specified, default value(the value
			//obtained by pick_aperture() will be used.
){
	const MGEdge* edge=0;
	if(aperturex<=0.) aperturex=pick_aperture();
	if(aperturey<=0.) aperturey=pick_aperture();

	glm::ivec4 viewport;//GLint viewport[4];
	get_viewport(viewport);//glGetIntegerv(GL_VIEWPORT,&viewport[0]);
	float centr[2]={float(sx),float(sy)};
	glm::vec2 c2(centr[0],centr[1]);
	glm::vec2 delta(aperturex,aperturey);
	m_projMat=glm::pickMatrix(c2,delta,viewport);

	int nloop=f.number_of_loops();
	for(int i=0; i<nloop; i++){
		const MGLoop& li=*(f.loop(i));
		mgEdgeSelection edgeSel(li,draw_param());
		std::set<unsigned> selected;
		pick_to_select_buf(centr,&delta[0],&edgeSel,selected);		
		size_t objnum=	selected.size();
		if(objnum){
			edge=li.edge(*selected.begin()-1);
			if(uv){
				MGEdge& be=*(edge->make_binder_with_curve());
				MGTrimmedCurve cij=be.trimmed_curve();
				double t;
				get_near_position(&cij,centr,t);
				*uv=edge->eval(be.param_pcell(t));
			}
			break;
		}
	}
	return edge;
}

//Determine if screen coordinate (sx,sy) is closer to the start point or to the end
//of the curve curve.
//Functin's return value is 0: if start point, 1: if end point.
int MGOpenGLView::pick_start_end_glv(
	const MGCurve& curve,
	int sx, int sy	//Screen coordinates. (left, bottom) is (0,0).
){
	MGStraight sl;
	unproject_to_sl_glv(sx,sy,sl);
	MGPosition P0=curve.start_point(), P1=curve.end_point();
	if(sl.distance(P0)<=sl.distance(P1))
		return 0;
	return 1;
}

///Extract selectionName data from the frame buffer drawn by selectionDraw();
void MGOpenGLView::extractSelected(
	const int viewport[4],///Viewport of the selection target window.
	std::set<unsigned>& selected///Selected name data will be returned.
		/// This data consist of the data set by selectionDraw.
){
	GLint x=viewport[0], y=viewport[1];
	GLsizei width=viewport[2], height=viewport[3];
	int numPixels=width*height;
	unsigned* pixels=new unsigned[numPixels];
	for(int i=0; i<numPixels; i++) pixels[i]=0;
	//glFlush();
	glReadPixels(x,y,width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixels);
	//if((glErr=glGetError()) != GL_NO_ERROR){
	//	CString msg(gluErrorString(glErr));
	//	COUT<<"MGOpenGLView::extractSelected::glReadPixels::"<<(TCAST)msg<<std::endl;
	//}
	for(int i=0; i<numPixels; i++){
		mgGLSLProgram::SELECT_NAME nub;
		nub.uiName=pixels[i];
		unsigned pixeli=nub.uiName;//unsigned pixeli=pixels[i];
		if(pixeli!=0){
			selected.insert(pixeli);
		}
	}
	delete[] pixels;
}

//Project world coordinates to OpenGL's screen coordinates.
//If modelMat, projMat, or vp is not input, project will ask OpenGL to get them.
//Generally, users of project are recommended to get modelMat, projlMat, or
//vp, and input them to project if continuous multiple use of project will take place.
//If one of modelMat, projlMat, or vp is not input, makeRCCurrent() must be invoked
//before use of project.
void MGOpenGLView::project(
	const MGPosition& world,
	MGPosition& screen,
	const glm::mat4* modelMat,	//OpenGL's model matrix
	const glm::mat4* projlMat,	//OpenGL's projection matrix
	const glm::ivec4* vp		//OpenGL's viewport
) const{
	const glm::mat4* model=modelMat;
	const glm::mat4* proj=projlMat;
	const glm::ivec4* vp2=vp;
	glm::mat4 modelM, projM;
	glm::ivec4 vp3;
	if(!vp2){
		vp2=&vp3;
		get_viewport(vp3);
	}

	if(!proj){
		proj=&projM;
		const int* vport=&((*vp2)[0]);
		get_projection_matrix(vport,projM);
	}
	if(!model){
		model=&modelM;
		get_model_matrix(modelM);
	}

	screen.resize(3);
	glm::vec3 obj(world[0],world[1],world[2]);
	glm::vec3 v=glm::project(obj,*model,*proj,*vp2);
	screen=MGPosition(v[0], v[1], v[2]);
}

//Rotate the current view by the angle along the vector(x,y,z),
//performs a counterclockwise rotation of angle angle about
//the vector from the origin through the point (x, y, z).
///The rotation matrix made is stored in m_PreCenterMat.
void MGOpenGLView::rotate(float angle, float x, float y, float z){
	const MGPosition& c=center();
	glm::vec3 cntr((float)c[0],(float)c[1],(float)c[2]);
	glm::mat4& preM=m_viewAttrib.m_PreCenterMat;
	preM=glm::translate(preM,cntr);
	preM=glm::rotate(preM,angle,glm::vec3(x,y,z));//Rotate around center.
	preM=glm::translate(preM,-cntr);
}

///Rotate the current view by the angle along vector around center.
///Performs a counterclockwise rotation.
///The rotation matrix made is stored in m_PreCenterMat.
void MGOpenGLView::rotate(float angle, float vector[3], const MGPosition& center){
	glm::vec3 vec(vector[0],vector[1],vector[2]);
	glm::vec3 cntr((float)center[0],(float)center[1],(float)center[2]);
	glm::mat4& preM=m_viewAttrib.m_PreCenterMat;
	preM=glm::translate(preM,cntr);
	preM=glm::rotate(preM,angle,vec);//Rotate around center.
	preM=glm::translate(preM,-cntr);
}

///Rotate the current view
///by the angle[0] around m_up_vector_current(Y-direction of the screen),
///and by the angle[1] around m_XAxis_current(view-up-vector*eye-vector=X-direction of the screen).
///Rotation is performed around m_center_current.
///The rotation matrix made is stored in m_PreCenterMat.
void MGOpenGLView::rotate(const float angle[2]){
	glm::mat4& preM=m_viewAttrib.m_PreCenterMat;
	preM=glm::translate(preM,m_center_current);
	preM=glm::rotate(preM,angle[1],m_XAxis_current);//Rotate around center.
	preM=glm::rotate(preM,angle[0],m_up_vector_current);//Rotate around center.
	preM=glm::translate(preM,-m_center_current);
}

void MGOpenGLView::set_center_current(int x, int y){
	MGStraight sl1, sl2;
	unproject_to_sl_glv(x,y,sl1);

	const MGPosition& C=center();
	MGPosition centrOnCursor=sl1.eval(sl1.param(C));
	for(int i=0; i<3; i++)
		m_center_current[i]=(float)centrOnCursor[i];
	//std::cout<<",C=("<<m_center_current[0]<<","<<m_center_current[1]<<","<<m_center_current[2]<<")";

	unproject_to_sl_glv(x,y+200,sl2);
	MGPosition centerUp=sl2.eval(sl2.param(C));
	MGVector upV(3);
	for(int i=0; i<3; i++){
		upV(i)=centerUp[i]-m_center_current[i];
	}
	//std::cout<<",Y=("<<upV[0]<<","<<upV[1]<<","<<upV[2]<<")";
	//double error1=upV.len();
	upV.set_unit();
	for(int i=0; i<3; i++){
		m_up_vector_current[i]=(float)upV[i];
	}

	const MGVector& eyeP=sl1.direction();
	MGVector X=upV*eyeP.normalize();//Screnn's X-direction
	//std::cout<<"X=("<<X[0]<<","<<X[1]<<","<<X[2]<<")";
	//double error2=X.len();
	//std::cout<<"errot1,2="<<error1<<","<<error2<<std::endl;
	X.set_unit();
	for(int i=0; i<3; i++)
		m_XAxis_current[i]=(float)X[i];
}

///Scale the current view by the factor and the view center.
void MGOpenGLView::scale(
	double factor,	///< saling factor.
	int* center///< scaling center of the screen coordinate (sx, sy) on the view plane,
		///where sx=center[0], sy=center[1] if center!=0.
){
	if(center){	
		GLint vp[4]; get_viewport(vp);//glGetIntegerv(GL_VIEWPORT,vp);
		double wx,wy;
		screen_to_world(vp+2,double(center[0]),double(center[1]),wx,wy);
		m_viewAttrib.m_cx=wx-(wx-m_viewAttrib.m_cx)/factor;
		m_viewAttrib.m_cy=wy-(wy-m_viewAttrib.m_cy)/factor;
	}
	m_viewAttrib.m_scale*=factor;
}

//Get redering context.
//RCは固定が前提としているので、もしdcがセットされているものにdcをsetすると
//dcだけを変更することとなる
void MGOpenGLView::setDCRC(HDC dc, HGLRC rc){
	m_hDC=dc;
	m_hRC=rc;
	makeRCCurrent();
}

void MGOpenGLView::set_window(int width, int height){
	if(width<=0 || height<=0)
		return;
	m_width=width;m_height=height;
}

///Set ProjModelView matrix to GLSL using setUniform();
void MGOpenGLView::setProjModelViewMat2GLSL(){
	glm::mat4 ProjModelView=m_projMat*m_viewAttrib.m_modelViewMat;
	mgGLSLProgram* glsl=mgGLSLProgram::getCurrentGLSLProgram();
	glsl->setUniform(mgGLSLProgram::modelViewProjMatrix, ProjModelView);
}

//Set Frustum of OpenGL.
void MGOpenGLView::setProjectionMat2GLSL(bool initialize){
	GLint vp[4];
	get_viewport(vp);
	glm::mat4 projMat;
	get_projection_matrix(vp,projMat);

	if(initialize)
		m_projMat=projMat;
	else
		m_projMat*=projMat;
	mgGLSLProgram* glsl=mgGLSLProgram::getCurrentGLSLProgram();
	glsl->setUniform(mgGLSLProgram::projMatrix,m_projMat);
}

//Set Model_View and Projection matrix of OpenGL.
void MGOpenGLView::setModelViewMat2GLSL(){
	get_model_matrix(m_viewAttrib.m_modelViewMat);
	mgGLSLProgram* glsl=mgGLSLProgram::getCurrentGLSLProgram();
	glsl->setUniform(mgGLSLProgram::modelViewMatrix,m_viewAttrib.m_modelViewMat);

	// Normal Matrix ModelViewMatrixとNormalMatrixはセットです。
	glm::mat3 normalMatrix = glm::mat3(m_viewAttrib.m_modelViewMat);
	glsl->setUniform(mgGLSLProgram::normalMatrix,normalMatrix);
}

// Anchor Point変換用の係数設定
void MGOpenGLView::setNdcMat2GLSL(){
	glm::ivec4 vp;
	get_viewport(vp);
	float widthHalf = float(vp[2]- vp[0])*0.5f;
	float heightHalf = float(vp[3]-vp[1])*0.5f;
	glm::vec3 scaleFactor( 1.0f/widthHalf, 1.0f/heightHalf, 1.0f);
	glm::mat4 ndcMtx = glm::scale(
		glm::translate(glm::mat4(),	glm::vec3(-widthHalf, -heightHalf, 0.0f)),
		scaleFactor );

	mgGLSLProgram::getCurrentGLSLProgram()->setUniform(mgGLSLProgram::ndcMarix, ndcMtx);

	glm::mat3 ndcScaleMtx = glm::mat3(ndcMtx);
	mgGLSLProgram::getCurrentGLSLProgram()->setUniform(mgGLSLProgram::ndcScaleMatrix,ndcScaleMtx );
}

void MGOpenGLView::setDpiFactor2GLSL(){
	// output 
	float func = m_dpi/72.0f;
	mgGLSLProgram::getCurrentGLSLProgram()->setUniform(mgGLSLProgram::dpiFactor,func );
}

//Set the parent MGOpenGLView.
void MGOpenGLView::set_parent_OpenGLView(MGOpenGLView* parent){
	m_parent_glView=parent;
}

//Convert the screen coordinate (sx, sy) to world coordinate (wx, wy) on the 
//view plane.
void MGOpenGLView::screen_to_world(
	int wh[2],	//width(wh[0]) and height(wh[1]) of the screen.
	double sx,double sy, double& wx, double& wy
)const{
	double sheight=double(wh[1]), swidth=double(wh[0]);
	double wsratio=view_volume_height()/sheight;
	wx=m_viewAttrib.m_cx+wsratio*(sx-swidth*.5);
	wy=m_viewAttrib.m_cy+wsratio*(sy-sheight*.5);
}

//Translate the current view by (dx, dy).
void MGOpenGLView::translate(double dx, double dy){
	m_viewAttrib.m_cx-=dx/m_viewAttrib.m_scale; m_viewAttrib.m_cy-=dy/m_viewAttrib.m_scale;
}

//Translate the current view by (dx, dy) without current scale.
void MGOpenGLView::translate_without_scale(double dx, double dy){
	m_viewAttrib.m_cx-=dx; m_viewAttrib.m_cy-=dy;
}

//Translate and scale the current view.
//(x0, y0) to (x1,y1) is the rectangle of screen coordinate whose origin is
//(left,bottom).
void MGOpenGLView::pan_zoom(int x0, int y0, int x1, int y1){
	makeRCCurrent();
	int dx=x1-x0, dy=y1-y0;
	if(dx==0 && dy==0) return;

	if(dx<0) dx*=-1;if(dy<0) dy*=-1;
	double sdy=double(dy);

	GLint vp[4]; get_viewport(vp);
	double sheight=double(vp[3]), swidth=double(vp[2]);
	if(dx){
		double sdx=double(dx);
		double aspect=sheight/swidth;
		if(sdy/sdx < aspect) sdy=sdx*aspect;
	}

	double sxm=(double(x0)+double(x1))*.5;
	double sym=(double(y0)+double(y1))*.5;
	screen_to_world(&vp[0]+2,sxm,sym,m_viewAttrib.m_cx,m_viewAttrib.m_cy);
	double wsratio=view_volume_height()/sheight;
	m_viewAttrib.m_scale=diameter()/(wsratio*sdy);
}

//Translate and scale the current view.
//box is world coordinate's box cube.
void MGOpenGLView::pan_zoom(const MGBox& box){
	glm::mat4 mmat; get_model_matrix(mmat);
	glm::ivec4 vp; get_viewport(vp);

	MGPosition wc=box.mid();
	MGPosition sc; project(wc,sc,&mmat,0,&vp);

	//Set the viewing center position.
	screen_to_world(&vp[0]+2,sc[0],sc[1],m_viewAttrib.m_cx,m_viewAttrib.m_cy);

	//Set the scaling factor.
	float* mp=&mmat[0][0];
	double p=mp[2]*wc[0]+mp[6]*wc[1]+mp[10]*wc[2]+mp[14];
	p*=-.9;//was -1.0. Maybe scaled to too large object.
	double len=view_volume_near()*box.len()/p;
	m_viewAttrib.m_scale=diameter()/len;
}

//Convert the windows screen coordinate (x,y) to MGCL's straight line.
//and get the intersection of the straight line and the construction plane.
//The origin of the screen coordinate is left, bottom. Not left, top.
void MGOpenGLView::unproject(
	int x, int y,	//screen coordinate whose origin is (left, bottom).
	MGStraight& sl,	//The straight line of (x,y) will be returnred.
	MGCSisect& is	//the intersectio of the sl and the construction plane
					//will be returned.
)const{
	unproject_to_sl_glv(x,y,sl);
	if(m_viewAttrib.m_cplane.valid())
		sl.relation(m_viewAttrib.m_cplane.plane(), is);
	else
		is=MGCSisect(sl.root_point(), 0.,MGPosition(0.,0.));
}

//Convert the windows screen coordinate (x,y) to MGCL's straight line.
//The origin of the screen coordinate is left, bottom. Not left, top.
void MGOpenGLView::unproject_to_sl_glv(int x, int y, MGStraight& sl)const{
	glm::ivec4 viewport;get_viewport(viewport);
	glm::mat4 modelMatrix, projMatrix;
	get_projection_matrix(&viewport[0],projMatrix);
	get_model_matrix(modelMatrix);

	glm::vec3 winPoint(x,y,0.);
	glm::vec3 P=glm::unProject(winPoint,modelMatrix,projMatrix,viewport);
	MGPosition origin(P[0],P[1],P[2]);
	winPoint[2]=100.;
	glm::vec3 Q=glm::unProject(winPoint,modelMatrix,projMatrix,viewport);
	MGPosition point(Q[0],Q[1],Q[2]);

	sl=MGStraight(MGSTRAIGHT_UNLIMIT, point-origin,origin);
	const MGConstructionPlane& pl=cplane();
	MGPosition xyz,uv2;
	xyz=pl.locate(sl,uv2);
	sl.update_root(xyz);
}

void MGOpenGLView::get_near_position(
	const MGCurve* crv, 
	const float center[2],///<screen coordinates whose origin is (left, bottom).
	double& t	//parameter value of the curve crv near to (sx,sy) will be returned.
){
	MGStraight sl;

	int sx=(int)center[0], sy=(int)center[1];
	unproject_to_sl_glv(sx,sy,sl);
	std::unique_ptr<MGCurve> crv2(crv->clone());//std::cout<<"1:"<<(*crv2)<<std::endl;
	const MGPosition rp=sl.root_point();
	*crv2-=rp;// std::cout<<"2:"<<(*crv2)<<std::endl;
	MGMatrix mat; mat.set_axis(sl.direction(),2);
	*crv2*=mat;// std::cout<<"2:"<<(*crv2)<<std::endl;
	t=crv2->closest2D(MGDefault::origin_2D());
	//std::cout<<"t="<<t<<","<<crv2->eval(t)<<std::endl;
}

///Enable when bEnabled=true, else disable grid snap.
///When bEnabled=true, cpnlane is enbaled.
void MGOpenGLView::enable_grid_snap(bool bEnabled){
	MGConstructionPlane& cpl=cplane();
	if(!cpl.valid())
		return;

	if(bEnabled){
		cpl.set_bind_to_grid_enable();
	}else{
		cpl.set_bind_to_grid_disable();
	}
}

// grid snap is enabled
bool MGOpenGLView::is_enabled_grid_snap()const{
	if(cplane().disabled()) return false;
	return cplane().is_bind_to_grid();
}

/// Each viewport uses its own context, so we need to make sure the correct
/// context is set whenever we make an OpenGL command.
void MGOpenGLView::makeRCCurrent()const{
	// Set current HGLRC and GlewContext.
	::wglMakeCurrent( m_hDC, m_hRC );
	m_currentGLView=const_cast<MGOpenGLView*>(this);
}