////////////////////////////////////////////////////////////////////// // 本类最核心的方法: 将本面片从凸多面体中删除! // 涉及到了很多相关的操作,包括: // 计算新的顶点、新顶点的添加、本面片的删除、及邻域面片的更新等操作 ////////////////////////////////////////////////////////////////////// public bool Removed() { int npCount = mLstNeighbors.Count; if (npCount < 3) { return(false); } //一些重要的数据结构 List <Vector3> lstVerteciesAdded = new List <Vector3>(); //删除后将产生的新的顶点集 List <byte> lstVDegree = new List <byte>(); //新产生顶点的度数,即邻接平面数。与上动态数组一一对应 //哪些邻域平面相交于相同的顶点 //对应于上面的要添加的顶点 //如对于添加的顶点v,其在VerteciesAdded中的id为2 //则PatchesIntersectV[2]对应了一个动态数组,其元素 //即为相交于点v的所有patch领域面片的id //=======注意:====== //AArray<AArray<int,int>,AArray<int,int>>在编译时会报错! //说明模板类的使用暂时不支持嵌套定义。因此,这里采用了指针! List <List <int> > PatchesIntersectV = new List <List <int> >(); //邻域面片包含了哪些新加入的顶点 List <List <int> > VerticesInPatch = new List <List <int> >(npCount); int i; for (i = 0; i < npCount; i++) { //初始化VerticesInPatch的每一个元素 VerticesInPatch.Add(new List <int>()); } ///////////////////////////////////////////////////////////////////// // 求解新交点部分! ///////////////////////////////////////////////////////////////////// List <int> lstN = new List <int>(); //存储选出的三个相邻面片的id CombGenerator cg = new CombGenerator(3, npCount); Vector3 v = Vector3.zero; bool valid; //主循环 while (!cg.Over) { //产生一个组合 cg.GetNextComb(lstN); if (Processed(lstN, PatchesIntersectV)) //已经处理过,直接跳过,进入下一轮循环! { continue; } if (Solve3NPIntersection(lstN, ref v)) //有唯一解 { valid = true; //如果有解,先代入到当前平面的方程 //注意:考察当前面片时,则应该是不在当前面片的内部! //在该平面上或其外部都可以 if (this.Inside(v)) { continue; //直接进入下一轮while循环 } List <int> lstPatchesPassV = new List <int>(); //定义一个过v的所有面片的动态数组 //依次代入其他的邻域面片方程检验 for (i = 0; i < npCount; i++) { if (i != lstN[0] && i != lstN[1] && i != lstN[2]) { //不能在这些面片的外部 if (mLstNeighbors[i].NeighborPatch.Outside(v)) { valid = false; break; //在某个平面外部,跳出for循环 } //在平面上的情况 if (mLstNeighbors[i].NeighborPatch.OnPlane(v)) { lstPatchesPassV.Add(i); //出现共面的情况! } } } if (valid) //有效的交点 { lstVerteciesAdded.Add(v); //添加到新有效顶点列表 //添加lstN[0,1,2]到相交于该顶点的邻域平面列表 lstPatchesPassV.Add(lstN[0]); lstPatchesPassV.Add(lstN[1]); lstPatchesPassV.Add(lstN[2]); //modified by wf, 04-10-09 //为保证前后计算的一致性而作的代码修改: int ExistPIV; if ((ExistPIV = HasPIntersectVExist(lstPatchesPassV, PatchesIntersectV)) == -1) { //不存在不一致的情况 //添加上述列表到PatchesIntersectV中 PatchesIntersectV.Add(lstPatchesPassV); //将顶点度数记录下来 lstVDegree.Add((byte)lstPatchesPassV.Count); //将该顶点在VerteciesAdded中的id添加到 //相交于该顶点的各个面片对应的VerticesInPatch中 int vid = lstVerteciesAdded.Count - 1; int npid; for (i = 0; i < lstPatchesPassV.Count; i++) { npid = lstPatchesPassV[i]; VerticesInPatch[npid].Add(vid); } } else { //出现了不一致的情况,此时按照最后最多面片相交的情况处理! lstVerteciesAdded.RemoveAt(lstVerteciesAdded.Count - 1); //删除该点 List <int> lstExistPIV = PatchesIntersectV[ExistPIV]; PatchesIntersectV[ExistPIV] = lstPatchesPassV; lstVDegree[ExistPIV] = (byte)lstPatchesPassV.Count; //将该顶点在VerteciesAdded中的id添加到 //相交于该顶点的各个面片对应的VerticesInPatch中 //int vid=VerteciesAdded.GetSize()-1; int npid; for (i = 0; i < lstPatchesPassV.Count; i++) { npid = lstPatchesPassV[i]; if (!InArray(npid, lstExistPIV)) //如果该npid没有出现过,则添加vid { VerticesInPatch[npid].Add(ExistPIV); } } lstExistPIV.Clear(); //释放内存 } } else { lstPatchesPassV.Clear(); //当前顶点无效,回收前面分配的内存 }; } //无解的情况暂不考虑 } //如果没有合法的新交点,则这里应该返回false了 //正常而言,应该不太回出现这种情况,因为在计算最小误差时 //就应该排除掉了无解的情况,因此还有一个最小误差求解和本方法结果一致性的问题 if (lstVerteciesAdded.Count == 0) { // error occured! mRemovedError = -1.0f; mConvexPolytope.ExceptionOccur = true; return(false); } ///////////////////////////////////////////////////////////////////// // 向Polytope中插入新交点部分! ///////////////////////////////////////////////////////////////////// //记录插入前Polytope已有的顶点数目,新交点插入后的全局id则依次为:g_vid,g_vid+1,g_vid+2... //因此可供以后更新面片的邻域顶点列表使用 int g_vid = mConvexPolytope.LstVertecies.Count; for (i = 0; i < lstVerteciesAdded.Count; i++) { VertexInfo vInfo = new VertexInfo(); vInfo.Degree = lstVDegree[i]; mConvexPolytope.AddV(lstVerteciesAdded[i], vInfo); } //更新之前应先将以前的邻域面片备份,这一步可以考虑放在Polytope调用Removed()函数 //前进行! ///////////////////////////////////////////////////////////////////// // 更新邻域面片的邻域列表(包括顶点和相邻面片) ///////////////////////////////////////////////////////////////////// Patch curNP; //当前的邻域面片 int g_vid1, g_vid2; //当前的邻域面片对应的顶点,及邻边上的另一个顶点(都是全局id) ///////////////////////////////////////////////////////////////////// // =======注意在两相邻面片中,边的方向正好相反===== // // 当前面片P // \ ---. / // 对应(Pnk) v1\________/ v2 对应邻域面片(Pn2) // / \ // / <----- \ // 邻域面片(Pn1) // ///////////////////////////////////////////////////////////////////// int next, pre; List <int> lstVSorted = new List <int>(); //按照顶点连接顺序连接好的数组 List <int> lstVertices = null; int ivid, lastvid; for (i = 0; i < npCount; i++) //外层循环,对每一个邻域面片分别考虑!循环体内是一个邻域面片的处理 { lstVSorted.Clear(); //清空上次循环中的顶点排序列表 next = (i + 1 < npCount) ? i + 1 : 0; //下一个neighbor的索引,由于是用数组表示环状结构,因此要作一个运算 pre = (i - 1 < 0) ? npCount - 1 : i - 1; //上一个neighbor的索引 curNP = mLstNeighbors[i].NeighborPatch; g_vid1 = mLstNeighbors[i].Vid; g_vid2 = mLstNeighbors[next].Vid; lstVertices = VerticesInPatch[i]; ///////////////////////////////////////////////////////////////////// // 开始顶点连接 // 注意下面的顶点连接算法似乎没有必然得出正确结果的保障 // 因此,这一段也应作为测试和调试的重点! ///////////////////////////////////////////////////////////////////// //先查找与vid2邻接的顶点 int j; for (j = 0; j < lstVertices.Count; j++) { ivid = lstVertices[j]; if (InArray(i, PatchesIntersectV[ivid]) && //vid1对应的邻域面片(Pn1)过vid InArray(next, PatchesIntersectV[ivid])) //vid2对应的邻域面片(Pn2)过vid { //说明vid与v2相邻接 lstVSorted.Add(ivid); //插入数组中 break; //找到了,跳出 } } //确保找到! if (lstVSorted.Count != 1) { // error occured! mRemovedError = -1.0f; mConvexPolytope.ExceptionOccur = true; return(false); } //然后,依次寻找并连接相邻的两个顶点! //lastvid标志最后一个已被连接的顶点 //定义llvid则标志倒数第二个被连接的顶点,引入改变量是为了避免重复连接! int llvid; int counter = 1; //引入counter防止死循环 while (counter < lstVertices.Count) { counter++; lastvid = lstVSorted[lstVSorted.Count - 1]; if (lstVSorted.Count < 2) { llvid = -1; //一个无效值,因为此时还没有倒数第二个 } else { llvid = lstVSorted[lstVSorted.Count - 2]; } for (j = 0; j < lstVertices.Count; j++) { ivid = lstVertices[j]; //这个地方应该增加判断:即不能重复连接! if ((ivid != lastvid) && (ivid != llvid) && IsVAdjacent(PatchesIntersectV[lastvid], PatchesIntersectV[ivid])) //相邻 { lstVSorted.Add(ivid); break; } } } if (counter != lstVSorted.Count) { // error occured! mRemovedError = -1.0f; mConvexPolytope.ExceptionOccur = true; return(false); } if (lstVSorted.Count != 1) { // error occured! } lastvid = lstVSorted[lstVSorted.Count - 1]; //确保最后一个顶点lastvid应该是和v1邻接的 if (!InArray(pre, PatchesIntersectV[lastvid]) || !InArray(i, PatchesIntersectV[lastvid])) { // error occured! mRemovedError = -1.0f; mConvexPolytope.ExceptionOccur = true; return(false); } ///////////////////////////////////////////////////////////////////// // 确定顶点连接关系后,则可以开始邻域面片的顶点插入操作了 ///////////////////////////////////////////////////////////////////// //判断v1和v2是否应该从当前邻域面片的Neighbor中删除! //判断方法:以v2为例,如果过v2的面片仅有P,Pn1,Pn2,则v2可以删除了,这可以简化为看其度数是否为3或>3 List <VPNeighbor> lstNeighbors = curNP.LstNeighbors; //寻找v2在当前邻域面片中的位置 for (j = 0; j < lstNeighbors.Count; j++) { int tmp = lstNeighbors[j].Vid; if (g_vid2 == tmp) { break; } } //循环结束,j保存了当前的v2位置 int v2pos = j; if (v2pos >= lstNeighbors.Count) //确保一定找到 { // error occured! mRemovedError = -1.0f; mConvexPolytope.ExceptionOccur = true; return(false); } if (g_vid1 != curNP.GetNextV(v2pos)) //确保一定找到 { // error occured! mRemovedError = -1.0f; mConvexPolytope.ExceptionOccur = true; return(false); } bool bv2Last = (v2pos == lstNeighbors.Count - 1); //v2是否是Neighbor中最后一个元素? //先处理v1,因为对其的操作比较简单!直接删除或保留即可! //判断v1是否应该从当前邻域面片的Neighbor中删除 VertexInfo v1Info = mConvexPolytope.LstVertexInfo[g_vid1]; if (v1Info.Degree == 3) { //删除该邻域,v1在当前patch中的索引为pCurNP.GetNext(j) lstNeighbors.RemoveAt(curNP.GetNext(v2pos)); if (bv2Last) //说明v1是第一个元素,因此,删除v1后,应将v2pos自减 { v2pos--; } } int InsertPos; //新邻域添加的位置 //处理v2是否删除或保留! VertexInfo v2Info = mConvexPolytope.LstVertexInfo[g_vid2]; if (v2Info.Degree == 3) { //则应删除v2 lstNeighbors.RemoveAt(v2pos); InsertPos = v2pos; //v2已被删除,直接从v2pos插入 } else { //不删除v2,但必须修改其pNeighborPatch lstNeighbors[v2pos].NeighborPatch = mLstNeighbors[next].NeighborPatch; //修改其对应NeighborPatch为Pn2! InsertPos = curNP.GetNext(v2pos); //插入点在v2pos后面第一个元素起 } //构造新要添加的邻域 List <VPNeighbor> lstNeighborsToAdd = new List <VPNeighbor>(); //新要添加的邻域 VPNeighbor vpn = new VPNeighbor(); for (j = 0; j < lstVSorted.Count; j++) { vpn.Vid = lstVSorted[j] + g_vid; //注意,这里要用全局id,因此应加上g_vid if (j == lstVSorted.Count - 1) { //最后一个邻域节点要特殊处理 //其邻域面片应等于本面片的上一个邻域面片 vpn.NeighborPatch = mLstNeighbors[pre].NeighborPatch; } else { //要用一个查找算法!找出对应于该点的邻域面片! for (int k = 0; k < npCount; k++) { if (k != i) //不是当前正在考察的面片 { List <int> lstVertecies = VerticesInPatch[k]; if (InArray(lstVSorted[j], lstVertecies) && InArray(lstVSorted[j + 1], lstVertecies)) { //由VSorted[j,j+1]构成的边出现在pVertecies中 //说明第k个邻域面片对应了顶点VSorted[j] vpn.NeighborPatch = mLstNeighbors[k].NeighborPatch; } } } } if (vpn.NeighborPatch == null) //确保一定找到 { // error occured! mRemovedError = -1.0f; mConvexPolytope.ExceptionOccur = true; return(false); } lstNeighborsToAdd.Add(vpn); } //插入新的邻域 lstNeighbors.InsertRange(InsertPos, lstNeighborsToAdd); //当前邻域面片pCurNP的新的邻域构造OK后,需要对其进行更新删除误差操作! curNP.UpdateRemovedError(); } // end of for-loop //循环遍历当前面片的每一个顶点,并将其度数自减1 int curvid; for (i = 0; i < npCount; i++) { curvid = mLstNeighbors[i].Vid; VertexInfo curVInfo = mConvexPolytope.LstVertexInfo[curvid]; curVInfo.Degree--; } return(true); //删除成功 }
public bool Init(GiftWrap gw) { if (gw.ExceptionOccur()) { return(false); } Reset(); //重置为初始状态 Centroid = gw.Centroid; //质心置为初始化gw的质心 //分情况考虑构造CConvexPolytope if (gw.HullType == GiftWrap.HULL_TYPE.HULL_3D) //3D Hull的情况 { int[] map = new int[gw.LstVertex.Count]; //构造一个映射表 int count = 0; //添加有效顶点&构造映射表 int i; for (i = 0; i < gw.LstVertex.Count; i++) { map[i] = -1; if (gw.LstExtremeVertex[i]) { //当前点有效,用其初始化v,并将其插入到顶点列表 Vector3 v = gw.LstVertex[i]; LstVertecies.Add(v); LstVertexInfo.Add(new VertexInfo()); map[i] = count++; } } //构造面片 //添加三角形面片 for (i = 0; i < gw.LstFaces.Count; i++) { Face f = gw.LstFaces[i]; if (!f.InPolygon) //不属于任何多边形,添加 { Vector3 v1 = gw.LstVertex[f.v1]; Vector3 v2 = gw.LstVertex[f.v2]; Vector3 v3 = gw.LstVertex[f.v3]; Patch pat = new Patch(this); pat.Set(v1, v2, v3); //几何信息 //依次向Neighbors中添加元素 VPNeighbor vpn = new VPNeighbor(); List <VPNeighbor> lstNeighbor = pat.LstNeighbors; vpn.Vid = map[f.v1]; //这里必须作一个映射 lstNeighbor.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = map[f.v2]; lstNeighbor.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = map[f.v3]; lstNeighbor.Add(vpn); //各顶点度数增1 LstVertexInfo[map[f.v1]].Degree++; LstVertexInfo[map[f.v2]].Degree++; LstVertexInfo[map[f.v3]].Degree++; //添加到链表 LstPatches.Add(pat); } } //添加多边形面片 for (i = 0; i < gw.LstPlanes.Count; i++) { List <int> planes = gw.LstPlanes[i]; //取前三个点构造平面几何信息 Vector3 v1 = gw.LstVertex[planes[0]]; Vector3 v2 = gw.LstVertex[planes[1]]; Vector3 v3 = gw.LstVertex[planes[2]]; Patch pat = new Patch(this); pat.Set(v1, v2, v3); //几何信息 //依次向Neighbors中添加元素 VPNeighbor vpn = new VPNeighbor(); List <VPNeighbor> lstNeighbors = pat.LstNeighbors; for (int j = 0; j < planes.Count; j++) { vpn = new VPNeighbor(); vpn.Vid = map[planes[j]]; //这里必须作一个映射 lstNeighbors.Add(vpn); LstVertexInfo[vpn.Vid].Degree++; //顶点度数增1 } //添加到链表 LstPatches.Add(pat); } } else { //说明是2D Hull的情况 List <int> lstCHVs = gw.GetCHVertecies(); if (lstCHVs == null) { return(false); } if (lstCHVs.Count < 3) //至少是一个三角形 { return(false); } //顶点信息 VertexInfo vInfo = new VertexInfo(); vInfo.Degree = 3; //直棱拄所有顶点的面度数均为3 HalfSpace planeOut = new HalfSpace(); HalfSpace planeIn = new HalfSpace(); //取前三个点构造平面几何信息 Vector3 v1 = gw.LstVertex[lstCHVs[0]]; Vector3 v2 = gw.LstVertex[lstCHVs[1]]; Vector3 v3 = gw.LstVertex[lstCHVs[2]]; //构造两个平面PlaneOut和PlaneIn,分别表示顶面和底面 planeOut.Set(v1, v2, v3); planeIn.Normal = -planeOut.Normal; planeIn.Dist = -planeOut.Dist; planeIn.Translate(HULL2D_HALF_THICKNESS); planeOut.Translate(HULL2D_HALF_THICKNESS); Vector3 vOutNormal = planeOut.Normal; Vector3 vInNormal = planeIn.Normal; //分别求出PlaneOut,PlaneIn上的两点 Vector3 vOut = v1 + HULL2D_HALF_THICKNESS * vOutNormal; Vector3 vIn = v1 + HULL2D_HALF_THICKNESS * vInNormal; //构造顶点及顶点信息 int i; for (i = 0; i < lstCHVs.Count; i++) { //同时添加底面和顶面的一个顶点 Vector3 vec1 = gw.LstVertex[lstCHVs[i]]; Vector3 vec2 = vec1; if (i < 3) { vec1 += HULL2D_HALF_THICKNESS * vOutNormal; vec2 += HULL2D_HALF_THICKNESS * vInNormal; } else { Vector3 vDiff = vec1 - vOut; vec1 -= Vector3.Dot(vDiff, vOutNormal) * vOutNormal; vDiff = vec2 - vIn; vec2 -= Vector3.Dot(vDiff, vInNormal) * vInNormal; } LstVertecies.Add(vec1); LstVertecies.Add(vec2); //相应的,添加两个顶点信息 LstVertexInfo.Add(vInfo); LstVertexInfo.Add(vInfo); } //开始构造平面面片 //向外的面 Patch pat = new Patch(this); pat.Normal = vOutNormal; //几何信息 pat.Dist = planeOut.Dist; //依次向Neighbors中添加元素 VPNeighbor vpn = new VPNeighbor(); List <VPNeighbor> lstNeighbors1 = pat.LstNeighbors; for (i = 0; i < lstCHVs.Count; i++) { vpn = new VPNeighbor(); vpn.Vid = 2 * i; lstNeighbors1.Add(vpn); } //添加到链表 LstPatches.Add(pat); //向内的面 pat = new Patch(this); pat.Normal = vInNormal; //几何信息 pat.Dist = planeIn.Dist; //依次向Neighbors中添加元素 List <VPNeighbor> lstNeighbors2 = pat.LstNeighbors; //顶面按照逆序添加 for (i = lstCHVs.Count - 1; i >= 0; i--) { vpn = new VPNeighbor(); vpn.Vid = 2 * i + 1; lstNeighbors2.Add(vpn); } //添加到链表 LstPatches.Add(pat); //开始添加各个侧面 for (i = 0; i < lstCHVs.Count; i++) { pat = new Patch(this); List <VPNeighbor> lstNeighbors = pat.LstNeighbors; //每个侧面都是一个矩形 if (i < lstCHVs.Count - 1) { v1 = LstVertecies[2 * i + 2]; v2 = LstVertecies[2 * i]; v3 = LstVertecies[2 * i + 1]; pat.Set(v1, v2, v3); vpn = new VPNeighbor(); vpn.Vid = 2 * i; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 1; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 3; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 2; lstNeighbors.Add(vpn); } else { //最后一个矩形的情况比较特殊 v1 = LstVertecies[0]; v2 = LstVertecies[2 * i]; v3 = LstVertecies[2 * i + 1]; pat.Set(v1, v2, v3); vpn = new VPNeighbor(); vpn.Vid = 2 * i; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 1; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 1; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 0; lstNeighbors.Add(vpn); } LstPatches.Add(pat); } } OriginPatchNum = LstPatches.Count; CurVNum = LstVertecies.Count; //初始化删除误差 if (mLstRemovedError == null) { mLstRemovedError = new List <float>(OriginPatchNum + 1); } mLstRemovedError.Clear(); for (int i = 0; i < OriginPatchNum + 1; i++) { mLstRemovedError.Add(-1.0f); //0,1,2,3都置为无效值 } ExceptionOccur = false; //计算每个patch的邻域patch ComputePatchNeighbors(); //HalfSpace::SetDistThresh(1e-3); //恢复到缺省的阈值 //寻找最小删除误差对应的面片 SearchLeastErrorPatch(); //开始简化,简化到头 ReduceAll(); return(true); }