예제 #1
0
        //遍历轮廓,找邻接的Region
        private static void findRegionConnections(OpenHeightSpan startSpan,
                                                  int startDirection,
                                                  ref List <int> outConnections)
        {
            OpenHeightSpan span             = startSpan;
            int            dir              = startDirection;
            int            lastEdgeRegionID = NULL_REGION;

            OpenHeightSpan nSpan = span.getNeighbor(dir);

            if (nSpan != null)
            {
                lastEdgeRegionID = nSpan.regionID();
            }

            //连接的Region,至少存在一个NULL_REGION
            outConnections.Add(lastEdgeRegionID);

            int loopCount = 0;

            while (++loopCount < ushort.MaxValue)
            {
                nSpan = span.getNeighbor(dir);
                int currEdgeRegionID = NULL_REGION;   //默认nSpan是null的
                if (null == nSpan ||
                    nSpan.regionID() != span.regionID())
                {
                    if (nSpan != null)
                    {
                        currEdgeRegionID = nSpan.regionID();
                    }
                    if (currEdgeRegionID != lastEdgeRegionID)
                    {
                        outConnections.Add(currEdgeRegionID);
                        lastEdgeRegionID = currEdgeRegionID;
                    }

                    //顺时针转向下一个
                    dir = NMGenUtility.ClockwiseRotateDir(dir);
                }
                else
                {
                    //这个分支代表 Region是相同的
                    span = nSpan;
                    //逆时针转一下
                    dir = NMGenUtility.CClockwiseRotateDir(dir);
                }

                if (startSpan == span &&
                    startDirection == dir)
                {
                    break;
                }
            } //while

            //TODO 为啥会存在首尾相同呢?因为退出条件是原来的那个点么
            int connectionsCnt = outConnections.Count;

            if (connectionsCnt > 1 &&
                outConnections[0] == outConnections[connectionsCnt - 1])
            {
                outConnections.RemoveAt(connectionsCnt - 1);
            }
        }
예제 #2
0
        /// <summary>
        /// 为了防止 Null Region 内含在有效Region内,这阻碍了凸包的生成
        /// </summary>
        /// <param name="startSpan"></param>
        /// <param name="startDirection"></param>
        /// <returns></returns>
        private bool processNullRegion(OpenHeightSpan startSpan, int startDirection)
        {
            /*
             *  这段直接翻译源码的:
             *  这个算法遍历这个轮廓。正如它所做的,这个算法检测并且修复一些危险的
             *  Span Configurations。
             *
             *  遍历轮廓:一个很好的可视化方向就是,想像一个机器人面向一堵墙,并坐
             *  在地板上。它会采取以下措施来绕过这堵墙:
             *  1. 如果有一堵墙位于它的前面,顺时针转90度,直到他前面不再是墙。
             *  2. 向前一步。
             *  3. 逆时针转向90度
             *  4. 重复从1开始的步骤 ,直到它发现自己位于原来的起点,还有原来的朝向。
             *
             */

            /*
             * 算法在遍历的同时,检测锐角(90度) 和 钝角(270)拐点。如果
             * 一个完整的轮廓被检测完整,并且 钝角拐点比锐角拐点多。那
             * 么 null Region 就在这个轮廓里面。否则就在轮廓外面。
             *
             */


            //环绕null region 走一圈,最后走回自己的起点
            int borderRegionID = startSpan.regionID();

            OpenHeightSpan span  = startSpan;
            OpenHeightSpan nSpan = null;
            int            dir   = startDirection;

            int  loopCount          = 0;
            int  acuteCornerCount   = 0;
            int  obtuseCornerCount  = 0;
            int  stepsWithoutBorder = 0;
            bool borderSeenLastLoop = false;
            bool isBorder           = true;

            bool hasSingleConnection = true;

            while (++loopCount < int.MaxValue)
            {
                //初始方向就是面向的Null Region,所以一开始是肯定是isBorder的
                nSpan = span.getNeighbor(dir);
                if (null == nSpan)
                {
                    isBorder = true;
                }
                else
                {
                    nSpan.flags = 1;
                    if (NULL_REGION == nSpan.regionID())
                    {
                        isBorder = true;
                    }
                    else
                    {
                        isBorder = false;
                        if (nSpan.regionID() != borderRegionID)
                        {
                            hasSingleConnection = false;
                        }
                    }
                } // else


                if (isBorder)
                {
                    if (borderSeenLastLoop)
                    {
                        /*
                         *  a x
                         *  x x
                         */

                        //其实这个应用用inner来描述更加准确 ,表明a被x包围了
                        acuteCornerCount++;
                    }
                    else if (stepsWithoutBorder > 1)
                    {
                        /*
                         *  a a
                         *  a x
                         *
                         */
                        obtuseCornerCount++;  //相对地,我觉得这个应该用outer来描述更加准确,表明a正在包围x
                        stepsWithoutBorder = 0;

                        //处理钝角的各种异常情况
                        if (processOuterCorner(span, dir))
                        {
                            hasSingleConnection = false;
                        }
                    }

                    dir = NMGenUtility.ClockwiseRotateDir(dir);
                    borderSeenLastLoop = true;
                    stepsWithoutBorder = 0;
                }
                else   //注意,不是边界,才会进行移动,如果是边界的话,只会进行转向
                {
                    span = nSpan;
                    dir  = NMGenUtility.CClockwiseRotateDir(dir); //逆时针转向一下
                    borderSeenLastLoop = false;
                    stepsWithoutBorder++;
                } // else

                //回到原方位了
                if (startSpan == span &&
                    startDirection == dir)
                {
                    return(hasSingleConnection &&
                           obtuseCornerCount > acuteCornerCount);
                }
            } // while

            return(false);
        }
