Example #1
0
    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;              //还能找到无效的面片
        }
    }
Example #2
0
    //////////////////////////////////////////////////////////////////////
    // 本类最核心的方法: 将本面片从凸多面体中删除!
    // 涉及到了很多相关的操作,包括:
    // 计算新的顶点、新顶点的添加、本面片的删除、及邻域面片的更新等操作
    //////////////////////////////////////////////////////////////////////
    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);                   //删除成功
    }