#ifndef _QDTREE_ICOSA_H
#define _QDTREE_ICOSA_H
#include "dataStruct.h"
#include "progressBar_imp.h"

class QdTree_Icosa
{
public:
	QdTree_Icosa(){
		refradius = refRadius = defaultR;
		strcpy(referSystem,"NULL");
		strcpy(orientPara,"NULL");
		strcpy(pointFile,"NULL");
		strcpy(lineFile,"NULL");
		strcpy(polyFile,"NULL");
		strcpy(circleFile,"NULL");
		strcpy(outlineFile,"NULL");
		strcpy(holeFile,"NULL");
		strcpy(mshFile,"NULL");
		strcpy(sphFile,"NULL");
		strcpy(locFile,"NULL");
		strcpy(locOutFile,"NULL");
	}
	~QdTree_Icosa(){}

	void runtine(char*);
	int init_refer(char*);
	int set_orient(char*);
	void init_para(double,vertex); //初始化参数
	void create_branch(int,int,int,int,int,int,Qdtree_node**); //创建分枝
	void create_tree(int,int,int,Qdtree*);//创建树
	void delete_tree(Qdtree_node**);//清空整颗树
	void return_leaf(Qdtree_node**);//返回叶子
	void cut_outline(Qdtree_node**);//切割模型范围 为了保证后续处理中树形结构的完整性 我们只添加node的属性值来控制是否输出改节点
	void cut_holes(Qdtree_node**);
	void add_vertex(int,Qdtree_node**); //按照规则添加个额外顶点
	void close_surface(int,Qdtree_node**); //闭合模型表面
	void locating_triangle(vertex,_1iArray&,Qdtree_node**); //判断包含着球坐标位置的最小三角形
	//转到球坐标之后事情变得麻烦了一点点 策略 对于每一个三角平面建立一个新的坐标系 将待测试的点投影到新的坐标系然后判断 呵呵呵呵呵
	//int inTriangleDot_old(Qdtree_node*);//判断插入点是否在节点三角形内 直角坐标系默认使用x y坐标 球坐标默认使用坐标投影
	int inTriangleSpoint(vertex,Qdtree_node*); //判断一个单独点与节点三角形的关系
	int inTriangleDot(Qdtree_node*);//在球面上判断点和三角形关系的一种方法 直接使用矢量运算确定包含关系 更直接更简单
	//注意因为球面上的特殊关系 两个点之间的夹角不能大于等于180度 因为球面上总是沿着最短路径走 而且通常我们指的也是最短路径
	int inTriangleLine(Qdtree_node*);//判断插入线是否穿过节点三角形 使用的是球面下的方法 直接矢量计算
	int inTrianglePoly(Qdtree_node*);//判断多边形与三角形的关系
	int inTriangleCircle(Qdtree_node*);//判断圆与三角形的关系
	int outPolyOutline(Qdtree_node*);//判断多边形与三角形的关系 用于切割模型边界
	int inPolyOutline(Qdtree_node*);//判断多边形与三角形的关系 用于切割模型边界 挖洞
	int out_msh(char*,double,double);
	int out_sph(char*,double,double);
	int out_loc(char*);
	int get_extrapoint(char*); //读取额外的点
	int get_extraline(char*); //读取额外的线段
	int get_extrapoly(char*); //读取额外的多边形
	int get_extracircle(char*); //读取额外的圆
	int get_outline(char*); //读取模型范围文件
	int get_hole(char*); //读取模型范围文件
	int get_loc(char*); //读入一组位置 判断包含着一组位置的最小三角形
	//辅助函数
	void init_icosa(double,vertex); //初始化一个二十面体实例 需要给定一个默认半径值 二十面体顶点的经纬坐标 在init_para函数中调用
	void extract_vertex();//按return_*函数返回的三角形索引 提取顶点向量的子集 在outmsh函数中调用
	double mean_edgeDegree(Qdtree_node*,double);//计算节点三角形的平均边长相对于半径的角度 会使用点的x y z坐标
private:
	int tree_depth; //树深度
	int max_depth; //最大深度
	double refradius,refRadius;

	vertexArray vArray; //顶点向量与映射
	idMap mapId;
	idMap::iterator ivd;
	strMap mapStr;
	strMap::iterator ivm;
	outIdMap mapOutId;
	outIdMap::iterator ioim;

	Icosa ICOSA;
	Qdtree* FOREST[20]; //四叉树集合，每一个二十面体的三角面都确定为一个树根
	vertex orient; //二十面的顶点的朝向 由经纬度坐标给出 半径长度为defaultR 默认的二十面体顶点 第一个顶点 在地理北极点

	_1iArray out_iArray; //输出顶点向量索引
	triangleArray out_tArray; //输出三角形索引向量

	pointArray extrapArray; //额外顶点向量
	lineArray extralArray; //额外折线向量
	polygonArray extrapolyArray; //额外的多边形
	circleArray extracirArray; //额外的圆圈
	//模型范围多边形 我们使用一个多边形来规定需要保留的模型范围
	//如果outlineArray为空的话直接输出整个球面模型
	//而如果模型范围存在的话则删除位于模型范围之外的三角形节点
	//如果一个节点三角形的三个顶点均位于范围多边形之外 则删除之
	polygonArray outlineArray; //模型范围多边形 位于该多边形之外的三角形单元将被删去 从深到浅执行
	polygonArray holeArray; //挖洞多边形

	//输入的球坐标位置
	vertexArray sphLocations; //输入的球坐标位置
	_2iArray locTriangles; //输入球坐标位置所在的最小三角形的列表 一个位置可能被多个三角形包含 即在三角形的边 或 顶点上

	char referSystem[1024];
	char orientPara[1024];
	char pointFile[1024];
	char lineFile[1024];
	char polyFile[1024];
	char circleFile[1024];
	char outlineFile[1024];
	char holeFile[1024];
	char mshFile[1024];
	char sphFile[1024];
	char locFile[1024];
	char locOutFile[1024];
};

void QdTree_Icosa::runtine(char* paras)
{
	if (!strcmp(paras,"NULL"))
	{
		cout << "set reference ellipsoid for output (input NULL to use defaults 1e+5/1e+5). use 0/0 to disable the function" << endl << "==> ";
		cin >> referSystem;
		cout << "set basic quad-tree depth. this is the depth all quad-tree nodes can achieve" << endl << "==> ";
		cin >> tree_depth;
		cout << "set maximal quad-tree depth. this is the depth no quad-tree node can surpass" << endl << "==> ";
		cin >> max_depth;
		cout << "set oriention of the basic icosahedron (lon/lat,input NULL to use defaults which are 0/90)" << endl << "==> ";
		cin >> orientPara;
		cout << "outline polygons locaiton file (input NULL if no)" << endl << "==> ";
		cin >> outlineFile;
		cout << "hole polygons locaiton file (input NULL if no)" << endl << "==> ";
		cin >> holeFile;
		cout << "extral points locaiton file (input NULL if no)" << endl << "==> ";
		cin >> pointFile;
		cout << "extral lines locaiton file (input NULL if no)" << endl << "==> ";
		cin >> lineFile;
		cout << "extral polygons locaiton file (input NULL if no)" << endl << "==> ";
		cin >> polyFile;
		cout << "extral circles locaiton file (input NULL if no)" << endl << "==> ";
		cin >> circleFile;
		cout << "extral spherical coordinates file (input NULL if no)" << endl << "==> ";
		cin >> locFile;
		cout << "set output Gmsh (.msh) file name (input NULL if no)" << endl << "==> ";
		cin >> mshFile;
		cout << "set output xyz (lon lat radius) file name (input NULL if no)" << endl << "==> ";
		cin >> sphFile;
		cout << "set output locations (lon lat radius triangles) file name (input NULL if no)" << endl << "==> ";
		cin >> locOutFile;
	}
	else
	{
		string temp_str;
		ifstream infile;
		if (open_infile(infile,paras)) return;
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#') continue;
			else
			{
				if (1==sscanf(temp_str.c_str(),"basic-depth=%d",&tree_depth)) cout << "basic tree depth:\t" << tree_depth << endl;
				else if (1==sscanf(temp_str.c_str(),"refer-system=%s",referSystem)) cout << "reference system:\t" << referSystem << endl;
				else if (1==sscanf(temp_str.c_str(),"max-depth=%d",&max_depth)) cout << "maximal tree depth:\t" << max_depth << endl;
				else if (1==sscanf(temp_str.c_str(),"orientation=%s",orientPara)) cout << "icosahedron orientation:\t" << orientPara << endl;
				else if (1==sscanf(temp_str.c_str(),"extra-points=%s",pointFile)) cout << "extral points:\t" << pointFile << endl;
				else if (1==sscanf(temp_str.c_str(),"extra-lines=%s",lineFile)) cout << "extral lines:\t" << lineFile << endl;
				else if (1==sscanf(temp_str.c_str(),"extra-polys=%s",polyFile)) cout << "extral polys:\t" << polyFile << endl;
				else if (1==sscanf(temp_str.c_str(),"extra-circles=%s",circleFile)) cout << "extral circles:\t" << circleFile << endl;
				else if (1==sscanf(temp_str.c_str(),"outline-polys=%s",outlineFile)) cout << "outline polys:\t" << outlineFile << endl;
				else if (1==sscanf(temp_str.c_str(),"hole-polys=%s",holeFile)) cout << "hole polys:\t" << holeFile << endl;
				else if (1==sscanf(temp_str.c_str(),"sph-locations=%s",locFile)) cout << "spherical locations:\t" << locFile << endl;
				else if (1==sscanf(temp_str.c_str(),"msh-save=%s",mshFile)) cout << "Gmsh save:\t" << mshFile << endl;
				else if (1==sscanf(temp_str.c_str(),"sph-save=%s",sphFile)) cout << "Xyz save:\t" << sphFile << endl;
				else if (1==sscanf(temp_str.c_str(),"loc-save=%s",locOutFile)) cout << "Xyz save:\t" << locOutFile << endl;
				else
				{
					cout << "Unknown parameter:\t" << temp_str << endl;
					return;
				}
			}
		}
	}

	if(init_refer(referSystem)) return;

	if(!get_outline(outlineFile))
		cout << "reading " << outlineFile << " ... done" << endl;
	if(!get_hole(holeFile))
		cout << "reading " << holeFile << " ... done" << endl;
	if(!get_extrapoint(pointFile))
		cout << "reading " << pointFile << " ... done" << endl;
	if(!get_extraline(lineFile))
		cout << "reading " << lineFile << " ... done" << endl;
	if (!get_extrapoly(polyFile))
		cout << "reading " << polyFile << " ... done" << endl;
	if (!get_extracircle(circleFile))
		cout << "reading " << circleFile << " ... done" << endl;
	if (!get_loc(locFile))
		cout << "reading " << locFile << " ... done" << endl;

	for (int i = 0; i < 20; i++)
	{
		FOREST[i] = new Qdtree;
		FOREST[i]->root = new Qdtree_node;
		FOREST[i]->root->tri = new triangle;
	}

	if (set_orient(orientPara)) return;
	init_para(defaultR,orient);

	ProgressBar *bar = new ProgressBar(20,"create model");
	for (int i = 0; i < 20; i++)
	{
		bar->Progressed(i);
		create_tree(ICOSA.tri[i].ids[0],ICOSA.tri[i].ids[1],ICOSA.tri[i].ids[2],FOREST[i]);
	}

	//如果没有额外的点 线 多边形 则跳过新增节点和封闭表面的操作
	if (strcmp(pointFile,"NULL") || strcmp(lineFile,"NULL") || strcmp(polyFile,"NULL") || strcmp(circleFile,"NULL"))
	{
		ProgressBar *bar2 = new ProgressBar(max_depth-tree_depth+1,"refine model");
		int curr_depth = tree_depth;
		while(curr_depth <= max_depth)
		{
			bar2->Progressed(curr_depth-tree_depth);
			for (int i = 0; i < 20; i++)
			{
				add_vertex(curr_depth,&(FOREST[i]->root));
			}
			for (int i = 0; i < 20; i++)
			{
				close_surface(curr_depth,&(FOREST[i]->root));
			}
			curr_depth++;
		}
	}

	//执行模型范围的剪切 在极点处有bug
	if (strcmp(outlineFile,"NULL")) //如果outlineFile存在 则执行剪裁流程
	{
		ProgressBar *bar3 = new ProgressBar(20,"cut model");
		for (int i = 0; i < 20; i++)
		{
			bar3->Progressed(i);
			cut_outline(&(FOREST[i]->root));
		}
	}

	//执行挖洞 在极点处有bug
	if (strcmp(holeFile,"NULL")) //如果outlineFile存在 则执行剪裁流程
	{
		ProgressBar *bar4 = new ProgressBar(20,"cut holes");
		for (int i = 0; i < 20; i++)
		{
			bar4->Progressed(i);
			cut_holes(&(FOREST[i]->root));
		}
	}

	for (int i = 0; i < 20; i++)
	{
		return_leaf(&(FOREST[i]->root));
	}

	//在确定节点三角形的输出id之后 我们再来确定输入位置所在的最小三角形
	if (strcmp(locFile,"NULL"))
	{
		//初始化locTriangles
		locTriangles.resize(sphLocations.size());
		ProgressBar *bar5 = new ProgressBar(sphLocations.size(),"locating");
		for (int i = 0; i < sphLocations.size(); i++)
		{
			bar5->Progressed(i);
			for (int j = 0; j < 20; j++)
			{
				locating_triangle(sphLocations[i],locTriangles[i],&(FOREST[j]->root));
			}
		}
	}

	if (!out_msh(mshFile,refradius,refRadius))
		cout << "file saved: " << mshFile << endl;
	if (!out_sph(sphFile,refradius,refRadius))
		cout << "file saved: " << sphFile << endl;
	if (!out_loc(locOutFile))
		cout << "file saved: " << locOutFile << endl;

	for (int i = 0; i < 20; i++)
	{
		delete_tree(&(FOREST[i]->root));
		if (FOREST[i] != NULL) delete FOREST[i];
	}
}

