/// <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;
        }