//遍历轮廓,找邻接的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); } }
/// <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); }
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