int QdTree_Icosa::init_refer(char* para)
{
	if (!strcmp(para,"NULL")) return 0;

	if (2 != sscanf(para,"%lf/%lf",&refradius,&refRadius))
	{
		cout << "reference system initialzation fail " << para << endl;
		return 1;
	}
	return 0;
}

int QdTree_Icosa::set_orient(char* para)
{
	if (!strcmp(para,"NULL"))
	{
		orient.posis.lon = 0;
		orient.posis.lat = 90;
		orient.posis.rad = defaultR;
		orient.set(orient.posis);
		return 0;
	}
	else
	{
		if (2!=sscanf(para,"%lf/%lf",&orient.posis.lon,&orient.posis.lat))
		{
			cout << "oriention initialzation fail " << para << endl;
			return 1;
		}
		else return 0;
	}
}

void QdTree_Icosa::init_para(double R,vertex ori)
{
	stringstream sstemp;
	string vert_id,mid_id;
	//初始化二十面体实例
	init_icosa(R,ori);
	for (int i = 0; i < 12; i++)
	{
		vArray.push_back(ICOSA.vert[i]); //添加二十面体顶点到顶点向量
		mapId[ICOSA.vert[i].id] = ICOSA.vert[i]; //保存顶点id映射
		//插入以坐标位置的字符串为键值的映射
		sstemp.str("");
		sstemp.clear();
		sstemp<<setprecision(16)<<ICOSA.vert[i].posic.x;
		sstemp>>vert_id;
		sstemp.str("");
		sstemp.clear();
		sstemp<<setprecision(16)<<ICOSA.vert[i].posic.y;
		sstemp>>mid_id;
		vert_id = vert_id + " " + mid_id;
		sstemp.str("");
		sstemp.clear();
		sstemp<<setprecision(16)<<ICOSA.vert[i].posic.z;
		sstemp>>mid_id;
		vert_id = vert_id + " " + mid_id;
		mapStr[vert_id] = ICOSA.vert[i];
	}
}

void QdTree_Icosa::create_branch(int upper_id,int order_id,int depth,int t_ids0,int t_ids1,int t_ids2,Qdtree_node** node)
{
	stringstream sstemp;
	string vert_id,mid_id;
	vertex localVert[6];
	Qdtree_node* currNode;

	*node = new Qdtree_node; //初始化一个新的四叉树节点
	currNode = *node;
	currNode->tri->ids[0] = t_ids0;//将上一节点的三角形顶点索引赋值给currNode内的triangle.ids,因此每一层节点实际上都保存了其本身的三角形顶点索引
	currNode->tri->ids[1] = t_ids1;
	currNode->tri->ids[2] = t_ids2;
	currNode->id = upper_id*10+order_id;//写入四叉树节点编号
	currNode->depth = depth;//记录四叉树深度

	//额外生长条件 满足其一即可生长 在局部加密模型的过程中 不同物理组的赋值顺序前后顺序为圈 多边形 线 点
	if ((depth < tree_depth //基本生长条件 所有节点都能达到的深度
	 || inTriangleCircle(currNode)
	 || inTrianglePoly(currNode)
	 || inTriangleLine(currNode)
	 || inTriangleDot(currNode))
	 && depth < max_depth) //最大深度限制 所有节点不能超过的深度
	{
		ivd = mapId.find(t_ids0);//利用map_ID映射找到四叉树节点的前三个点，这三个节点是上一层四叉树产生的，必然存在
		localVert[0] = ivd->second;
		ivd = mapId.find(t_ids1);
		localVert[1] = ivd->second;
		ivd = mapId.find(t_ids2);
		localVert[2] = ivd->second;

		for(int i = 0; i < 3; i++)//循环产生三个新的顶点，位于节点三角形的三条边的中点，给每一个新产生的节点赋索引值与坐标，并保存到顶点链表中
		{
			localVert[i+3].posic = middle_cpoint(localVert[i%3].posic,localVert[(i+1)%3].posic);//计算新顶点坐标,这里需要注意，如果顶点已经存在则需要将顶点索引重置，不增加顶点计数
			sstemp.str("");
			sstemp.clear();
			sstemp<<setprecision(16)<<localVert[i+3].posic.x;
			sstemp>>vert_id;
			sstemp.str("");
			sstemp.clear();
			sstemp<<setprecision(16)<<localVert[i+3].posic.y;
			sstemp>>mid_id;
			vert_id = vert_id + " " + mid_id;
			sstemp.str("");
			sstemp.clear();
			sstemp<<setprecision(16)<<localVert[i+3].posic.z;
			sstemp>>mid_id;
			vert_id = vert_id + " " + mid_id;
			ivm = mapStr.find(vert_id);
			if(ivm != mapStr.end())//利用map_vert查到当前顶点是否存在,这里需要注意，如果顶点已经存在则只需要将顶点索引置为已存在顶点的索引，不增加顶点计数
			{
				localVert[i+3].id = ivm->second.id;
			}
			else//若为新的顶点则将其增加到两个映射和一个链表中
			{
				localVert[i+3].set(vArray.size());//新的顶点索引等于顶点集的数量
				localVert[i+3].set(localVert[i+3].posic);
				vArray.push_back(localVert[i+3]);//将新产生的顶点保存到顶点链表中
				mapId[localVert[i+3].id] = localVert[i+3];//将新产生的顶点保存到顶点索引映射中
				mapStr[vert_id] = localVert[i+3];//将新产生的顶点保存到顶点位置映射中
			}
		}

		create_branch(currNode->id,1,depth+1,localVert[0].id,localVert[3].id,localVert[5].id,&(currNode->children[0]));
		create_branch(currNode->id,2,depth+1,localVert[1].id,localVert[4].id,localVert[3].id,&(currNode->children[1]));
		create_branch(currNode->id,3,depth+1,localVert[2].id,localVert[5].id,localVert[4].id,&(currNode->children[2]));
		create_branch(currNode->id,4,depth+1,localVert[3].id,localVert[4].id,localVert[5].id,&(currNode->children[3]));
	}
	return;
}

void QdTree_Icosa::create_tree(int t_ids0,int t_ids1,int t_ids2,Qdtree* pTree)
{
	if (tree_depth == 0)
	{
		pTree->root->id = 0;
		pTree->root->depth = 0;
		pTree->root->tri->ids[0] = t_ids0;//将上一节点的三角形顶点索引赋值给currNode内的triangle.ids,因此每一层节点实际上都保存了其本身的三角形顶点索引
		pTree->root->tri->ids[1] = t_ids1;
		pTree->root->tri->ids[2] = t_ids2;
		for(int i=0;i<4;i++)
		{
			pTree->root->children[i] = NULL;
		}
	}
	else
	{
		create_branch(0,0,0,t_ids0,t_ids1,t_ids2,&(pTree->root));//以根节点开始创建四叉树
	}
}

void QdTree_Icosa::delete_tree(Qdtree_node** pTree)
{
	Qdtree_node* currNode = *pTree;
	if (currNode == NULL)
	{
		return;
	}
	else
	{
		for (int i = 0; i < 4; i++)
		{
			delete_tree(&(currNode->children[i]));
		}
		delete currNode->tri; //构造函数初始化 一定存在
		delete currNode; //上面已经判断证否 一定存在
		currNode = NULL;
		return;
	}
}

void QdTree_Icosa::return_leaf(Qdtree_node** pTree)
{
	triangle temp_tri;
	Qdtree_node* currNode = *pTree;
	if (currNode->children[0]==NULL && 
		currNode->children[1]==NULL && 
		currNode->children[2]==NULL && 
		currNode->children[3]==NULL &&
		currNode->outOK==true)
	{
		for (int i = 0; i < 3; i++)
		{
			temp_tri.ids[i] = currNode->tri->ids[i];
			temp_tri.physic = currNode->tri->physic;
		}
		currNode->outId = out_tArray.size(); //为outId赋值
		out_tArray.push_back(temp_tri);
		return;
	}
	else
	{
		for (int i = 0; i < 4; i++)
		{
			if (currNode->children[i]!=NULL)
				return_leaf(&(currNode->children[i]));
		}
		return;
	}
}

void QdTree_Icosa::cut_outline(Qdtree_node** pTree)
{
	//切割范围多边形之外的四叉树节点 从深到浅执行
	Qdtree_node* currNode = *pTree;
	//当前节点是叶子节点 进行判断
	if (currNode->children[0]==NULL && 
		currNode->children[1]==NULL && 
		currNode->children[2]==NULL && 
		currNode->children[3]==NULL)
	{
		if (outPolyOutline(currNode)) //如果节点三角形在范围多边形之外 删除节点三角形 同时初始化指针
		{
			currNode->outOK = false;
			return;
		}
		else return;
	}
	else
	{
		for (int i = 0; i < 4; i++)
		{
			//顺序访问当前节点的四个子节点 先处理子节点的情况 当然前提是节点存在
			if (currNode->children[i] != NULL)
			{
				cut_outline(&(currNode->children[i]));
			}
			else continue;
		}
	}
	return;
}

void QdTree_Icosa::cut_holes(Qdtree_node** pTree)
{
	//切割范围多边形之外的四叉树节点 从深到浅执行
	Qdtree_node* currNode = *pTree;
	//当前节点是叶子节点 进行判断
	if (currNode->children[0]==NULL && 
		currNode->children[1]==NULL && 
		currNode->children[2]==NULL && 
		currNode->children[3]==NULL)
	{
		if (inPolyOutline(currNode)) //如果节点三角形在范围多边形之外 删除节点三角形 同时初始化指针
		{
			currNode->outOK = false;
			return;
		}
		else return;
	}
	else
	{
		for (int i = 0; i < 4; i++)
		{
			//顺序访问当前节点的四个子节点 先处理子节点的情况 当然前提是节点存在
			if (currNode->children[i] != NULL)
			{
				cut_holes(&(currNode->children[i]));
			}
			else continue;
		}
	}
	return;
}

