/// <summary>
 /// Gets the <see cref="CompactSpan"/> specified by the reference.
 /// </summary>
 /// <param name="spanRef">A reference to a span in this <see cref="CompactHeightfield"/>.</param>
 /// <returns>The referenced span.</returns>
 public CompactSpan this[CompactSpanReference spanRef]
 {
     get
     {
         return spans[spanRef.Index];
     }
 }
        /// <summary>
        /// Builds a set of <see cref="Contour"/>s around the generated regions. Must be called after regions are generated.
        /// </summary>
        /// <param name="maxError">The maximum allowed deviation in a simplified contour from a raw one.</param>
        /// <param name="maxEdgeLength">The maximum edge length.</param>
        /// <param name="buildFlags">Flags that change settings for the build process.</param>
        /// <returns>A <see cref="ContourSet"/> containing one contour per region.</returns>
        public ContourSet BuildContourSet(float maxError, int maxEdgeLength, ContourBuildFlags buildFlags)
        {
            BBox3 contourSetBounds = bounds;
            if (borderSize > 0)
            {
                //remove offset
                float pad = borderSize * cellSize;
                contourSetBounds.Min.X += pad;
                contourSetBounds.Min.Z += pad;
                contourSetBounds.Max.X -= pad;
                contourSetBounds.Max.Z -= pad;
            }

            int contourSetWidth = width - borderSize * 2;
            int contourSetLength = length - borderSize * 2;

            int maxContours = Math.Max(maxRegions, 8);
            var contours = new List<Contour>(maxContours);

            EdgeFlags[] flags = new EdgeFlags[spans.Length];

            //Modify flags array by using the CompactHeightfield data
            //mark boundaries
            for (int z = 0; z < length; z++)
            {
                for (int x = 0; x < width; x++)
                {
                    //loop through all the spans in the cell
                    CompactCell c = cells[x + z * width];
                    for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++)
                    {
                        CompactSpan s = spans[i];

                        //set the flag to 0 if the region is a border region or null.
                        if (s.Region.IsNull || RegionId.HasFlags(s.Region, RegionFlags.Border))
                        {
                            flags[i] = 0;
                            continue;
                        }

                        //go through all the neighboring cells
                        for (var dir = Direction.West; dir <= Direction.South; dir++)
                        {
                            //obtain region id
                            RegionId r = RegionId.Null;
                            if (s.IsConnected(dir))
                            {
                                int dx = x + dir.GetHorizontalOffset();
                                int dz = z + dir.GetVerticalOffset();
                                int di = cells[dx + dz * width].StartIndex + CompactSpan.GetConnection(ref s, dir);
                                r = spans[di].Region;
                            }

                            //region ids are equal
                            if (r == s.Region)
                            {
                                //res marks all the internal edges
                                EdgeFlagsHelper.AddEdge(ref flags[i], dir);
                            }
                        }

                        //flags represents all the nonconnected edges, edges that are only internal
                        //the edges need to be between different regions
                        EdgeFlagsHelper.FlipEdges(ref flags[i]);
                    }
                }
            }

            var verts = new List<ContourVertex>();
            var simplified = new List<ContourVertex>();

            for (int z = 0; z < length; z++)
            {
                for (int x = 0; x < width; x++)
                {
                    CompactCell c = cells[x + z * width];
                    for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++)
                    {
                        //flags is either 0000 or 1111
                        //in other words, not connected at all
                        //or has all connections, which means span is in the middle and thus not an edge.
                        if (flags[i] == EdgeFlags.None || flags[i] == EdgeFlags.All)
                        {
                            flags[i] = EdgeFlags.None;
                            continue;
                        }

                        var spanRef = new CompactSpanReference(x, z, i);
                        RegionId reg = this[spanRef].Region;
                        if (reg.IsNull || RegionId.HasFlags(reg, RegionFlags.Border))
                            continue;

                        //reset each iteration
                        verts.Clear();
                        simplified.Clear();

                        //Walk along a contour, then build it
                        WalkContour(spanRef, flags, verts);
                        Contour.Simplify(verts, simplified, maxError, maxEdgeLength, buildFlags);
                        Contour.RemoveDegenerateSegments(simplified);
                        Contour contour = new Contour(simplified, reg, areas[i], borderSize);

                        if (!contour.IsNull)
                            contours.Add(contour);
                    }
                }
            }

            //Check and merge bad contours
            for (int i = 0; i < contours.Count; i++)
            {
                Contour cont = contours[i];

                //Check if contour is backwards
                if (cont.Area2D < 0)
                {
                    //Find another contour to merge with
                    int mergeIndex = -1;
                    for (int j = 0; j < contours.Count; j++)
                    {
                        if (i == j)
                            continue;

                        //Must have at least one vertex, the same region ID, and be going forwards.
                        Contour contj = contours[j];
                        if (contj.Vertices.Length != 0 && contj.RegionId == cont.RegionId && contj.Area2D > 0)
                        {
                            mergeIndex = j;
                            break;
                        }
                    }

                    //Merge if found.
                    if (mergeIndex != -1)
                    {
                        contours[mergeIndex].MergeWith(cont);
                        contours.RemoveAt(i);
                        i--;
                    }
                }
            }

            return new ContourSet(contours, contourSetBounds, contourSetWidth, contourSetLength);
        }
        /// <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>
        /// Initial generation of the contours
        /// </summary>
        /// <param name="spanReference">A referecne to the span to start walking from.</param>
        /// <param name="flags">An array of flags determinining </param>
        /// <param name="points">The vertices of a contour.</param>
        private void WalkContour(CompactSpanReference spanReference, EdgeFlags[] flags, List<ContourVertex> points)
        {
            Direction dir = Direction.West;

            //find the first direction that has a connection
            while (!EdgeFlagsHelper.IsConnected(ref flags[spanReference.Index], dir))
                dir++;

            Direction startDir = dir;
            int startIndex = spanReference.Index;

            Area area = areas[startIndex];

            //TODO make the max iterations value a variable
            int iter = 0;
            while (++iter < 40000)
            {
                // this direction is connected
                if (EdgeFlagsHelper.IsConnected(ref flags[spanReference.Index], dir))
                {
                    // choose the edge corner
                    bool isBorderVertex;
                    bool isAreaBorder = false;

                    int px = spanReference.X;
                    int py = GetCornerHeight(spanReference, dir, out isBorderVertex);
                    int pz = spanReference.Y;

                    switch (dir)
                    {
                        case Direction.West:
                            pz++;
                            break;
                        case Direction.North:
                            px++;
                            pz++;
                            break;
                        case Direction.East:
                            px++;
                            break;
                    }

                    RegionId r = RegionId.Null;
                    CompactSpan s = this[spanReference];
                    if (s.IsConnected(dir))
                    {
                        int dx = spanReference.X + dir.GetHorizontalOffset();
                        int dy = spanReference.Y + dir.GetVerticalOffset();
                        int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref s, dir);
                        r = spans[di].Region;
                        if (area != areas[di])
                            isAreaBorder = true;
                    }

                    // apply flags if neccessary
                    if (isBorderVertex)
                        r = RegionId.WithFlags(r, RegionFlags.VertexBorder);

                    if (isAreaBorder)
                        r = RegionId.WithFlags(r, RegionFlags.AreaBorder);

                    //save the point
                    points.Add(new ContourVertex(px, py, pz, r));

                    EdgeFlagsHelper.RemoveEdge(ref flags[spanReference.Index], dir);	// remove visited edges
                    dir = dir.NextClockwise();			// rotate clockwise
                }
                else
                {
                    //get a new cell(x, y) and span index(i)
                    int di = -1;
                    int dx = spanReference.X + dir.GetHorizontalOffset();
                    int dy = spanReference.Y + dir.GetVerticalOffset();

                    CompactSpan s = this[spanReference];
                    if (s.IsConnected(dir))
                    {
                        CompactCell dc = cells[dx + dy * width];
                        di = dc.StartIndex + CompactSpan.GetConnection(ref s, dir);
                    }

                    if (di == -1)
                    {
                        // shouldn't happen
                        // TODO if this shouldn't happen, this check shouldn't be necessary.
                        throw new InvalidOperationException("Something went wrong");
                    }

                    spanReference = new CompactSpanReference(dx, dy, di);
                    dir = dir.NextCounterClockwise(); // rotate counterclockwise
                }

                if (startIndex == spanReference.Index && startDir == dir)
                    break;
            }
        }
        /// <summary>
        /// Checks whether the edge from a span in a direction is a solid edge.
        /// A solid edge is an edge between two regions.
        /// </summary>
        /// <param name="regions">The region ID array.</param>
        /// <param name="spanRef">A reference to the span connected to the edge.</param>
        /// <param name="dir">The direction of the edge.</param>
        /// <returns>A value indicating whether the described edge is solid.</returns>
        private bool IsSolidEdge(RegionId[] regions, ref CompactSpanReference spanRef, Direction dir)
        {
            CompactSpan s = spans[spanRef.Index];
            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 == regions[spanRef.Index])
                return false;

            return true;
        }
        /// <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;
        }
        /// <summary>
        /// Floods the regions at a certain level
        /// </summary>
        /// <param name="regions">source region</param>
        /// <param name="floodDistances">source distances</param>
        /// <param name="regionIndex">region id</param>
        /// <param name="level">current level</param>
        /// <param name="start">A reference to the starting span.</param>
        /// <returns>Always true.</returns>
        private bool FloodRegion(RegionId[] regions, int[] floodDistances, int regionIndex, int level, ref CompactSpanReference start)
        {
            //TODO this method should always return true, make it not return a bool?
            //flood fill mark region
            Stack<CompactSpanReference> stack = new Stack<CompactSpanReference>();
            stack.Push(start);

            Area area = areas[start.Index];
            regions[start.Index] = new RegionId(regionIndex);
            floodDistances[start.Index] = 0;

            int lev = level >= 2 ? level - 2 : 0;
            int count = 0;

            while (stack.Count > 0)
            {
                CompactSpanReference cell = stack.Pop();
                CompactSpan cs = spans[cell.Index];

                //check if any of the neighbors already have a valid reigon set
                RegionId ar = RegionId.Null;
                for (var dir = Direction.West; dir <= Direction.South; dir++)
                {
                    //8 connected
                    if (cs.IsConnected(dir))
                    {
                        int dx = cell.X + dir.GetHorizontalOffset();
                        int dy = cell.Y + dir.GetVerticalOffset();
                        int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref cs, dir);

                        if (areas[di] != area)
                            continue;

                        RegionId nr = regions[di];

                        if (RegionId.HasFlags(nr, RegionFlags.Border)) //skip borders
                            continue;

                        if (nr != 0 && nr != regionIndex)
                        {
                            ar = nr;
                            break;
                        }

                        CompactSpan ds = spans[di];
                        Direction dir2 = dir.NextClockwise();
                        if (ds.IsConnected(dir2))
                        {
                            int dx2 = dx + dir2.GetHorizontalOffset();
                            int dy2 = dy + dir2.GetVerticalOffset();
                            int di2 = cells[dx2 + dy2 * width].StartIndex + CompactSpan.GetConnection(ref ds, dir2);

                            if (areas[di2] != area)
                                continue;

                            RegionId nr2 = regions[di2];
                            if (nr2 != 0 && nr2 != regionIndex)
                            {
                                ar = nr2;
                                break;
                            }
                        }
                    }
                }

                if (ar != 0)
                {
                    regions[cell.Index] = RegionId.Null;
                    continue;
                }

                count++;

                //expand neighbors
                for (var dir = Direction.West; dir <= Direction.South; dir++)
                {
                    if (cs.IsConnected(dir))
                    {
                        int dx = cell.X + dir.GetHorizontalOffset();
                        int dy = cell.Y + dir.GetVerticalOffset();
                        int di = cells[dx + dy * width].StartIndex + CompactSpan.GetConnection(ref cs, dir);

                        if (areas[di] != area)
                            continue;

                        if (distances[di] >= lev && regions[di] == 0)
                        {
                            regions[di] = new RegionId(regionIndex);
                            floodDistances[di] = 0;
                            stack.Push(new CompactSpanReference(dx, dy, di));
                        }
                    }
                }
            }

            return count > 0;
        }
        /// <summary>
        /// Discards regions that are too small. 
        /// </summary>
        /// <param name="regionIds">region data</param>
        /// <param name="minRegionArea">The minimum area a region can have</param>
        /// <param name="mergeRegionSize">The size of the regions after merging</param>
        /// <param name="maxRegionId">determines the number of regions available</param>
        /// <returns>The reduced number of regions.</returns>
        private int FilterSmallRegions(RegionId[] regionIds, int minRegionArea, int mergeRegionSize, int maxRegionId)
        {
            int numRegions = maxRegionId + 1;
            Region[] regions = new Region[numRegions];

            //construct regions
            for (int i = 0; i < numRegions; i++)
                regions[i] = new Region(i);

            //find edge of a region and find connections around a contour
            for (int y = 0; y < length; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    CompactCell c = cells[x + y * width];
                    for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++)
                    {
                        CompactSpanReference spanRef = new CompactSpanReference(x, y, i);

                        //HACK since the border region flag makes r negative, I changed r == 0 to r <= 0. Figure out exactly what maxRegionId's purpose is and see if Region.IsBorderOrNull is all we need.
                        int r = (int)regionIds[i];
                        if (r <= 0 || (int)r >= numRegions)
                            continue;

                        Region reg = regions[(int)r];
                        reg.SpanCount++;

                        //update floors
                        for (int j = c.StartIndex; j < end; j++)
                        {
                            if (i == j) continue;
                            RegionId floorId = regionIds[j];
                            if (floorId == 0 || (int)floorId >= numRegions)
                                continue;
                            reg.AddUniqueFloorRegion(floorId);
                        }

                        //have found contour
                        if (reg.Connections.Count > 0)
                            continue;

                        reg.AreaType = areas[i];

                        //check if this cell is next to a border
                        for (var dir = Direction.West; dir <= Direction.South; dir++)
                        {
                            if (IsSolidEdge(regionIds, ref spanRef, dir))
                            {
                                //The cell is at a border.
                                //Walk around contour to find all neighbors
                                WalkContour(regionIds, spanRef, dir, reg.Connections);
                                break;
                            }
                        }
                    }
                }
            }

            //Remove too small regions
            Stack<RegionId> stack = new Stack<RegionId>();
            List<RegionId> trace = new List<RegionId>();
            for (int i = 0; i < numRegions; i++)
            {
                Region reg = regions[i];
                if (reg.IsBorderOrNull || reg.SpanCount == 0 || reg.Visited)
                    continue;

                //count the total size of all connected regions
                //also keep track of the regions connections to a tile border
                bool connectsToBorder = false;
                int spanCount = 0;
                stack.Clear();
                trace.Clear();

                reg.Visited = true;
                stack.Push(reg.Id);

                while (stack.Count > 0)
                {
                    //pop
                    RegionId ri = stack.Pop();

                    Region creg = regions[(int)ri];

                    spanCount += creg.SpanCount;
                    trace.Add(ri);

                    for (int j = 0; j < creg.Connections.Count; j++)
                    {
                        if (RegionId.HasFlags(creg.Connections[j], RegionFlags.Border))
                        {
                            connectsToBorder = true;
                            continue;
                        }

                        Region neiReg = regions[(int)creg.Connections[j]];
                        if (neiReg.Visited || neiReg.IsBorderOrNull)
                            continue;

                        //visit
                        stack.Push(neiReg.Id);
                        neiReg.Visited = true;
                    }
                }

                //if the accumulated region size is too small, remove it
                //do not remove areas which connect to tile borders as their size can't be estimated correctly
                //and removing them can potentially remove necessary areas
                if (spanCount < minRegionArea && !connectsToBorder)
                {
                    //kill all visited regions
                    for (int j = 0; j < trace.Count; j++)
                    {
                        int index = (int)trace[j];

                        regions[index].SpanCount = 0;
                        regions[index].Id = RegionId.Null;
                    }
                }
            }

            //Merge too small regions to neighbor regions
            int mergeCount = 0;
            do
            {
                mergeCount = 0;
                for (int i = 0; i < numRegions; i++)
                {
                    Region reg = regions[i];
                    if (reg.IsBorderOrNull || reg.SpanCount == 0)
                        continue;

                    //check to see if region should be merged
                    if (reg.SpanCount > mergeRegionSize && reg.IsConnectedToBorder())
                        continue;

                    //small region with more than one connection or region which is not connected to border at all
                    //find smallest neighbor that connects to this one
                    int smallest = int.MaxValue;
                    RegionId mergeId = reg.Id;
                    for (int j = 0; j < reg.Connections.Count; j++)
                    {
                        if (RegionId.HasFlags(reg.Connections[j], RegionFlags.Border))
                            continue;

                        Region mreg = regions[(int)reg.Connections[j]];
                        if (mreg.IsBorderOrNull)
                            continue;

                        if (mreg.SpanCount < smallest && reg.CanMergeWith(mreg) && mreg.CanMergeWith(reg))
                        {
                            smallest = mreg.SpanCount;
                            mergeId = mreg.Id;
                        }
                    }

                    //found new id
                    if (mergeId != reg.Id)
                    {
                        RegionId oldId = reg.Id;
                        Region target = regions[(int)mergeId];

                        //merge regions
                        if (target.MergeWithRegion(reg))
                        {
                            //fix regions pointing to current region
                            for (int j = 0; j < numRegions; j++)
                            {
                                if (regions[j].IsBorderOrNull)
                                    continue;

                                //if another regions was already merged into current region
                                //change the nid of the previous region too
                                if (regions[j].Id == oldId)
                                    regions[j].Id = mergeId;

                                //replace current region with new one if current region is neighbor
                                regions[j].ReplaceNeighbour(oldId, mergeId);
                            }

                            mergeCount++;
                        }
                    }
                }
            }
            while (mergeCount > 0);

            //Compress region ids
            for (int i = 0; i < numRegions; i++)
            {
                regions[i].Remap = false;

                if (regions[i].IsBorderOrNull)
                    continue;

                regions[i].Remap = true;
            }

            int regIdGen = 0;
            for (int i = 0; i < numRegions; i++)
            {
                if (!regions[i].Remap)
                    continue;

                RegionId oldId = regions[i].Id;
                RegionId newId = new RegionId(++regIdGen);
                for (int j = i; j < numRegions; j++)
                {
                    if (regions[j].Id == oldId)
                    {
                        regions[j].Id = newId;
                        regions[j].Remap = false;
                    }
                }
            }

            //Remap regions
            for (int i = 0; i < spans.Length; i++)
            {
                if (!RegionId.HasFlags(regionIds[i], RegionFlags.Border))
                    regionIds[i] = regions[(int)regionIds[i]].Id;
            }

            return regIdGen;
        }