예제 #3
0
        private void buildRawContours(OpenHeightSpan startSpan,
                                      int startWidthIndex,
                                      int startDepthIndex,
                                      int startDirection,
                                      ref List <int> outContourVerts
                                      )
        {
            OpenHeightSpan span      = startSpan;
            int            dir       = startDirection;
            int            spanX     = startWidthIndex;
            int            spanZ     = startDepthIndex;
            int            loopCount = 0;

            while (++loopCount < ushort.MaxValue)
            {
                //这个Edge的一个Span
                if (!isSameRegion(span, dir))
                {
                    //span
                    int px = spanX;
                    int py = getCornerHeight(span, dir);
                    int pz = spanZ;

                    /*
                     * 这里取的是点,所以需要做这些偏移来记录点,而不是记录对应的Span
                     * 这里需要结合 :  方向 + 某个点 ,来理解 为什么要加这个偏移
                     *
                     *
                     *     * --- *
                     *     |  S  |
                     *     ----—-*
                     *
                     *    dir = 0 的时候,需要取左上角的点
                     *    dir = 1 的时候,需要取右上角的点
                     *    dir = 2 的时候,需要取右下角的点
                     *    dir = 3 的时候,取的就是参考点
                     *
                     *    以左下角的点为参考点,就是 dir方向对应的顺时针的点
                     *
                     */


                    /*
                     * Update the px and pz values based on current direction.
                     * The update is such that the corner being represented is
                     * clockwise from the edge the direction is currently pointing
                     * toward.
                     */

                    switch (dir)
                    {
                    case 0: pz++; break;

                    case 1: px++; pz++; break;

                    case 2: px++; break;
                    }

                    int            regionThisDirection = NULL_REGION;
                    OpenHeightSpan nSpan = span.getNeighbor(dir);
                    if (nSpan != null)
                    {
                        regionThisDirection = nSpan.regionID();
                    }

                    //这是轮廓的点
                    outContourVerts.Add(px);
                    outContourVerts.Add(py);
                    outContourVerts.Add(pz);
                    outContourVerts.Add(regionThisDirection);

                    span.flags &= ~(1 << dir);  //清除dir对应的位
                    dir         = NMGenUtility.ClockwiseRotateDir(dir);
                } //isSameRegion
                else
                {
                    //这段就是步进到下一个同Region的Span,很好理解。
                    span = span.getNeighbor(dir);
                    switch (dir)
                    {
                    case 0: spanX--; break;

                    case 1: spanZ++; break;

                    case 2: spanX++; break;

                    case 3: spanZ--; break;
                    }
                    dir = NMGenUtility.CClockwiseRotateDir(dir);
                } // no the SameRegion

                if (span == startSpan &&
                    dir == startDirection)
                {
                    break;
                }
            } //while
        }     //buildRawContour