void QdTree_Icosa::add_vertex(int vist_depth,Qdtree_node** pTree)
{
	cpoint middle_p;
	cpoint temp_p,temp_p2;
	vertex temp_v;
	stringstream sstemp;
	string vert_id,mid_id;
	Qdtree_node* currNode = *pTree;
	//访问深度为vist_depth的节点 节点深度不足 深入
	if (currNode->depth < vist_depth)
	{
		//访问存在的子节点
		for (int i = 0; i < 4; i++)
		{
			if (currNode->children[i] != NULL)
				add_vertex(vist_depth,&(currNode->children[i]));
			else continue;
		}
	}
	/*来到了目标节点 检查在当前节点三角形每条边的1/4和3/4位置上是否有已知顶点
	如有则检查在相对边的相应位置是否有已知的顶点 若无则添加*/
	else if (currNode->depth == vist_depth)
	{
		for (int i = 0; i < 3; i++)
		{
			middle_p = middle_cpoint(vArray.at(currNode->tri->ids[i%3]).posic,vArray.at(currNode->tri->ids[(i+1)%3]).posic);
			//检查1/4 3/4位置
			for (int n = 0; n < 2; n++)
			{
				temp_p = middle_cpoint(vArray.at(currNode->tri->ids[(i+n)%3]).posic,middle_p);
				sstemp.str("");
				sstemp.clear();
				sstemp<<setprecision(16)<<temp_p.x;
				sstemp>>vert_id;
				sstemp.str("");
				sstemp.clear();
				sstemp<<setprecision(16)<<temp_p.y;
				sstemp>>mid_id;
				vert_id = vert_id + " " + mid_id;
				sstemp.str("");
				sstemp.clear();
				sstemp<<setprecision(16)<<temp_p.z;
				sstemp>>mid_id;
				vert_id = vert_id + " " + mid_id;
				ivm = mapStr.find(vert_id);
				if(ivm!=mapStr.end())//利用map_vert查到1/4 3/4位置点存在 检查对边中点是否存在
				{
					temp_p2 = middle_cpoint(vArray.at(currNode->tri->ids[i%3]).posic,vArray.at(currNode->tri->ids[(i+2)%3]).posic);
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p2.x;
					sstemp>>vert_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p2.y;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p2.z;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					ivm = mapStr.find(vert_id);
					if(ivm==mapStr.end())//利用map_vert查到对边中点不存在 添加一个新的顶点到顶点向量
					{
						temp_v.set(vArray.size());//新的顶点索引等于顶点集的数量
						temp_v.set(temp_p2);
						vArray.push_back(temp_v);//将新产生的顶点保存到顶点链表中
						mapId[temp_v.id] = temp_v;//将新产生的顶点保存到顶点索引映射中
						mapStr[vert_id] = temp_v;//将新产生的顶点保存到顶点位置映射中
					}

					temp_p2 = middle_cpoint(vArray.at(currNode->tri->ids[(i+1)%3]).posic,vArray.at(currNode->tri->ids[(i+2)%3]).posic);
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p2.x;
					sstemp>>vert_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p2.y;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p2.z;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					ivm = mapStr.find(vert_id);
					if(ivm==mapStr.end())//利用map_vert查到对边中点不存在 添加一个新的顶点到顶点向量
					{
						temp_v.set(vArray.size());//新的顶点索引等于顶点集的数量
						temp_v.set(temp_p2);
						vArray.push_back(temp_v);//将新产生的顶点保存到顶点链表中
						mapId[temp_v.id] = temp_v;//将新产生的顶点保存到顶点索引映射中
						mapStr[vert_id] = temp_v;//将新产生的顶点保存到顶点位置映射中
					}
					break;
				}
			}
		}
	}
}

void QdTree_Icosa::close_surface(int vist_depth,Qdtree_node** pTree)
{
	int pointNum;
	cpoint temp_p;
	stringstream sstemp;
	string vert_id,mid_id;
	vertex localVert[6];
	int newids[4][3] = {{0,3,5},{1,4,3},{2,5,4},{3,4,5}};
	Qdtree_node* currNode = *pTree;
	//访问深度为vist_depth的节点 节点深度不足 深入
	if (currNode->depth < vist_depth)
	{
		//访问存在的子节点
		for (int i = 0; i < 4; i++)
		{
			if (currNode->children[i] != NULL)
				close_surface(vist_depth,&(currNode->children[i]));
			else continue;
		}
	}
	/*来到了目标节点 首先检查在当前节点三角形第四个子节点是否存在 若存在则则前三个也存在 不用做操作*/
	else if (currNode->depth == vist_depth)
	{
		if (currNode->children[3] == NULL)
		{
			//循环确定有几个新顶点
			pointNum = 0;
			for (int j = 0; j < 3; j++)
			{
				temp_p = middle_cpoint(vArray.at(currNode->tri->ids[j%3]).posic,vArray.at(currNode->tri->ids[(j+1)%3]).posic);
				sstemp.str("");
				sstemp.clear();
				sstemp<<setprecision(16)<<temp_p.x;
				sstemp>>vert_id;
				sstemp.str("");
				sstemp.clear();
				sstemp<<setprecision(16)<<temp_p.y;
				sstemp>>mid_id;
				vert_id = vert_id + " " + mid_id;
				sstemp.str("");
				sstemp.clear();
				sstemp<<setprecision(16)<<temp_p.z;
				sstemp>>mid_id;
				vert_id = vert_id + " " + mid_id;
				ivm = mapStr.find(vert_id);
				if(ivm!=mapStr.end())//利用map_vert查到1/4 3/4位置点存在 检查对边中点是否存在
				{
					pointNum++;
				}
			}
			//按不同情况生成新的三角形
			if (pointNum == 1)
			{
				for (int j = 0; j < 3; j++)
				{
					temp_p = middle_cpoint(vArray.at(currNode->tri->ids[j%3]).posic,vArray.at(currNode->tri->ids[(j+1)%3]).posic);
					//计算点的位置ID
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p.x;
					sstemp>>vert_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p.y;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p.z;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					ivm = mapStr.find(vert_id);
					if(ivm!=mapStr.end())//利用map_vert查到当前顶点是否存在
					{
						//增加第一个三角形
						currNode->children[0] = new Qdtree_node;
						currNode->children[0]->id = 10*(currNode->id) + 1;
						currNode->children[0]->depth = currNode->depth + 1;
						currNode->children[0]->tri->ids[0] = currNode->tri->ids[j%3];
						currNode->children[0]->tri->ids[1] = ivm->second.id;
						currNode->children[0]->tri->ids[2] = currNode->tri->ids[(j+2)%3];
						//增加第二个三角形
						currNode->children[1] = new Qdtree_node;
						currNode->children[1]->id = 10*(currNode->id) + 2;
						currNode->children[1]->depth = currNode->depth + 1;
						currNode->children[1]->tri->ids[0] = currNode->tri->ids[(j+1)%3];
						currNode->children[1]->tri->ids[1] = currNode->tri->ids[(j+2)%3];
						currNode->children[1]->tri->ids[2] = ivm->second.id;
					}
				}
			}
			else if (pointNum == 2)
			{
				for (int i = 0; i < 3; i++)
				{
					temp_p = middle_cpoint(vArray.at(currNode->tri->ids[i%3]).posic,vArray.at(currNode->tri->ids[(i+1)%3]).posic);
					//计算点的位置ID
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p.x;
					sstemp>>vert_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p.y;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<temp_p.z;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					ivm = mapStr.find(vert_id);
					if(ivm==mapStr.end())//利用map_vert查到当前顶点是否存在
					{
						for(int k = 0; k < 2; k++)//循环产生三个新的顶点，位于节点三角形的三条边的中点，给每一个新产生的节点赋索引值与坐标，并保存到顶点链表中
						{
							localVert[k].posic = middle_cpoint(vArray.at(currNode->tri->ids[(i+k)%3]).posic,vArray.at(currNode->tri->ids[(i+2)%3]).posic);//计算新顶点坐标,这里需要注意，如果顶点已经存在则需要将顶点索引重置，不增加顶点计数
							sstemp.str("");
							sstemp.clear();
							sstemp<<setprecision(16)<<localVert[k].posic.x;
							sstemp>>vert_id;
							sstemp.str("");
							sstemp.clear();
							sstemp<<setprecision(16)<<localVert[k].posic.y;
							sstemp>>mid_id;
							vert_id = vert_id + " " + mid_id;
							sstemp.str("");
							sstemp.clear();
							sstemp<<setprecision(16)<<localVert[k].posic.z;
							sstemp>>mid_id;
							vert_id = vert_id + " " + mid_id;
							ivm = mapStr.find(vert_id);
							localVert[k].id = ivm->second.id;
						}
						currNode->children[0] = new Qdtree_node;
						currNode->children[0]->id = 10*(currNode->id) + 1;
						currNode->children[0]->depth = currNode->depth + 1;
						currNode->children[0]->tri->ids[0] = currNode->tri->ids[i%3];
						currNode->children[0]->tri->ids[1] = currNode->tri->ids[(i+1)%3];
						currNode->children[0]->tri->ids[2] = localVert[1].id;

						currNode->children[1] = new Qdtree_node;
						currNode->children[1]->id = 10*(currNode->id) + 2;
						currNode->children[1]->depth = currNode->depth + 1;
						currNode->children[1]->tri->ids[0] = currNode->tri->ids[i%3];
						currNode->children[1]->tri->ids[1] = localVert[1].id;
						currNode->children[1]->tri->ids[2] = localVert[0].id;

						currNode->children[2] = new Qdtree_node;
						currNode->children[2]->id = 10*(currNode->id) + 3;
						currNode->children[2]->depth = currNode->depth + 1;
						currNode->children[2]->tri->ids[0] = localVert[0].id;
						currNode->children[2]->tri->ids[1] = localVert[1].id;
						currNode->children[2]->tri->ids[2] = currNode->tri->ids[(i+2)%3];
					}
				}
			}
			else if (pointNum == 3)
			{
				for (int k = 0; k < 3; k++)
				{
					ivd = mapId.find(currNode->tri->ids[k]);//利用map_ID映射找到四叉树节点的前三个点，这三个节点是上一层四叉树产生的，必然存在
					localVert[k] = ivd->second;
				}
				for(int k = 0; k < 3; k++)//循环产生三个新的顶点，位于节点三角形的三条边的中点，给每一个新产生的节点赋索引值与坐标，并保存到顶点链表中
				{
					localVert[k+3].posic = middle_cpoint(localVert[k%3].posic,localVert[(k+1)%3].posic);//计算新顶点坐标,这里需要注意，如果顶点已经存在则需要将顶点索引重置，不增加顶点计数
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<localVert[k+3].posic.x;
					sstemp>>vert_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<localVert[k+3].posic.y;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					sstemp.str("");
					sstemp.clear();
					sstemp<<setprecision(16)<<localVert[k+3].posic.z;
					sstemp>>mid_id;
					vert_id = vert_id + " " + mid_id;
					ivm = mapStr.find(vert_id);
					localVert[k+3].id = ivm->second.id;
				}
				for (int i = 0; i < 4; i++)
				{
					currNode->children[i] = new Qdtree_node;
					currNode->children[i]->id = 10*(currNode->id) + 1 + i;
					currNode->children[i]->depth = currNode->depth + 1;
					currNode->children[i]->tri->ids[0] = localVert[newids[i][0]].id;
					currNode->children[i]->tri->ids[1] = localVert[newids[i][1]].id;
					currNode->children[i]->tri->ids[2] = localVert[newids[i][2]].id;
				}
			}
		}
	}
}

void QdTree_Icosa::locating_triangle(vertex sp,_1iArray& belongs,Qdtree_node** pTree)
{
	Qdtree_node* currNode = *pTree;
	//首先判断输入点是否在当前节点三角形内 如果不在则返回
	if (inTriangleSpoint(sp,currNode))
	{
		//如果节点三角形是叶子 则将其outId添加到belongs内 注意outOk应该也为真
		if (currNode->children[0]==NULL && 
		currNode->children[1]==NULL && 
		currNode->children[2]==NULL && 
		currNode->children[3]==NULL &&
		currNode->outOK==true)
		{
			belongs.push_back(currNode->outId);
			return;
		}
		else
		{
			for (int i = 0; i < 4; i++)
			{
				//顺序访问当前节点的四个子节点 先处理子节点的情况 当然前提是节点存在
				if (currNode->children[i] != NULL)
				{
					locating_triangle(sp,belongs,&(currNode->children[i]));
				}
				else continue;
			}
		}
	}
	else return;
}

