public void UpdateRemovedError() //更新该平面面片的删除误差! { mRemovedError = -1.0f; //每次更新时,首先初始化为一个负值! //计算所有邻域的可能交点,找出其中距离该平面最远的点,其距离即为该平面的删除误差 int npCount = mLstNeighbors.Count; if (npCount < 3) { return; //邻域面片数小于3,出现了异常! } // 防止出现有效交点但拓扑错误的情况 bool[] lstNPValid = new bool[npCount]; //记录每一个邻域面片是否有效,即是否经过一个有效交点! List <int> lstN = new List <int>(); //存储选出的三个相邻面片的id Vector3 v = Vector3.zero; bool valid = false; CombGenerator cg = new CombGenerator(3, npCount); if (!cg.Solvable) { return; } while (!cg.Over) { //产生一个组合 cg.GetNextComb(lstN); //求解 if (Solve3NPIntersection(lstN, ref v)) { valid = true; //如果有解,先代入到当前平面的方程 //注意:考察当前面片时,则应该是不在当前面片的内部! if (Inside(v)) { continue; //直接进入下一轮while循环 } //继续代入其他的邻域面片方程检验 for (int i = 0; i < npCount; i++) { if (i != lstN[0] && i != lstN[1] && i != lstN[2]) { //不能在这些面片的外部 if (LstNeighbors[i].NeighborPatch.Outside(v)) { valid = false; break; //在某个平面外部,跳出for循环 } } } if (valid) { //说明该点确实是一个有效的交点,可以计算误差 float error = OnPlane(v) ? 0.0f : Dist2Plane(v); //如果顶点满足OnPlane(),则置距离为0.0f if (error > mRemovedError) { mRemovedError = error; } //将产生该交点的三个面片置为有效 lstNPValid[lstN[0]] = true; lstNPValid[lstN[1]] = true; lstNPValid[lstN[2]] = true; } } else { //无解的情况暂不考虑 } } //是否所有的邻域面片都有效? bool allvalid = true; for (int i = 0; i < lstNPValid.Length; i++) { if (!lstNPValid[i]) { allvalid = false; break; } } if (allvalid) { mRemovedError = -1.0f; //还能找到无效的面片 } }
////////////////////////////////////////////////////////////////////// // 本类最核心的方法: 将本面片从凸多面体中删除! // 涉及到了很多相关的操作,包括: // 计算新的顶点、新顶点的添加、本面片的删除、及邻域面片的更新等操作 ////////////////////////////////////////////////////////////////////// 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); //删除成功 }