/// <summary> /// Try to visit all the spans. May be needed in filtering small regions. /// </summary> /// <param name="regions">an array of region values</param> /// <param name="spanRef">The span to start walking from.</param> /// <param name="dir">The direction to start walking in.</param> /// <param name="cont">A collection of regions to append to.</param> private void WalkContour(RegionId[] regions, CompactSpanReference spanRef, Direction dir, List<RegionId> cont) { Direction startDir = dir; int starti = spanRef.Index; CompactSpan ss = spans[starti]; RegionId curReg = RegionId.Null; if (ss.IsConnected(dir)) { int dx = spanRef.X + dir.GetHorizontalOffset(); int dy = spanRef.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref ss, dir); curReg = regions[di]; } cont.Add(curReg); int iter = 0; while (++iter < 40000) { CompactSpan s = spans[spanRef.Index]; if (IsSolidEdge(regions, ref spanRef, dir)) { //choose the edge corner RegionId r = RegionId.Null; if (s.IsConnected(dir)) { int dx = spanRef.X + dir.GetHorizontalOffset(); int dy = spanRef.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dir); r = regions[di]; } if (r != curReg) { curReg = r; cont.Add(curReg); } dir = dir.NextClockwise(); //rotate clockwise } else { int di = -1; int dx = spanRef.X + dir.GetHorizontalOffset(); int dy = spanRef.Y + dir.GetVerticalOffset(); if (s.IsConnected(dir)) { CompactCell dc = cells[dx + dy * width]; di = dc.StartIndex + CompactSpan.GetConnection(ref s, dir); } if (di == -1) { //shouldn't happen return; } spanRef = new CompactSpanReference(dx, dy, di); dir = dir.NextCounterClockwise(); //rotate counterclockwise } if (starti == spanRef.Index && startDir == dir) break; } //remove adjacent duplicates if (cont.Count > 1) { for (int j = 0; j < cont.Count;) { //next element int nj = (j + 1) % cont.Count; //adjacent duplicate found if (cont[j] == cont[nj]) cont.RemoveAt(j); else j++; } } }
/// <summary> /// Helper method for WalkContour function /// </summary> /// <param name="sr">The span to get the corner height for.</param> /// <param name="dir">The direction to get the corner height from.</param> /// <param name="isBorderVertex">Determine whether the vertex is a border or not.</param> /// <returns>The corner height.</returns> private int GetCornerHeight(CompactSpanReference sr, Direction dir, out bool isBorderVertex) { isBorderVertex = false; CompactSpan s = this[sr]; int cornerHeight = s.Minimum; Direction dirp = dir.NextClockwise(); //new clockwise direction RegionId[] cornerRegs = new RegionId[4]; Area[] cornerAreas = new Area[4]; //combine region and area codes in order to prevent border vertices, which are in between two areas, to be removed cornerRegs[0] = s.Region; cornerAreas[0] = areas[sr.Index]; if (s.IsConnected(dir)) { //get neighbor span int dx = sr.X + dir.GetHorizontalOffset(); int dy = sr.Y + dir.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dir); CompactSpan ds = spans[di]; cornerHeight = Math.Max(cornerHeight, ds.Minimum); cornerRegs[1] = spans[di].Region; cornerAreas[1] = areas[di]; //get neighbor of neighbor's span if (ds.IsConnected(dirp)) { int dx2 = dx + dirp.GetHorizontalOffset(); int dy2 = dy + dirp.GetVerticalOffset(); int di2 = cells[dx2 + dy2 * width].StartIndex + CompactSpan.GetConnection(ref ds, dirp); CompactSpan ds2 = spans[di2]; cornerHeight = Math.Max(cornerHeight, ds2.Minimum); cornerRegs[2] = ds2.Region; cornerAreas[2] = areas[di2]; } } //get neighbor span if (s.IsConnected(dirp)) { int dx = sr.X + dirp.GetHorizontalOffset(); int dy = sr.Y + dirp.GetVerticalOffset(); int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dirp); CompactSpan ds = spans[di]; cornerHeight = Math.Max(cornerHeight, ds.Minimum); cornerRegs[3] = ds.Region; cornerAreas[3] = areas[di]; //get neighbor of neighbor's span if (ds.IsConnected(dir)) { int dx2 = dx + dir.GetHorizontalOffset(); int dy2 = dy + dir.GetVerticalOffset(); int di2 = cells[dx2 + dy2 * width].StartIndex + CompactSpan.GetConnection(ref ds, dir); CompactSpan ds2 = spans[di2]; cornerHeight = Math.Max(cornerHeight, ds2.Minimum); cornerRegs[2] = ds2.Region; cornerAreas[2] = areas[di2]; } } //check if vertex is special edge vertex //if so, these vertices will be removed later for (int j = 0; j < 4; j++) { int a = j; int b = (j + 1) % 4; int c = (j + 2) % 4; int d = (j + 3) % 4; RegionId ra = cornerRegs[a], rb = cornerRegs[b], rc = cornerRegs[c], rd = cornerRegs[d]; Area aa = cornerAreas[a], ab = cornerAreas[b], ac = cornerAreas[c], ad = cornerAreas[d]; //the vertex is a border vertex if: //two same exterior cells in a row followed by two interior cells and none of the regions are out of bounds bool twoSameExteriors = RegionId.HasFlags(ra, RegionFlags.Border) && RegionId.HasFlags(rb, RegionFlags.Border) && (ra == rb && aa == ab); bool twoSameInteriors = !(RegionId.HasFlags(rc, RegionFlags.Border) || RegionId.HasFlags(rd, RegionFlags.Border)); bool intsSameArea = ac == ad; bool noZeros = ra != 0 && rb != 0 && rc != 0 && rd != 0 && aa != 0 && ab != 0 && ac != 0 && ad != 0; if (twoSameExteriors && twoSameInteriors && intsSameArea && noZeros) { isBorderVertex = true; break; } } return cornerHeight; }