int QdTree_Icosa::inTriangleSpoint(vertex sp,Qdtree_node* node)
{
	int count;
	cpoint tri_nor;
	cpoint cross_point;
	cpoint edge_nor,edge_nor2;
	triangle temp_tri;

	for (int j = 0; j < 3; j++)
	{
		temp_tri.ids[j] = node->tri->ids[j];
	}

	//计算三角面元外法线矢量
	tri_nor = cross(vArray.at(temp_tri.ids[1]).posic-vArray.at(temp_tri.ids[0]).posic , vArray.at(temp_tri.ids[2]).posic-vArray.at(temp_tri.ids[0]).posic);
	if (dot(tri_nor,sp.posic) > 0)
	{
		//如果sp等于三角形顶点中的一个返回真
		for (int j = 0; j < 3; j++)
		{
			if (cpoint_angle(vArray.at(temp_tri.ids[j]).posic,sp.posic)<ZERO)
				return 1;
		}

		//我们需要判断点是否刚好落在三角形的一条边上
		for (int j = 0; j < 3; j++)
		{
			edge_nor = cross(sp.posic,vArray.at(temp_tri.ids[j]).posic);
			edge_nor2 = cross(vArray.at(temp_tri.ids[(j+1)%3]).posic,sp.posic);
			if (cpoint_angle(edge_nor,edge_nor2)<ZERO)
				return 1;
		}

		count = 0;
		for (int j = 0; j < 3; j++)
		{
			cross_point = lineOnPlane(vArray.at(temp_tri.ids[j]).posic,tri_nor,sp.posic);
			//依次判断前后两条边与待检测点的外法线是否同向 注意排除从球体背面穿射的情况 全为真则返回真
			if (dot(tri_nor,cross(vArray.at(temp_tri.ids[(j+1)%3]).posic-vArray.at(temp_tri.ids[j]).posic , cross_point-vArray.at(temp_tri.ids[j]).posic)) >= 0)
				count++;
		}
		if (count == 3) return 1;
	}
	return 0;
}

int QdTree_Icosa::inTriangleDot(Qdtree_node* node)
{
	if (extrapArray.empty()) //没有插入的点位 直接返回否
	{
		return 0;
	}
	else
	{
		int tempDepth;
		double tempDeg;
		int count;
		cpoint tri_nor;
		cpoint cross_point;
		triangle temp_tri;

		for (int j = 0; j < 3; j++)
		{
			temp_tri.ids[j] = node->tri->ids[j];
		}

		tempDepth = node->depth;
		tempDeg = 0;
		for (int i = 0; i < 3; i++)
		{
			tempDeg += acos(dot(vArray.at(temp_tri.ids[i]).posic,vArray.at(temp_tri.ids[(i+1)%3]).posic)
				/(length_cpoint(vArray.at(temp_tri.ids[i]).posic)*length_cpoint(vArray.at(temp_tri.ids[(i+1)%3]).posic)));
		}
		tempDeg = tempDeg*60/pi;

		//计算三角面元外法线矢量
		tri_nor = cross(vArray.at(temp_tri.ids[1]).posic-vArray.at(temp_tri.ids[0]).posic , vArray.at(temp_tri.ids[2]).posic-vArray.at(temp_tri.ids[0]).posic);
		for (int i = 0; i < extrapArray.size(); i++)
		{
			if (dot(tri_nor,extrapArray.at(i).vert.posic) > 0)
			{
				count = 0;
				for (int j = 0; j < 3; j++)
				{
					cross_point = lineOnPlane(vArray.at(temp_tri.ids[j]).posic,tri_nor,extrapArray.at(i).vert.posic);
					//依次判断前后两条边与待检测点的外法线是否同向 注意排除从球体背面穿射的情况 全为真则返回真
					if (dot(tri_nor,
						cross(vArray.at(temp_tri.ids[(j+1)%3]).posic-vArray.at(temp_tri.ids[j%3]).posic , cross_point-vArray.at(temp_tri.ids[j%3]).posic)) > 0)
					{
						count++;
					}
				}
				//全为真 当前节点深度小于允许的最大值 当前节点三角形角度大于允许的最小值 都为真则返回真
				if (count == 3)
				{
					node->tri->physic = extrapArray.at(i).physic;
					if (extrapArray.at(i).maxDepth >= tempDepth && tempDeg >= extrapArray.at(i).minDeg) return 1;
				}
			}
		}
		return 0;
	}
}

int QdTree_Icosa::inTriangleLine(Qdtree_node* node)
{
	if (extralArray.empty())
	{
		return 0;
	}
	else
	{
		int tempDepth;
		double tempDeg;
		int count;
		cpoint tri_nor;
		cpoint lineface_nor, edgeface_nor;
		cpoint intersect[2];
		cpoint angle_mid;
		cpoint edge_mid;
		cpoint cross_point;
		triangle temp_tri;
		for (int i = 0; i < 3; i++)
		{
			temp_tri.ids[i] = node->tri->ids[i];
		}

		tempDepth = node->depth;
		tempDeg = 0;
		for (int i = 0; i < 3; i++)
		{
			tempDeg += acos(dot(vArray.at(temp_tri.ids[i]).posic,vArray.at(temp_tri.ids[(i+1)%3]).posic)
				/(length_cpoint(vArray.at(temp_tri.ids[i]).posic)*length_cpoint(vArray.at(temp_tri.ids[(i+1)%3]).posic)));
		}
		tempDeg = tempDeg*60/pi;

		//计算三角面元外法线矢量
		tri_nor = cross(vArray.at(temp_tri.ids[1]).posic-vArray.at(temp_tri.ids[0]).posic , vArray.at(temp_tri.ids[2]).posic-vArray.at(temp_tri.ids[0]).posic);
		//遍历所有线段
		//判断线段端点是否在三角形内 如果是 返回真
		for (int i = 0; i < extralArray.size(); i++)
		{
			for (int j = 0; j < extralArray.at(i).vert.size(); j++)
			{
				//判断线段端点是否在三角形内 如果是 返回真
				if (dot(tri_nor,extralArray.at(i).vert.at(j).posic) > 0)
				{
					count = 0;
					for (int k = 0; k < 3; k++)
					{
						cross_point = lineOnPlane(vArray.at(temp_tri.ids[k%3]).posic,tri_nor,extralArray.at(i).vert.at(j).posic);
						//依次判断前后两条边与待检测点的外法线是否同向 注意排除从球体背面穿射的情况 全为真则返回真
						if (dot(tri_nor,
							cross(vArray.at(temp_tri.ids[(k+1)%3]).posic-vArray.at(temp_tri.ids[k%3]).posic , cross_point-vArray.at(temp_tri.ids[k%3]).posic)) > 0)
						{
							count++;
						}
					}
					//全部在左侧 返回真
					if (count == 3)
					{
						node->tri->physic = extralArray.at(i).physic;
						if (extralArray.at(i).maxDepth >= tempDepth && tempDeg >= extralArray.at(i).minDeg) return 1;
					}
				}
			}
		}
		//无端点在三角形内 判断球面大圆弧是否与三角形相交 三角形内任意一条边与大圆弧相交则两者相交
		//先计算线段球心扇面的法线矢量 因为点序不确定所以具体方向不确定 不过没关系 哈哈
		for (int i = 0; i < extralArray.size(); i++)
		{
			for (int j = 0; j < extralArray.at(i).vert.size()-1; j++)
			{
				lineface_nor = cross(extralArray.at(i).vert.at(j).posic,extralArray.at(i).vert.at(j+1).posic);
				angle_mid = middle_cpoint(extralArray.at(i).vert.at(j).posic,extralArray.at(i).vert.at(j+1).posic);
				for (int n = 0; n < 3; n++)
				{
					edgeface_nor = cross(vArray.at(temp_tri.ids[n%3]).posic,vArray.at(temp_tri.ids[(n+1)%3]).posic);
					edge_mid = middle_cpoint(vArray.at(temp_tri.ids[n%3]).posic,vArray.at(temp_tri.ids[(n+1)%3]).posic);
					//排除两个扇面在同一个平面的情况
					if (fabs(dot(lineface_nor,edgeface_nor))/(length_cpoint(lineface_nor)*length_cpoint(edgeface_nor)) != 1.0)
					{
						//两个扇面可能的交点矢量垂直于两个扇面的外法线矢量 正反两个矢量
						intersect[0] = cross(lineface_nor,edgeface_nor);
						intersect[1] = cross(edgeface_nor,lineface_nor);
						for (int k = 0; k < 2; k++)
						{
							//交点矢量在两个线段端点矢量之间 注意端点先后顺序决定了大圆弧在球面上的范围 注意这里同样有从背面穿透的可能 因为我们不确定intersect中哪一个是我们想要的
							//注意计算叉乘的时候 我们总是会走一个角度小于180的方向
							//排除与angle_mid相反的半球上所有的三角形
							if (dot(angle_mid,tri_nor) > 0
							 && dot(cross(intersect[k],vArray.at(temp_tri.ids[n%3]).posic),cross(intersect[k],vArray.at(temp_tri.ids[(n+1)%3]).posic)) < 0
							 && dot(cross(intersect[k],extralArray.at(i).vert.at(j).posic),cross(intersect[k],extralArray.at(i).vert.at(j+1).posic)) < 0
							 && dot(intersect[k],angle_mid) > 0
							 && dot(intersect[k],edge_mid) > 0)
							{
								node->tri->physic = extralArray.at(i).physic;
								if (extralArray.at(i).maxDepth >= tempDepth && tempDeg >= extralArray.at(i).minDeg) return 1;
							}
						}
					}
				}
			}
		}
		return 0;
	}
}