示例#9
0
		/// <summary>
		/// Floodfill heightfield to get 2D height data, starting at vertex locations
		/// </summary>
		/// <param name="compactField">Original heightfield data</param>
		/// <param name="poly">Polygon in PolyMesh</param>
		/// <param name="polyCount">Number of vertices per polygon</param>
		/// <param name="verts">PolyMesh Vertices</param>
		/// <param name="borderSize">Heightfield border size</param>
		/// <param name="hp">HeightPatch which extracts heightfield data</param>
		/// <param name="stack">Temporary stack of CompactSpanReferences</param>
		private void GetHeightDataSeedsFromVertices(CompactHeightfield compactField, PolyMesh.Polygon poly, int polyCount, PolyVertex[] verts, int borderSize, HeightPatch hp, List<CompactSpanReference> stack)
		{
			hp.SetAll(0);

			//use poly vertices as seed points
			for (int j = 0; j < polyCount; j++)
			{
				var csr = new CompactSpanReference(0, 0, -1);
				int dmin = int.MaxValue;

				var v = verts[poly.Vertices[j]];

				for (int k = 0; k < 9; k++)
				{
					//get vertices and offset x and z coordinates depending on current drection
					int ax = v.X + VertexOffset[k * 2 + 0];
					int ay = v.Y;
					int az = v.Z + VertexOffset[k * 2 + 1];

					//skip if out of bounds
					if (ax < hp.X || ax >= hp.X + hp.Width || az < hp.Y || az >= hp.Y + hp.Length)
						continue;

					//get new cell
					CompactCell c = compactField.Cells[(az + borderSize) * compactField.Width + (ax + borderSize)];
					
					//loop through all the spans
					for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++)
					{
						CompactSpan s = compactField.Spans[i];
						
						//find minimum y-distance
						int d = Math.Abs(ay - s.Minimum);
						if (d < dmin)
						{
							csr = new CompactSpanReference(ax, az, i);
							dmin = d;
						}
					}
				}

				//only add if something new found
				if (csr.Index != -1)
				{
					stack.Add(csr);
				}
			}

			//find center of polygon using flood fill
			int pcx = 0, pcz = 0;
			for (int j = 0; j < polyCount; j++)
			{
				var v = verts[poly.Vertices[j]];
				pcx += v.X;
				pcz += v.Z;
			}

			pcx /= polyCount;
			pcz /= polyCount;

			//stack groups 3 elements as one part
			foreach (var cell in stack)
			{
				int idx = (cell.Y - hp.Y) * hp.Width + (cell.X - hp.X);
				hp[idx] = 1;
			}

			//process the entire stack
			while (stack.Count > 0)
			{
				var cell = stack[stack.Count - 1];
				stack.RemoveAt(stack.Count - 1);

				//check if close to center of polygon
				if (Math.Abs(cell.X - pcx) <= 1 && Math.Abs(cell.Y - pcz) <= 1)
				{
					//clear the stack and add a new group
					stack.Clear();

					stack.Add(cell);
					break;
				}

				CompactSpan cs = compactField[cell];

				//check all four directions
				for (var dir = Direction.West; dir <= Direction.South; dir++)
				{
					//skip if disconnected
					if (!cs.IsConnected(dir))
						continue;

					//get neighbor
					int ax = cell.X + dir.GetHorizontalOffset();
					int ay = cell.Y + dir.GetVerticalOffset();

					//skip if out of bounds
					if (ax < hp.X || ax >= (hp.X + hp.Width) || ay < hp.Y || ay >= (hp.Y + hp.Length))
						continue;

					if (hp[(ay - hp.Y) * hp.Width + (ax - hp.X)] != 0)
						continue;

					//get the new index
					int ai = compactField.Cells[(ay + borderSize) * compactField.Width + (ax + borderSize)].StartIndex + CompactSpan.GetConnection(ref cs, dir);

					//save data
					int idx = (ay - hp.Y) * hp.Width + (ax - hp.X);
					hp[idx] = 1;

					//push to stack
					stack.Add(new CompactSpanReference(ax, ay, ai));
				}
			}

			//clear the heightpatch
			hp.Clear();

			//mark start locations
			for (int i = 0; i < stack.Count; i++)
			{
				var c = stack[i];

				//set new heightpatch data
				int idx = (c.Y - hp.Y) * hp.Width + (c.X - hp.X);
				CompactSpan cs = compactField.Spans[c.Index];
				hp[idx] = cs.Minimum;

				stack[i] = new CompactSpanReference(c.X + borderSize, c.Y + borderSize, c.Index);
			}
		}