int QdTree_Icosa::inTrianglePoly(Qdtree_node* node)
{
	if (extrapolyArray.empty())
	{
		return 0;
	}
	else
	{
		int tempDepth;
		double tempDeg;
		int count;
		int pnum;
		cpoint tri_nor;
		cpoint lineface_nor, edgeface_nor;
		cpoint intersect[2];
		cpoint angle_mid;
		cpoint polygon_mid;
		cpoint cross_point;
		triangle temp_tri;
		for (int j = 0; j < 3; j++)
		{
			temp_tri.ids[j] = node->tri->ids[j];
		}

		tempDepth = node->depth;
		tempDeg = 0;
		for (int i = 0; i < 3; i++)
		{
			tempDeg += acos(dot(vArray.at(temp_tri.ids[i]).posic,vArray.at(temp_tri.ids[(i+1)%3]).posic)
				/(length_cpoint(vArray.at(temp_tri.ids[i]).posic)*length_cpoint(vArray.at(temp_tri.ids[(i+1)%3]).posic)));
		}
		tempDeg = tempDeg*60/pi;

		//计算三角面元外法线矢量
		tri_nor = cross(vArray.at(temp_tri.ids[1]).posic-vArray.at(temp_tri.ids[0]).posic , vArray.at(temp_tri.ids[2]).posic-vArray.at(temp_tri.ids[0]).posic);
		//首先判断多边形的顶点是否在当前节点三角形内 或者多边形的边是否与当前节点三角形相交 这些条件可以判断多边形边上的三角形
		for (int i = 0; i < extrapolyArray.size(); i++)
		{
			pnum = extrapolyArray.at(i).vert.size();
			for (int j = 0; j < extrapolyArray.at(i).vert.size(); j++)
			{
				if (dot(tri_nor,extrapolyArray.at(i).vert.at(j).posic) > 0)
				{
					//多边形节点在当前节点三角形内
					count = 0;
					for (int k = 0; k < 3; k++)
					{
						cross_point = lineOnPlane(vArray.at(temp_tri.ids[k%3]).posic,tri_nor,extrapolyArray.at(i).vert.at(j).posic);
						//依次判断前后两条边与待检测点的外法线是否同向 注意排除从球体背面穿射的情况 全为真则返回真
						if (dot(tri_nor,
							cross(vArray.at(temp_tri.ids[(k+1)%3]).posic-vArray.at(temp_tri.ids[k%3]).posic , cross_point-vArray.at(temp_tri.ids[k%3]).posic)) > 0)
						{
							count++;
						}
					}
					//全部在左侧 返回真
					if (count == 3)
					{
						node->tri->physic = extrapolyArray.at(i).physic;
						if (extrapolyArray.at(i).maxDepth >= tempDepth && tempDeg >= extrapolyArray.at(i).minDeg) return 1;
					}
				}
				//多边形边与当前节点三角形相交
				lineface_nor = cross(extrapolyArray.at(i).vert.at(j%pnum).posic,extrapolyArray.at(i).vert.at((j+1)%pnum).posic);
				angle_mid = middle_cpoint(extrapolyArray.at(i).vert.at(j%pnum).posic,extrapolyArray.at(i).vert.at((j+1)%pnum).posic);
				for (int n = 0; n < 3; n++)
				{
					edgeface_nor = cross(vArray.at(temp_tri.ids[n%3]).posic,vArray.at(temp_tri.ids[(n+1)%3]).posic);
					//排除两个扇面在同一个平面的情况
					if (fabs(dot(lineface_nor,edgeface_nor))/(length_cpoint(lineface_nor)*length_cpoint(edgeface_nor)) != 1.0)
					{
						//两个扇面可能的交点矢量垂直于两个扇面的外法线矢量 正反两个矢量
						intersect[0] = cross(lineface_nor,edgeface_nor);
						intersect[1] = cross(edgeface_nor,lineface_nor);
						for (int k = 0; k < 2; k++)
						{
							//交点矢量在两个线段端点矢量之间 注意端点先后顺序决定了大圆弧在球面上的范围 注意这里同样有从背面穿透的可能 因为我们不确定intersect中哪一个是我们想要的
							//注意计算叉乘的时候 我们总是会走一个角度小于180的方向
							//排除与angle_mid相反的半球上所有的三角形
							if (dot(cross(intersect[k],vArray.at(temp_tri.ids[n%3]).posic),cross(intersect[k],vArray.at(temp_tri.ids[(n+1)%3]).posic)) < 0
							 && dot(cross(intersect[k],extrapolyArray.at(i).vert.at(j%pnum).posic),cross(intersect[k],extrapolyArray.at(i).vert.at((j+1)%pnum).posic)) < 0
							 && dot(angle_mid,tri_nor) > 0)
							{
								node->tri->physic = extrapolyArray.at(i).physic;
								if (extrapolyArray.at(i).maxDepth >= tempDepth && tempDeg >= extrapolyArray.at(i).minDeg) return 1;
							}
						}
					}
				}
			}
		}
		//多边形的顶点和边与当前节点三角形不相交或者包含 判断三角形是否在多边形内
		for (int i = 0; i < extrapolyArray.size(); i++)
		{
			pnum = extrapolyArray.at(i).vert.size();
			polygon_mid = middle_vertex(extrapolyArray.at(i).vert);
			//依次判断节点三角形的三条边与多边形边的交点个数
			for (int k = 0; k < 3; k++)
			{
				count = 0;
				//计算三角形边与球心的平面的法线矢量 只要任意一条边在多边形内 则三角形在多边形内
				edgeface_nor = cross(vArray.at(temp_tri.ids[(k)%3]).posic,vArray.at(temp_tri.ids[(k+1)%3]).posic);
				for (int j = 0; j < extrapolyArray.at(i).vert.size(); j++)
				{
					//多边形边与当前节点三角形相交
					lineface_nor = cross(extrapolyArray.at(i).vert.at(j%pnum).posic,extrapolyArray.at(i).vert.at((j+1)%pnum).posic);
					angle_mid = middle_cpoint(extrapolyArray.at(i).vert.at(j%pnum).posic,extrapolyArray.at(i).vert.at((j+1)%pnum).posic);
					//排除两个扇面在同一个平面的情况
					if (fabs(dot(lineface_nor,edgeface_nor))/(length_cpoint(lineface_nor)*length_cpoint(edgeface_nor)) != 1.0)
					{
						//两个扇面可能的交点矢量垂直于两个扇面的外法线矢量 正反两个矢量
						intersect[0] = cross(lineface_nor,edgeface_nor);
						intersect[1] = cross(edgeface_nor,lineface_nor);
						for (int n = 0; n < 2; n++)
						{
							/*注意 这里我们只判断交点是否在线段之间 或者一个点上 这里选择第一个点也可以选择第二点 但只能包含一个 不判断是不是在边之间
							注意我们只统计从当前三角形顶点向左或者向右旋转180度中遇到的交点*/
							//交点矢量在两个线段端点矢量之间 注意端点先后顺序决定了大圆弧在球面上的范围 注意这里同样有从背面穿透的可能 因为我们不确定intersect中哪一个是我们想要的
							//注意计算叉乘的时候 我们总是会走一个角度小于180的方向
							//排除与angle_mid相反的半球上所有的三角形
							if (dot(polygon_mid,tri_nor) > 0 //排除位于球背面的三角形
								&& (dot(cross(intersect[k],extrapolyArray.at(i).vert.at(j%pnum).posic),cross(intersect[k],extrapolyArray.at(i).vert.at((j+1)%pnum).posic)) < 0
								|| extrapolyArray.at(i).vert.at(j).posic == intersect[n]) //排除与多边形的边不相交的三角形边的延长线 这里包含了一个等于条件 即交点刚好在多边形的顶点上
								&& dot(angle_mid,intersect[n]) > 0 //排除位于球背面的多边形边与三角形边延长线的交点
								&& dot(edgeface_nor,cross(vArray.at(temp_tri.ids[k%3]).posic,intersect[n])) > 0) //只取三角形边其中一则的延长线
							{
								count++;
							}
						}
					}
				}
				//交点个数为奇数 边在多边形内
				if (pow(-1,count) < 0)
				{
					node->tri->physic = extrapolyArray.at(i).physic;
					if (extrapolyArray.at(i).maxDepth >= tempDepth && tempDeg >= extrapolyArray.at(i).minDeg) return 1;
				}
			}
		}
		//全不为真 返回假
		return 0;
	}
}

int QdTree_Icosa::inTriangleCircle(Qdtree_node* node)
{
	if (extracirArray.empty()) //没有插入的圆圈 直接返回否
	{
		return 0;
	}
	else
	{
		int tempDepth;
		double tempDeg;
		double cenDeg;
		triangle temp_tri;

		for (int j = 0; j < 3; j++)
		{
			temp_tri.ids[j] = node->tri->ids[j];
		}

		tempDepth = node->depth;
		tempDeg = 0;
		for (int i = 0; i < 3; i++)
		{
			tempDeg += acos(dot(vArray.at(temp_tri.ids[i]).posic,vArray.at(temp_tri.ids[(i+1)%3]).posic)
				/(length_cpoint(vArray.at(temp_tri.ids[i]).posic)*length_cpoint(vArray.at(temp_tri.ids[(i+1)%3]).posic)));
		}
		tempDeg = tempDeg*60/pi;

		//计算节点三角形顶点与圆圈中心点的球心夹角
		for (int i = 0; i < extracirArray.size(); i++)
		{
			for (int j = 0; j < 3; j++)
			{
				cenDeg = acos(dot(extracirArray.at(i).cen.posic,vArray.at(temp_tri.ids[j]).posic)
					/(length_cpoint(extracirArray.at(i).cen.posic)*length_cpoint(vArray.at(temp_tri.ids[j]).posic)))*180/pi;
				if (cenDeg <= extracirArray.at(i).radDeg)
				{
					node->tri->physic = extracirArray.at(i).physic;
					if (extracirArray.at(i).maxDepth >= tempDepth && tempDeg >= extracirArray.at(i).minDeg) return 1;
				}
			}
		}
		return 0;
	}
}

int QdTree_Icosa::outPolyOutline(Qdtree_node* node)
{
	if (outlineArray.empty()) //没有范围多边形 直接返回否
	{
		return 0;
	}
	else
	{
		int count;
		int pnum;
		cpoint tri_nor;
		cpoint lineface_nor, edgeface_nor;
		cpoint intersect[2];
		cpoint angle_mid;
		cpoint polygon_mid;
		cpoint cross_point;
		triangle temp_tri;
		for (int j = 0; j < 3; j++)
		{
			temp_tri.ids[j] = node->tri->ids[j];
		}

		//计算三角面元外法线矢量
		tri_nor = cross(vArray.at(temp_tri.ids[1]).posic-vArray.at(temp_tri.ids[0]).posic , vArray.at(temp_tri.ids[2]).posic-vArray.at(temp_tri.ids[0]).posic);
		//首先判断多边形的顶点是否在当前节点三角形内 或者多边形的边是否与当前节点三角形相交 这些条件可以判断多边形边上的三角形
		for (int i = 0; i < outlineArray.size(); i++)
		{
			pnum = outlineArray.at(i).vert.size();
			for (int j = 0; j < outlineArray.at(i).vert.size(); j++)
			{
				if (dot(tri_nor,outlineArray.at(i).vert.at(j).posic) > 0) //排除球形背面的点
				{
					//多边形节点在当前节点三角形内
					count = 0;
					for (int k = 0; k < 3; k++)
					{
						cross_point = lineOnPlane(vArray.at(temp_tri.ids[k%3]).posic,tri_nor,outlineArray.at(i).vert.at(j).posic);
						//依次判断前后两条边与待检测点的外法线是否同向 注意排除从球体背面穿射的情况 全为真则返回真
						if (dot(tri_nor,
							cross(vArray.at(temp_tri.ids[(k+1)%3]).posic-vArray.at(temp_tri.ids[k%3]).posic , cross_point-vArray.at(temp_tri.ids[k%3]).posic)) > 0)
						{
							count++;
						}
					}
					//全部在左侧 多边形顶点至少有一个在节点三角形内 即节点三角形至少有一个顶点在多边形内 返回假
					if (count == 3) return 0;
				}
				//多边形边与当前节点三角形相交
				lineface_nor = cross(outlineArray.at(i).vert.at(j%pnum).posic,outlineArray.at(i).vert.at((j+1)%pnum).posic);
				angle_mid = middle_cpoint(outlineArray.at(i).vert.at(j%pnum).posic,outlineArray.at(i).vert.at((j+1)%pnum).posic);
				for (int n = 0; n < 3; n++)
				{
					edgeface_nor = cross(vArray.at(temp_tri.ids[n%3]).posic,vArray.at(temp_tri.ids[(n+1)%3]).posic);
					//排除两个扇面在同一个平面的情况
					if (fabs(dot(lineface_nor,edgeface_nor))/(length_cpoint(lineface_nor)*length_cpoint(edgeface_nor)) != 1.0)
					{
						//两个扇面可能的交点矢量垂直于两个扇面的外法线矢量 正反两个矢量
						intersect[0] = cross(lineface_nor,edgeface_nor);
						intersect[1] = cross(edgeface_nor,lineface_nor);
						for (int k = 0; k < 2; k++)
						{
							//交点矢量在两个线段端点矢量之间 注意端点先后顺序决定了大圆弧在球面上的范围 注意这里同样有从背面穿透的可能 因为我们不确定intersect中哪一个是我们想要的
							//注意计算叉乘的时候 我们总是会走一个角度小于180的方向
							//排除与angle_mid相反的半球上所有的三角形
							if (dot(cross(intersect[k],vArray.at(temp_tri.ids[n%3]).posic),cross(intersect[k],vArray.at(temp_tri.ids[(n+1)%3]).posic)) < 0
							 && dot(cross(intersect[k],outlineArray.at(i).vert.at(j%pnum).posic),cross(intersect[k],outlineArray.at(i).vert.at((j+1)%pnum).posic)) < 0
							 && dot(angle_mid,tri_nor) > 0)
							{
								//多边形边与节点三角形相交 即节点三角形至少有一个顶点在多边形内 返回假
								return 0;
							}
						}
					}
				}
			}
		}
		//多边形的顶点和边与当前节点三角形不相交或者包含 判断三角形是否在多边形内
		for (int i = 0; i < outlineArray.size(); i++)
		{
			pnum = outlineArray.at(i).vert.size();
			polygon_mid = middle_vertex(outlineArray.at(i).vert);
			//依次判断节点三角形的三条边与多边形边的交点个数
			for (int k = 0; k < 3; k++)
			{
				count = 0;
				//计算三角形边与球心的平面的法线矢量 只要任意一条边在多边形内 则三角形在多边形内
				edgeface_nor = cross(vArray.at(temp_tri.ids[(k)%3]).posic,vArray.at(temp_tri.ids[(k+1)%3]).posic);
				for (int j = 0; j < outlineArray.at(i).vert.size(); j++)
				{
					//多边形边与当前节点三角形相交
					lineface_nor = cross(outlineArray.at(i).vert.at(j%pnum).posic,outlineArray.at(i).vert.at((j+1)%pnum).posic);
					angle_mid = middle_cpoint(outlineArray.at(i).vert.at(j%pnum).posic,outlineArray.at(i).vert.at((j+1)%pnum).posic);
					//排除两个扇面在同一个平面的情况
					if (fabs(dot(lineface_nor,edgeface_nor))/(length_cpoint(lineface_nor)*length_cpoint(edgeface_nor)) != 1.0)
					{
						//两个扇面可能的交点矢量垂直于两个扇面的外法线矢量 正反两个矢量
						intersect[0] = cross(lineface_nor,edgeface_nor);
						intersect[1] = cross(edgeface_nor,lineface_nor);
						for (int n = 0; n < 2; n++)
						{
							/*注意 这里我们只判断交点是否在线段之间 或者一个点上 这里选择第一个点也可以选择第二点 但只能包含一个 不判断是不是在边之间
							注意我们只统计从当前三角形顶点向左或者向右旋转180度中遇到的交点*/
							//交点矢量在两个线段端点矢量之间 注意端点先后顺序决定了大圆弧在球面上的范围 注意这里同样有从背面穿透的可能 因为我们不确定intersect中哪一个是我们想要的
							//注意计算叉乘的时候 我们总是会走一个角度小于180的方向
							//排除与angle_mid相反的半球上所有的三角形
							if (dot(polygon_mid,tri_nor) > 0 //排除位于球背面的三角形
								&& (dot(cross(intersect[k],outlineArray.at(i).vert.at(j%pnum).posic),cross(intersect[k],outlineArray.at(i).vert.at((j+1)%pnum).posic)) < 0
								|| outlineArray.at(i).vert.at(j).posic == intersect[n]) //排除与多边形的边不相交的三角形边的延长线 这里包含了一个等于条件 即交点刚好在多边形的顶点上
								&& dot(angle_mid,intersect[n]) > 0 //排除位于球背面的多边形边与三角形边延长线的交点
								&& dot(edgeface_nor,cross(vArray.at(temp_tri.ids[k%3]).posic,intersect[n])) > 0) //只取三角形边其中一则的延长线
							{
								count++;
							}
						}
					}
				}
				//交点个数为奇数 边在多边形内 返回假
				if (pow(-1,count) < 0) return 0;
			}
		}
		//全不为假 返回真
		return 1;
	}
}

int QdTree_Icosa::inPolyOutline(Qdtree_node* node)
{
	if (holeArray.empty()) //没有范围多边形 直接返回否
	{
		return 0;
	}
	else
	{
		int count;
		int pnum;
		cpoint tri_nor;
		cpoint lineface_nor, edgeface_nor;
		cpoint intersect[2];
		cpoint angle_mid;
		cpoint polygon_mid;
		cpoint cross_point;
		triangle temp_tri;
		for (int j = 0; j < 3; j++)
		{
			temp_tri.ids[j] = node->tri->ids[j];
		}

		//计算三角面元外法线矢量
		tri_nor = cross(vArray.at(temp_tri.ids[1]).posic-vArray.at(temp_tri.ids[0]).posic , vArray.at(temp_tri.ids[2]).posic-vArray.at(temp_tri.ids[0]).posic);
		//首先判断多边形的顶点是否在当前节点三角形内 或者多边形的边是否与当前节点三角形相交 这些条件可以判断多边形边上的三角形
		for (int i = 0; i < holeArray.size(); i++)
		{
			pnum = holeArray.at(i).vert.size();
			for (int j = 0; j < holeArray.at(i).vert.size(); j++)
			{
				if (dot(tri_nor,holeArray.at(i).vert.at(j).posic) > 0) //排除球形背面的点
				{
					//多边形节点在当前节点三角形内
					count = 0;
					for (int k = 0; k < 3; k++)
					{
						cross_point = lineOnPlane(vArray.at(temp_tri.ids[k%3]).posic,tri_nor,holeArray.at(i).vert.at(j).posic);
						//依次判断前后两条边与待检测点的外法线是否同向 注意排除从球体背面穿射的情况 全为真则返回真
						if (dot(tri_nor,
							cross(vArray.at(temp_tri.ids[(k+1)%3]).posic-vArray.at(temp_tri.ids[k%3]).posic , cross_point-vArray.at(temp_tri.ids[k%3]).posic)) > 0)
						{
							count++;
						}
					}
					//全部在左侧 多边形顶点至少有一个在节点三角形内 即节点三角形至少有一个顶点在多边形内 返回真
					if (count == 3) return 1;
				}
				//多边形边与当前节点三角形相交
				lineface_nor = cross(holeArray.at(i).vert.at(j%pnum).posic,holeArray.at(i).vert.at((j+1)%pnum).posic);
				angle_mid = middle_cpoint(holeArray.at(i).vert.at(j%pnum).posic,holeArray.at(i).vert.at((j+1)%pnum).posic);
				for (int n = 0; n < 3; n++)
				{
					edgeface_nor = cross(vArray.at(temp_tri.ids[n%3]).posic,vArray.at(temp_tri.ids[(n+1)%3]).posic);
					//排除两个扇面在同一个平面的情况
					if (fabs(dot(lineface_nor,edgeface_nor))/(length_cpoint(lineface_nor)*length_cpoint(edgeface_nor)) != 1.0)
					{
						//两个扇面可能的交点矢量垂直于两个扇面的外法线矢量 正反两个矢量
						intersect[0] = cross(lineface_nor,edgeface_nor);
						intersect[1] = cross(edgeface_nor,lineface_nor);
						for (int k = 0; k < 2; k++)
						{
							//交点矢量在两个线段端点矢量之间 注意端点先后顺序决定了大圆弧在球面上的范围 注意这里同样有从背面穿透的可能 因为我们不确定intersect中哪一个是我们想要的
							//注意计算叉乘的时候 我们总是会走一个角度小于180的方向
							//排除与angle_mid相反的半球上所有的三角形
							if (dot(cross(intersect[k],vArray.at(temp_tri.ids[n%3]).posic),cross(intersect[k],vArray.at(temp_tri.ids[(n+1)%3]).posic)) < 0
							 && dot(cross(intersect[k],holeArray.at(i).vert.at(j%pnum).posic),cross(intersect[k],holeArray.at(i).vert.at((j+1)%pnum).posic)) < 0
							 && dot(angle_mid,tri_nor) > 0)
							{
								//多边形边与节点三角形相交 即节点三角形至少有一个顶点在多边形内 返回真
								return 1;
							}
						}
					}
				}
			}
		}
		//多边形的顶点和边与当前节点三角形不相交或者包含 判断三角形是否在多边形内
		for (int i = 0; i < holeArray.size(); i++)
		{
			pnum = holeArray.at(i).vert.size();
			polygon_mid = middle_vertex(holeArray.at(i).vert);
			//依次判断节点三角形的三条边与多边形边的交点个数
			for (int k = 0; k < 3; k++)
			{
				count = 0;
				//计算三角形边与球心的平面的法线矢量 只要任意一条边在多边形内 则三角形在多边形内
				edgeface_nor = cross(vArray.at(temp_tri.ids[(k)%3]).posic,vArray.at(temp_tri.ids[(k+1)%3]).posic);
				for (int j = 0; j < holeArray.at(i).vert.size(); j++)
				{
					//多边形边与当前节点三角形相交
					lineface_nor = cross(holeArray.at(i).vert.at(j%pnum).posic,holeArray.at(i).vert.at((j+1)%pnum).posic);
					angle_mid = middle_cpoint(holeArray.at(i).vert.at(j%pnum).posic,holeArray.at(i).vert.at((j+1)%pnum).posic);
					//排除两个扇面在同一个平面的情况
					if (fabs(dot(lineface_nor,edgeface_nor))/(length_cpoint(lineface_nor)*length_cpoint(edgeface_nor)) != 1.0)
					{
						//两个扇面可能的交点矢量垂直于两个扇面的外法线矢量 正反两个矢量
						intersect[0] = cross(lineface_nor,edgeface_nor);
						intersect[1] = cross(edgeface_nor,lineface_nor);
						for (int n = 0; n < 2; n++)
						{
							/*注意 这里我们只判断交点是否在线段之间 或者一个点上 这里选择第一个点也可以选择第二点 但只能包含一个 不判断是不是在边之间
							注意我们只统计从当前三角形顶点向左或者向右旋转180度中遇到的交点*/
							//交点矢量在两个线段端点矢量之间 注意端点先后顺序决定了大圆弧在球面上的范围 注意这里同样有从背面穿透的可能 因为我们不确定intersect中哪一个是我们想要的
							//注意计算叉乘的时候 我们总是会走一个角度小于180的方向
							//排除与angle_mid相反的半球上所有的三角形
							if (dot(polygon_mid,tri_nor) > 0 //排除位于球背面的三角形
								&& (dot(cross(intersect[k],holeArray.at(i).vert.at(j%pnum).posic),cross(intersect[k],holeArray.at(i).vert.at((j+1)%pnum).posic)) < 0
								|| holeArray.at(i).vert.at(j).posic == intersect[n]) //排除与多边形的边不相交的三角形边的延长线 这里包含了一个等于条件 即交点刚好在多边形的顶点上
								&& dot(angle_mid,intersect[n]) > 0 //排除位于球背面的多边形边与三角形边延长线的交点
								&& dot(edgeface_nor,cross(vArray.at(temp_tri.ids[k%3]).posic,intersect[n])) > 0) //只取三角形边其中一则的延长线
							{
								count++;
							}
						}
					}
				}
				//交点个数为奇数 边在多边形内 返回真
				if (pow(-1,count) < 0) return 1;
			}
		}
		//全不为真 返回假
		return 0;
	}
}

int QdTree_Icosa::out_msh(char* filename,double refr,double refR)
{
	time_t now = time(0);
	char* dt = ctime(&now);
	vertex v;
	ofstream outfile;

	if (!strcmp(filename,"NULL"))
	{
		return -1;
	}

	if(open_outfile(outfile,filename)) return -1;

	extract_vertex();

	outfile << "$Comments" << endl << "this file is created by Qdtree_icosa.ex on " << dt;
	outfile << "basic quad-tree depth: " << tree_depth << endl;
	outfile << "maximal quad-tree depth: " << max_depth << endl;
	outfile << "outline: " << outlineFile << endl;
	outfile << "hole:" << holeFile << endl;
	outfile << "extral points: " << pointFile << endl;
	outfile << "extral lines: " << lineFile << endl;
	outfile << "extral polygons: " << polyFile << endl;
	outfile << "extral circles: " << circleFile << endl << "$EndComments" << endl;

	outfile<<"$MeshFormat"<<endl<<"2.2 0 8"<<endl<<"$EndMeshFormat"<<endl<<"$Nodes"<<endl<<out_iArray.size()<<endl;
	for (int i = 0; i < out_iArray.size(); i++)
	{
		if (refr == 0 && refR == 0)
		{
			outfile << i << " " << setprecision(16) << vArray.at(out_iArray.at(i)).posic.x << " " << vArray.at(out_iArray.at(i)).posic.y << " " << vArray.at(out_iArray.at(i)).posic.z << endl;
		}
		else if (refr != 0 && refR != 0)
		{
			v = vArray.at(out_iArray.at(i));
			v.posic = rescale_cpoint(vArray.at(out_iArray.at(i)).posic, REF_r(v.posis.lat,refr,refR));
			v.set(v.posic);
			outfile << i << " " << setprecision(16) << v.posic.x << " " << v.posic.y << " " << v.posic.z << endl;
		}
		else
		{
			cout << "error ==> reference system fail" << endl;
			return -1;
		}
	}
	outfile<<"$EndNodes"<<endl<<"$Elements"<<endl<<out_tArray.size()<<endl;
	for (int i = 0; i < out_tArray.size(); i++)
	{
		outfile<< i <<" 2 1 " << out_tArray.at(i).physic << " " << mapOutId[out_tArray.at(i).ids[0]] << " " << mapOutId[out_tArray.at(i).ids[1]] << " " << mapOutId[out_tArray.at(i).ids[2]] << endl;
	}
	outfile<<"$EndElements"<<endl;

	outfile.close();
	return 0;
}

int QdTree_Icosa::out_sph(char* filename,double refr,double refR)
{
	time_t now = time(0);
	char* dt = ctime(&now);
	vertex v;
	ofstream outfile;

	if (!strcmp(filename,"NULL"))
	{
		return -1;
	}

	if(open_outfile(outfile,filename)) return -1;

	extract_vertex();

	outfile << "# this file is created by Qdtree_icosa.ex on " << dt;
	outfile << "# basic quad-tree depth: " << tree_depth << endl;
	outfile << "# maximal quad-tree depth: " << max_depth << endl;
	outfile << "# outline: " << outlineFile << endl;
	outfile << "# hole: " << holeFile << endl;
	outfile << "# extral points: " << pointFile << endl;
	outfile << "# extral lines: " << lineFile << endl;
	outfile << "# extral polygons: " << polyFile << endl;
	outfile << "# extral circles: " << circleFile << endl;
	//outfile << "# lon(deg) lat(deg) rad(m)" << endl;
	outfile << "# node position lon(deg) lat(deg)" << endl;

	for (int i = 0; i < out_iArray.size(); i++)
	{
		if (refr == 0 && refR == 0) //not used
		{
			outfile << setprecision(16) << vArray.at(out_iArray.at(i)).posis.lon << " " << vArray.at(out_iArray.at(i)).posis.lat << " " << vArray.at(out_iArray.at(i)).posis.rad << endl;
		}
		else if (refr != 0 && refR != 0)
		{
			v = vArray.at(out_iArray.at(i));
			v.posic = rescale_cpoint(vArray.at(out_iArray.at(i)).posic, REF_r(v.posis.lat,refr,refR));
			v.set(v.posic);
			outfile << setprecision(16) << v.posis.lon << " " << v.posis.lat << " " << v.posis.rad << endl;
			//outfile << setprecision(16) << v.posis.lon << " " << v.posis.lat << endl;
		}
		else
		{
			cout << "error ==> reference system fail" << endl;
			return -1;
		}
	}

	outfile.close();
	return 0;
}

int QdTree_Icosa::out_loc(char* filename)
{
	time_t now = time(0);
	char* dt = ctime(&now);
	ofstream outfile;

	if (!strcmp(filename,"NULL"))
	{
		return -1;
	}

	if(open_outfile(outfile,filename)) return -1;

	outfile << "# this file is created by Qdtree_icosa.ex on " << dt;
	outfile << "# basic quad-tree depth: " << tree_depth << endl;
	outfile << "# maximal quad-tree depth: " << max_depth << endl;
	outfile << "# outline: " << outlineFile << endl;
	outfile << "# hole: " << holeFile << endl;
	outfile << "# extral points: " << pointFile << endl;
	outfile << "# extral lines: " << lineFile << endl;
	outfile << "# extral polygons: " << polyFile << endl;
	outfile << "# extral circles: " << circleFile << endl;
	outfile << "# node position lon(deg) lat(deg) Triangle-ids" << endl;

	for (int i = 0; i < sphLocations.size(); i++)
	{
		outfile << setprecision(16);
		outfile << sphLocations[i].posis.lon << " " << sphLocations[i].posis.lat << " ";
		if (locTriangles[i].empty())
		{
			outfile << "nan" << endl; //对于没有所属三角形的情况 输出-1
		}
		else
		{
			for (int j = 0; j < locTriangles[i].size(); j++)
			{
				outfile << locTriangles[i][j] << " ";
			}
			outfile << endl;
		}
	}
	outfile.close();
	return 0;
}

int QdTree_Icosa::get_extrapoint(char* filename)
{
	double node_eleva;
	stringstream temp_ss;
	string temp_str;
	point temp_v;
	ifstream infile;

	if (!strcmp(filename,"NULL"))
	{
		return 0;
	}

	if (open_infile(infile,filename)) return -1;
	else
	{
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#' || temp_str == "") continue;
			else
			{
				temp_ss.str("");
				temp_ss.clear();
				temp_ss << temp_str;
				if (temp_ss >> temp_v.maxDepth >> temp_v.minDeg >> temp_v.physic >> temp_v.vert.posis.lon >> temp_v.vert.posis.lat >> node_eleva)
				{
					if (temp_v.maxDepth < 0) temp_v.maxDepth = 1e+3; //这里直接给一个很大的深度值 节点深度一定小于这个值
					if (temp_v.minDeg < 0) temp_v.minDeg = -1; //这里直接给成-1
					temp_v.vert.posis.rad = defaultR + node_eleva;
					temp_v.vert.set(extrapArray.size());
					temp_v.vert.set(temp_v.vert.posis);
					extrapArray.push_back(temp_v);
				}
			}
		}
		infile.close();
	}
	return 0;
}

int QdTree_Icosa::get_extraline(char* filename) //点个数跟点列表
{
	int count;
	double node_eleva;
	stringstream temp_ss;
	string temp_str;
	vertex temp_v;
	line l;
	ifstream infile;

	if (!strcmp(filename,"NULL"))
	{
		return 0;
	}

	if (open_infile(infile,filename)) return -1;
	else
	{
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#' || temp_str == "") continue;
			else
			{
				if (!l.vert.empty()) l.vert.clear();
				temp_ss.str("");
				temp_ss.clear();
				temp_ss << temp_str;
				temp_ss >> count >> l.maxDepth >> l.minDeg >> l.physic;
				if (l.maxDepth < 0) l.maxDepth = 1e+3; //这里直接给一个很大的深度值 节点深度一定小于这个值
				if (l.minDeg < 0) l.minDeg = -1; //这里直接给成-1
				for (int i = 0; i < count; i++)
				{
					getline(infile,temp_str);
					temp_ss.str("");
					temp_ss.clear();
					temp_ss << temp_str;
					if (temp_ss >> temp_v.posis.lon >> temp_v.posis.lat >> node_eleva)
					{
						temp_v.posis.rad = defaultR + node_eleva;
						temp_v.set(l.vert.size());
						temp_v.set(temp_v.posis);
						l.vert.push_back(temp_v);
					}
				}
				l.id = extralArray.size();
				extralArray.push_back(l);
			}
		}
		infile.close();
	}
	return 0;
}

int QdTree_Icosa::get_extrapoly(char* filename)
{
	int count;
	double node_eleva;
	stringstream temp_ss;
	string temp_str;
	vertex temp_v;
	polygon p;
	ifstream infile;

	if (!strcmp(filename,"NULL"))
	{
		return 0;
	}

	if (open_infile(infile,filename)) return -1;
	else
	{
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#' || temp_str == "") continue;
			else
			{
				if (!p.vert.empty()) p.vert.clear();
				temp_ss.str("");
				temp_ss.clear();
				temp_ss << temp_str;
				temp_ss >> count >> p.maxDepth >> p.minDeg >> p.physic;
				if (p.maxDepth < 0) p.maxDepth = 100; //这里直接给一个很大的深度值 节点深度一定小于这个值
				if (p.minDeg < 0) p.minDeg = -1.0; //这里直接给成-1
				for (int i = 0; i < count; i++)
				{
					getline(infile,temp_str);
					temp_ss.str("");
					temp_ss.clear();
					temp_ss << temp_str;
					if (temp_ss >> temp_v.posis.lon >> temp_v.posis.lat >> node_eleva)
					{
						temp_v.posis.rad = defaultR + node_eleva;
						temp_v.set(p.vert.size());
						temp_v.set(temp_v.posis);
						p.vert.push_back(temp_v);
					}
				}
				p.id = extrapolyArray.size();
				extrapolyArray.push_back(p);
			}
		}
		infile.close();
	}
	return 0;
}

int QdTree_Icosa::get_extracircle(char* filename)
{
	double node_eleva;
	stringstream temp_ss;
	string temp_str;
	circle temp_v;
	ifstream infile;

	if (!strcmp(filename,"NULL"))
	{
		return 0;
	}

	if (open_infile(infile,filename)) return -1;
	else
	{
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#' || temp_str == "") continue;
			else
			{
				temp_ss.str("");
				temp_ss.clear();
				temp_ss << temp_str;
				if (temp_ss >> temp_v.maxDepth >> temp_v.minDeg >> temp_v.physic >> temp_v.radDeg >> temp_v.cen.posis.lon >> temp_v.cen.posis.lat >> node_eleva)
				{
					if (temp_v.maxDepth < 0) temp_v.maxDepth = 1e+3; //这里直接给一个很大的深度值 节点深度一定小于这个值
					if (temp_v.minDeg < 0) temp_v.minDeg = -1; //这里直接给成-1
					temp_v.cen.posis.rad = defaultR + node_eleva;
					temp_v.cen.set(extracirArray.size());
					temp_v.cen.set(temp_v.cen.posis);
					extracirArray.push_back(temp_v);
				}
			}
		}
		infile.close();
	}
	return 0;
}

int QdTree_Icosa::get_outline(char* filename)
{
	int count;
	double node_eleva;
	stringstream temp_ss;
	string temp_str;
	vertex temp_v;
	polygon p;
	ifstream infile;

	if (!strcmp(filename,"NULL"))
	{
		return 0;
	}

	if (open_infile(infile,filename)) return -1;
	else
	{
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#' || temp_str == "") continue;
			else
			{
				if (!p.vert.empty()) p.vert.clear();
				temp_ss.str("");
				temp_ss.clear();
				temp_ss << temp_str;
				//由于范围多边形对模型生成范围作出的是绝对性的约束 因此并不需要制定每个范围多边形的最大深度和最小尺寸等信息
				//这里暂时保留这两个参数 只是他们在后续的过程中并不起任何作用
				temp_ss >> count >> p.maxDepth >> p.minDeg >> p.physic; //后面三个没用
				if (p.maxDepth < 0) p.maxDepth = 1e+3; //这里直接给一个很大的深度值 节点深度一定小于这个值
				if (p.minDeg < 0) p.minDeg = -1; //这里直接给成-1
				for (int i = 0; i < count; i++)
				{
					getline(infile,temp_str);
					temp_ss.str("");
					temp_ss.clear();
					temp_ss << temp_str;
					if (temp_ss >> temp_v.posis.lon >> temp_v.posis.lat >> node_eleva)
					{
						temp_v.posis.rad = defaultR + node_eleva;
						temp_v.set(p.vert.size());
						temp_v.set(temp_v.posis);
						p.vert.push_back(temp_v);
					}
				}
				p.id = outlineArray.size();
				outlineArray.push_back(p);
			}
		}
		infile.close();
	}
	return 0;
}

int QdTree_Icosa::get_hole(char* filename)
{
	int count;
	double node_eleva;
	stringstream temp_ss;
	string temp_str;
	vertex temp_v;
	polygon p;
	ifstream infile;

	if (!strcmp(filename,"NULL"))
	{
		return 0;
	}

	if (open_infile(infile,filename)) return -1;
	else
	{
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#' || temp_str == "") continue;
			else
			{
				if (!p.vert.empty()) p.vert.clear();
				temp_ss.str("");
				temp_ss.clear();
				temp_ss << temp_str;
				//由于范围多边形对模型生成范围作出的是绝对性的约束 因此并不需要制定每个范围多边形的最大深度和最小尺寸等信息
				//这里暂时保留这两个参数 只是他们在后续的过程中并不起任何作用
				temp_ss >> count >> p.maxDepth >> p.minDeg >> p.physic; //后面三个没用
				if (p.maxDepth < 0) p.maxDepth = 1e+3; //这里直接给一个很大的深度值 节点深度一定小于这个值
				if (p.minDeg < 0) p.minDeg = -1; //这里直接给成-1
				for (int i = 0; i < count; i++)
				{
					getline(infile,temp_str);
					temp_ss.str("");
					temp_ss.clear();
					temp_ss << temp_str;
					if (temp_ss >> temp_v.posis.lon >> temp_v.posis.lat >> node_eleva)
					{
						temp_v.posis.rad = defaultR + node_eleva;
						temp_v.set(p.vert.size());
						temp_v.set(temp_v.posis);
						p.vert.push_back(temp_v);
					}
				}
				p.id = holeArray.size();
				holeArray.push_back(p);
			}
		}
		infile.close();
	}
	return 0;
}

int QdTree_Icosa::get_loc(char* filename)
{
	stringstream temp_ss;
	string temp_str;
	vertex temp_vert;
	ifstream infile;

	if (!strcmp(filename,"NULL"))
	{
		return 0;
	}

	if (open_infile(infile,filename)) return -1;
	else
	{
		while (getline(infile,temp_str))
		{
			if (*(temp_str.begin()) == '#' || temp_str == "") continue;
			else
			{
				temp_ss.str("");
				temp_ss.clear();
				temp_ss << temp_str;
				temp_ss >> temp_vert.posis.lon >> temp_vert.posis.lat;
				temp_vert.posis.rad = defaultR;
				temp_vert.set(temp_vert.posis);
				sphLocations.push_back(temp_vert);
			}
		}
		infile.close();
	}
	return 0;
}

/*辅助函数*/
void QdTree_Icosa::init_icosa(double R,vertex ori)
{
	double constL,constZ;
	//计算二十面的十二个顶点位置 首先将顶点位置固定在地理北极点
	ICOSA.vert[0].id = 0;
	ICOSA.vert[0].posic.x = 0.0;
	ICOSA.vert[0].posic.y = 0.0;
	ICOSA.vert[0].posic.z = R;
	ICOSA.vert[0].set(ICOSA.vert[0].posic);
	ICOSA.vert[11].id = 11;
	ICOSA.vert[11].posic.x = 0.0;
	ICOSA.vert[11].posic.y = 0.0;
	ICOSA.vert[11].posic.z = -R;
	ICOSA.vert[11].set(ICOSA.vert[11].posic);
	
	constZ = R*(golden_mean*golden_mean - 1.0)/(golden_mean*golden_mean + 1.0);
	constL = R*sqrt(1.0 - pow((golden_mean*golden_mean - 1.0)/(golden_mean*golden_mean + 1.0),2));

	for(int i=1;i<=5;i++)
	{
		ICOSA.vert[i].id = i;
		ICOSA.vert[i].posic.x = cos(72.0*(i-1)*pi/180.0)*constL;
		ICOSA.vert[i].posic.y = sin(72.0*(i-1)*pi/180.0)*constL;
		ICOSA.vert[i].posic.z = constZ;
		ICOSA.vert[i].set(ICOSA.vert[i].posic);

		ICOSA.vert[i+5].id = i+5;
		ICOSA.vert[i+5].posic.x = cos((72.0*(i-1)+36.0)*pi/180.0)*constL;
		ICOSA.vert[i+5].posic.y = sin((72.0*(i-1)+36.0)*pi/180.0)*constL;
		ICOSA.vert[i+5].posic.z = -constZ;
		ICOSA.vert[i+5].set(ICOSA.vert[i+5].posic);
	}

	//给定二十面的面顶点索引，各个三角面顶点索引按逆时针排序
	ICOSA.tri[0].ids[0] = 0; ICOSA.tri[0].ids[1] = 1; ICOSA.tri[0].ids[2] = 2;
	ICOSA.tri[1].ids[0] = 0; ICOSA.tri[1].ids[1] = 2; ICOSA.tri[1].ids[2] = 3;
	ICOSA.tri[2].ids[0] = 0; ICOSA.tri[2].ids[1] = 3; ICOSA.tri[2].ids[2] = 4;
	ICOSA.tri[3].ids[0] = 0; ICOSA.tri[3].ids[1] = 4; ICOSA.tri[3].ids[2] = 5;
	ICOSA.tri[4].ids[0] = 0; ICOSA.tri[4].ids[1] = 5; ICOSA.tri[4].ids[2] = 1;
	ICOSA.tri[5].ids[0] = 1; ICOSA.tri[5].ids[1] = 6; ICOSA.tri[5].ids[2] = 2;
	ICOSA.tri[6].ids[0] = 2; ICOSA.tri[6].ids[1] = 6; ICOSA.tri[6].ids[2] = 7;
	ICOSA.tri[7].ids[0] = 2; ICOSA.tri[7].ids[1] = 7; ICOSA.tri[7].ids[2] = 3;
	ICOSA.tri[8].ids[0] = 3; ICOSA.tri[8].ids[1] = 7; ICOSA.tri[8].ids[2] = 8;
	ICOSA.tri[9].ids[0] = 3; ICOSA.tri[9].ids[1] = 8; ICOSA.tri[9].ids[2] = 4;
	ICOSA.tri[10].ids[0] = 4; ICOSA.tri[10].ids[1] = 8; ICOSA.tri[10].ids[2] = 9;
	ICOSA.tri[11].ids[0] = 4; ICOSA.tri[11].ids[1] = 9; ICOSA.tri[11].ids[2] = 5;
	ICOSA.tri[12].ids[0] = 5; ICOSA.tri[12].ids[1] = 9; ICOSA.tri[12].ids[2] = 10;
	ICOSA.tri[13].ids[0] = 5; ICOSA.tri[13].ids[1] = 10; ICOSA.tri[13].ids[2] = 1;
	ICOSA.tri[14].ids[0] = 1; ICOSA.tri[14].ids[1] = 10; ICOSA.tri[14].ids[2] = 6;
	ICOSA.tri[15].ids[0] = 6; ICOSA.tri[15].ids[1] = 11; ICOSA.tri[15].ids[2] = 7;
	ICOSA.tri[16].ids[0] = 7; ICOSA.tri[16].ids[1] = 11; ICOSA.tri[16].ids[2] = 8;
	ICOSA.tri[17].ids[0] = 8; ICOSA.tri[17].ids[1] = 11; ICOSA.tri[17].ids[2] = 9;
	ICOSA.tri[18].ids[0] = 9; ICOSA.tri[18].ids[1] = 11; ICOSA.tri[18].ids[2] = 10;
	ICOSA.tri[19].ids[0] = 10; ICOSA.tri[19].ids[1] = 11; ICOSA.tri[19].ids[2] = 6;

	//旋转二十面顶点的位置
	vertex ref_vert = ICOSA.vert[0]; //注意我们选取的参考点为z轴正方向
	for (int i = 0; i < 12; i++)
	{
		ICOSA.vert[i] = rotate_vertex(ref_vert,ori,ICOSA.vert[i]);
	}
	return;
}

void QdTree_Icosa::extract_vertex()
{
	vector<int>::iterator pos;
	for (int i = 0; i < out_tArray.size(); i++)
	{
		for (int j = 0; j < 3; j++)
		{
			out_iArray.push_back(out_tArray.at(i).ids[j]);
		}
	}
	sort(out_iArray.begin(),out_iArray.end()); //对顶点序列由小到大排序
	pos = unique(out_iArray.begin(), out_iArray.end()); //获取重复序列开始的位置
	out_iArray.erase(pos, out_iArray.end()); //删除重复顶点序列
	//初始化mapOutId;
	for (int i = 0; i < out_iArray.size(); i++)
	{
		mapOutId[out_iArray.at(i)] = i;
	}
}

double QdTree_Icosa::mean_edgeDegree(Qdtree_node* node,double refR)
{
	double l = 0;
	triangle temp_tri;
	for (int j = 0; j < 3; j++)
	{
		temp_tri.ids[j] = node->tri->ids[j];
	}
	for (int i = 0; i < 3; i++)
	{
		l += distance_cpoint(vArray.at(temp_tri.ids[i%3]).posic,vArray.at(temp_tri.ids[(i+1)%3]).posic);
	}
	return l*60.0/(pi*refR);
}

//判断点是否在三角形内 使用投影方法的函数 如要使用需要取消dataStruct.h中的相应部分
/*
int QdTree_Icosa::inTriangleDot_old(Qdtree_node* node)
{
	int count;
	planePara currPlane;
	point2d currTri[3];
	point2d currP2d;
	cpoint dir_v012;
	double xmin,xmax,ymin,ymax; //用于判断点是否在外接矩形外
	if (extrapArray.empty()) //没有插入的点位 直接返回否
	{
		return 0;
	}
	else
	{
		triangle temp_tri;
		for (int j = 0; j < 3; j++)
		{
			temp_tri.ids[j] = node->tri->ids[j];
		}
		//计算一样三角形的外法线矢量
		dir_v012 = cross(vArray.at(temp_tri.ids[1]).posic - vArray.at(temp_tri.ids[0]).posic,vArray.at(temp_tri.ids[2]).posic - vArray.at(temp_tri.ids[0]).posic);
		//计算三角面平面
		currPlane = get_planePara(vArray.at(temp_tri.ids[0]).posic,vArray.at(temp_tri.ids[1]).posic,vArray.at(temp_tri.ids[2]).posic);
		//计算三角形三个顶点在新坐标系中的坐标
		currTri[0].x = 0.0; currTri[0].y = 0.0; //新坐标系的原点
		currTri[1].x = distance_cpoint(vArray.at(temp_tri.ids[0]).posic,vArray.at(temp_tri.ids[1]).posic); currTri[1].y = 0.0; //新坐标系的x轴
		currTri[2] = newXY(vArray.at(temp_tri.ids[0]),vArray.at(temp_tri.ids[1]),vArray.at(temp_tri.ids[2]),vArray.at(temp_tri.ids[2]));
		//确定外接矩形
		xmin = MAX_DBL; ymin = MAX_DBL;
		xmax = MIN_BDL; ymax = MIN_BDL;
		for (int j = 0; j < 3; j++)
		{
			if (currTri[j].x < xmin) xmin = currTri[j].x;
			if (currTri[j].x > xmax) xmax = currTri[j].x;
			if (currTri[j].y < ymin) ymin = currTri[j].y;
			if (currTri[j].y > ymax) ymax = currTri[j].y;
		}

		for (int i = 0; i < extrapArray.size(); i++)
		{
			//首先要计算三角形外法线矢量与待检测点矢量的方向是否基本一致 需要排除反向穿越的情况
			if (dot(dir_v012,extrapArray.at(i).vert.posic) > 0)
			{
				//计算带监测点在新坐标系中的位置
				currP2d = newXY(vArray.at(temp_tri.ids[0]),vArray.at(temp_tri.ids[1]),vArray.at(temp_tri.ids[2]),dotOnPlane(currPlane,extrapArray.at(i).vert));
				//满足点在外接矩形内
				if (currP2d.x > xmin && currP2d.x < xmax && currP2d.y > ymin && currP2d.y < ymax)
				{
					//向右做射线 计算与线段的交点 待检验点横坐标如果小于交点横坐标 则表示射线穿过
					//如果穿过次数为奇数 则在多边形内
					count = 0;
					for (int j = 0; j < 3; j++)
					{
						//注意现在使用的条件不包含位于多边形的边和顶点上的点
						//前两组条件表示待检测点y坐标在线段内 单侧包含
						//后一组条件表示交点横坐标大于待检测点横坐标
						//注意这里线段两个端点的y值相同时分母为零的情况在前叙的if条件中已经规避了
						if (((currP2d.y < currTri[j%3].y && currP2d.y >= currTri[(j+1)%3].y) ||
							(currP2d.y > currTri[j%3].y && currP2d.y <= currTri[(j+1)%3].y)) &&
							currP2d.x < ((currP2d.y - currTri[j%3].y)*(currTri[(j+1)%3].x - currTri[j%3].x)/(currTri[(j+1)%3].y - currTri[j%3].y) + currTri[j%3].x))
						{
							count++;
						}
					}
					//至少有一个点在当前多边形内 直接返回真
					if (pow(-1,count) < 0) return 1;
				}
			}
		}
		//没有点在当前多边形内 返回假
		return 0;
	}
}
*/
#endif