private void RemoveDegenerateSegments(ref IntArray simplified)
        {
            for (int i = 0; i < simplified.Size / 4; i++)
            {
                int ni = i + 1;
                if (ni >= simplified.Size / 4)
                {
                    ni = 0;
                }

                if (simplified[i * 4 + 0] == simplified[ni * 4 + 0] && simplified[i * 4 + 2] == simplified[ni * 4 + 2])
                {
                    for (int j = i; j < simplified.Size / 4 - 1; j++)
                    {
                        simplified[j * 4 + 0] = simplified[(j + 1) * 4 + 0];
                        simplified[j * 4 + 1] = simplified[(j + 1) * 4 + 1];
                        simplified[j * 4 + 2] = simplified[(j + 1) * 4 + 2];
                        simplified[j * 4 + 3] = simplified[(j + 1) * 4 + 3];
                    }
                    simplified.Resize(simplified.Size - 4);
                }
            }
        }
        private void DelaunayHull(int npts, float[] pts, int nhull, int[] hull, ref IntArray tris, ref IntArray edges)
        {
            int nfaces   = 0;
            int nedges   = 0;
            int maxEdges = npts * 10;

            edges.Resize(maxEdges * 4);

            for (int i = 0, j = nhull - 1; i < nhull; j = i++)
            {
                AddEdge(ref edges, ref nedges, maxEdges, hull[j], hull[i], Hull, Undef);
            }

            int currentEdge = 0;

            while (currentEdge < nedges)
            {
                if (edges[currentEdge * 4 + 2] == Undef)
                {
                    CompleteFacet(pts, npts, ref edges, ref nedges, maxEdges, ref nfaces, currentEdge);
                }
                if (edges[currentEdge * 4 + 3] == Undef)
                {
                    CompleteFacet(pts, npts, ref edges, ref nedges, maxEdges, ref nfaces, currentEdge);
                }
                currentEdge++;
            }

            tris.Resize(nfaces * 4);
            for (int i = 0; i < nfaces * 4; i++)
            {
                tris[i] = -1;
            }

            for (int i = 0; i < nedges; i++)
            {
                int e = i * 4;
                if (edges[e + 3] >= 0)
                {
                    int t = edges[e + 3] * 4;
                    if (tris[t + 0] == -1)
                    {
                        tris[t + 0] = edges[e + 0];
                        tris[t + 1] = edges[e + 1];
                    }
                    else if (tris[t + 0] == edges[e + 1])
                    {
                        tris[t + 2] = edges[e + 0];
                    }
                    else if (tris[t + 1] == edges[e + 0])
                    {
                        tris[t + 2] = edges[e + 1];
                    }
                }
                if (edges[e + 2] >= 0)
                {
                    int t = edges[e + 2] * 4;
                    if (tris[t + 0] == -1)
                    {
                        tris[t + 0] = edges[e + 1];
                        tris[t + 1] = edges[e + 0];
                    }
                    else if (tris[t + 0] == edges[e + 0])
                    {
                        tris[t + 2] = edges[e + 1];
                    }
                    else if (tris[t + 1] == edges[e + 1])
                    {
                        tris[t + 2] = edges[e + 0];
                    }
                }
            }

            for (int i = 0; i < tris.Size / 4; i++)
            {
                int t = i * 4;
                if (tris[t + 0] == -1 || tris[t + 1] == -1 || tris[t + 2] == -1)
                {
                    // error
                    tris[t + 0] = tris[tris.Size - 4];
                    tris[t + 1] = tris[tris.Size - 3];
                    tris[t + 2] = tris[tris.Size - 2];
                    tris[t + 3] = tris[tris.Size - 1];
                    tris.Resize(tris.Size - 4);
                    --i;
                }
            }
        }
        private bool BuildPolyDetail(float[] inArray, int nin, float sampleDist, float sampleMaxError, CompactHeightfield chf, HeightPatch hp, ref float[] verts, out int nverts, ref IntArray tris, ref IntArray edges, ref IntArray samples)
        {
            int MaxVerts        = 127;
            int MaxTris         = 255;
            int MaxVertsPerEdge = 32;

            float[] edge  = new float[(MaxVertsPerEdge + 1) * 3];
            int[]   hull  = new int[MaxVerts];
            int     nhull = 0;

            nverts = 0;

            for (int i = 0; i < nin; i++)
            {
                Array.Copy(inArray, i * 3, verts, i * 3, 3);
            }

            nverts = nin;

            float cs  = chf.Cs;
            float ics = 1.0f / cs;

            if (sampleDist > 0)
            {
                for (int i = 0, j = nin - 1; i < nin; j = i++)
                {
                    int vj = j * 3;
                    int vi = i * 3;

                    bool swapped = false;
                    if (Math.Abs(inArray[vj + 0] - inArray[vi + 0]) < 1e-6f)
                    {
                        if (inArray[vj + 2] > inArray[vi + 2])
                        {
                            int temp = vj;
                            vj      = vi;
                            vi      = temp;
                            swapped = true;
                        }
                    }
                    else
                    {
                        if (inArray[vj + 0] > inArray[vi + 0])
                        {
                            int temp = vj;
                            vj      = vi;
                            vi      = temp;
                            swapped = true;
                        }
                    }

                    float dx = inArray[vi + 0] - inArray[vj + 0];
                    float dy = inArray[vi + 1] - inArray[vj + 1];
                    float dz = inArray[vi + 2] - inArray[vj + 2];
                    float d  = (float)Math.Sqrt(dx * dx + dz * dz);
                    int   nn = 1 + (int)Math.Floor(d / sampleDist);
                    if (nn >= MaxVertsPerEdge)
                    {
                        nn = MaxVertsPerEdge - 1;
                    }
                    if (nverts + nn >= MaxVerts)
                    {
                        nn = MaxVerts - 1 - nverts;
                    }

                    for (int k = 0; k <= nn; k++)
                    {
                        float u   = (float)k / (float)nn;
                        int   pos = k * 3;
                        edge[pos + 0] = inArray[vj + 0] + dx * u;
                        edge[pos + 1] = inArray[vj + 1] + dy * u;
                        edge[pos + 2] = inArray[vj + 2] + dz * u;
                        edge[pos + 1] = GetHeight(edge[pos + 0], edge[pos + 1], edge[pos + 2], cs, ics, chf.Ch, hp) * chf.Ch;
                    }

                    int[] idx = new int[MaxVertsPerEdge];
                    idx[0] = 0; idx[1] = nn;
                    int nidx = 2;
                    for (int k = 0; k < nidx - 1;)
                    {
                        int a  = idx[k];
                        int b  = idx[k + 1];
                        int va = a * 3;
                        int vb = b * 3;

                        float maxd = 0;
                        int   maxi = -1;
                        for (int m = a + 1; m < b; m++)
                        {
                            float dev = DistancePtSeg(edge[m * 3 + 0], edge[m * 3 + 1], edge[m * 3 + 2], edge[va + 0], edge[va + 1], edge[va + 2], edge[vb + 0], edge[vb + 1], edge[vb + 2]);
                            if (dev > maxd)
                            {
                                maxd = dev;
                                maxi = m;
                            }
                        }

                        if (maxi != -1 && maxd > sampleMaxError * sampleMaxError)
                        {
                            for (int m = nidx; m > k; m--)
                            {
                                idx[m] = idx[m - 1];
                            }
                            idx[k + 1] = maxi;
                            nidx++;
                        }
                        else
                        {
                            k++;
                        }
                    }

                    hull[nhull++] = j;
                    if (swapped)
                    {
                        for (int k = nidx - 2; k > 0; k--)
                        {
                            Array.Copy(edge, idx[k] * 3, verts, nverts * 3, 3);
                            hull[nhull++] = nverts;
                            nverts++;
                        }
                    }
                    else
                    {
                        for (int k = 1; k < nidx - 1; k++)
                        {
                            Array.Copy(edge, idx[k] * 3, verts, nverts * 3, 3);
                            hull[nhull++] = nverts;
                            nverts++;
                        }
                    }
                }
            }

            edges.Resize(0);
            tris.Resize(0);

            DelaunayHull(nverts, verts, nhull, hull, ref tris, ref edges);

            if (tris.Size == 0)
            {
                // error add default data
                for (int i = 2; i < nverts; i++)
                {
                    tris.Push(0);
                    tris.Push(i - 1);
                    tris.Push(i);
                    tris.Push(0);
                }
                return(true);
            }

            if (sampleDist > 0)
            {
                float[] bmin = new float[3], bmax = new float[3];
                Array.Copy(inArray, 0, bmin, 0, 3);
                Array.Copy(inArray, 0, bmax, 0, 3);

                for (int i = 1; i < nin; i++)
                {
                    bmin[0] = Math.Min(bmin[0], inArray[i * 3 + 0]);
                    bmin[1] = Math.Min(bmin[1], inArray[i * 3 + 1]);
                    bmin[2] = Math.Min(bmin[2], inArray[i * 3 + 2]);
                    bmax[0] = Math.Max(bmax[0], inArray[i * 3 + 0]);
                    bmax[1] = Math.Max(bmax[1], inArray[i * 3 + 1]);
                    bmax[2] = Math.Max(bmax[2], inArray[i * 3 + 2]);
                }

                int x0 = (int)Math.Floor(bmin[0] / sampleDist);
                int x1 = (int)Math.Ceiling(bmax[0] / sampleDist);
                int z0 = (int)Math.Floor(bmin[2] / sampleDist);
                int z1 = (int)Math.Ceiling(bmax[2] / sampleDist);
                samples.Resize(0);
                for (int z = z0; z < z1; z++)
                {
                    for (int x = x0; x < x1; x++)
                    {
                        float[] pt = new float[3];
                        pt[0] = x * sampleDist;
                        pt[1] = (bmax[1] + bmin[1]) * 0.5f;
                        pt[2] = z * sampleDist;

                        if (DistToPoly(nin, inArray, pt[0], pt[1], pt[2]) > -sampleDist / 2)
                        {
                            continue;
                        }
                        samples.Push(x);
                        samples.Push(GetHeight(pt[0], pt[1], pt[2], cs, ics, chf.Ch, hp));
                        samples.Push(z);
                        samples.Push(0);
                    }
                }

                int nsamples = samples.Size / 4;
                for (int iter = 0; iter < nsamples; iter++)
                {
                    if (nverts >= MaxVerts)
                    {
                        break;
                    }

                    float[] bestpt = { 0, 0, 0 };
                    float   bestd  = 0;
                    int     besti  = -1;
                    for (int i = 0; i < nsamples; i++)
                    {
                        int s = i * 4;
                        if (samples[s + 3] != 0)
                        {
                            continue;
                        }
                        float[] pt = new float[3];

                        pt[0] = samples[s + 0] * sampleDist + GetJitterX(i) * cs * 0.1f;
                        pt[1] = samples[s + 1] * chf.Ch;
                        pt[2] = samples[s + 2] * sampleDist + GetJitterY(i) * cs * 0.1f;
                        float d = DistToTriMesh(pt[0], pt[1], pt[2], verts, nverts, tris, tris.Size / 4);
                        if (d < 0)
                        {
                            continue;        // didn't hit the mesh
                        }
                        if (d > bestd)
                        {
                            bestd = d;
                            besti = i;
                            Array.Copy(pt, 0, bestpt, 0, 3);
                        }
                    }
                    if (bestd <= sampleMaxError || besti == -1)
                    {
                        break;
                    }
                    samples[besti * 4 + 3] = 1;
                    Array.Copy(bestpt, 0, verts, nverts * 3, 3);
                    nverts++;

                    edges.Resize(0);
                    tris.Resize(0);
                    DelaunayHull(nverts, verts, nhull, hull, ref tris, ref edges);
                }
            }

            int ntris = tris.Size / 4;

            if (ntris > MaxTris)
            {
                // error, shrink
                tris.Resize(MaxTris * 4);
            }
            return(true);
        }
        private void GetHeightData(CompactHeightfield chf, int[] p, int npoly, int[] verts, int bs, ref HeightPatch hp, ref IntArray stack)
        {
            for (int i = 0; i < hp.Width * hp.Height; i++)
            {
                hp.Data[i] = 0;
            }

            stack.Resize(0);

            int[] offset = { 0, 0, -1, -1, 0, -1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, -1, 0 };

            for (int j = 0; j < npoly; j++)
            {
                int cx = 0, cz = 0, ci = -1;

                int dmin = UnsetHeight;
                for (int k = 0; k < 9; k++)
                {
                    int ax = verts[p[j] * 3 + 0] + offset[k * 2 + 0];
                    int ay = verts[p[j] * 3 + 1];
                    int az = verts[p[j] * 3 + 2] + offset[k * 2 + 1];
                    if (ax < hp.XMin || ax >= hp.XMin + hp.Width || az < hp.YMin || az >= hp.YMin + hp.Height)
                    {
                        continue;
                    }

                    CompactCell c = chf.Cells[(ax + bs) + (az + bs) * chf.Width];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        CompactSpan s = chf.Spans[i];
                        int         d = Math.Abs(ay - s.Y);
                        if (d < dmin)
                        {
                            cx   = ax;
                            cz   = az;
                            ci   = i;
                            dmin = d;
                        }
                    }
                }
                if (ci != -1)
                {
                    stack.Push(cx);
                    stack.Push(cz);
                    stack.Push(ci);
                }
            }

            int pcx = 0, pcz = 0;

            for (int j = 0; j < npoly; j++)
            {
                pcx += verts[p[j] * 3 + 0];
                pcz += verts[p[j] * 3 + 2];
            }
            pcx /= npoly;
            pcz /= npoly;

            for (int i = 0; i < stack.Size; i += 3)
            {
                int cx  = stack[i + 0];
                int cy  = stack[i + 1];
                int idx = cx - hp.XMin + (cy - hp.YMin) * hp.Width;
                hp.Data[idx] = 1;
            }

            while (stack.Size > 0)
            {
                int ci = stack.Pop();
                int cy = stack.Pop();
                int cx = stack.Pop();

                if (Math.Abs(cx - pcx) <= 1 && Math.Abs(cy - pcz) <= 1)
                {
                    stack.Resize(0);
                    stack.Push(cx);
                    stack.Push(cy);
                    stack.Push(ci);
                    break;
                }

                CompactSpan cs = chf.Spans[ci];

                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) == CompactHeightfield.NotConnected)
                    {
                        continue;
                    }

                    int ax = cx + Helper.GetDirOffsetX(dir);
                    int ay = cy + Helper.GetDirOffsetY(dir);

                    if (ax < hp.XMin || ax >= (hp.XMin + hp.Width) ||
                        ay < hp.YMin || ay >= (hp.YMin + hp.Height))
                    {
                        continue;
                    }

                    if (hp.Data[ax - hp.XMin + (ay - hp.YMin) * hp.Width] != 0)
                    {
                        continue;
                    }

                    int ai = (int)chf.Cells[(ax + bs) + (ay + bs) * chf.Width].Index + cs.GetCon(dir);

                    int idx = ax - hp.XMin + (ay - hp.YMin) * hp.Width;
                    hp.Data[idx] = 1;

                    stack.Push(ax);
                    stack.Push(ay);
                    stack.Push(ai);
                }
            }

            for (int i = 0; i < hp.Data.Length; i++)
            {
                hp.Data[i] = UnsetHeight;
            }

            for (int i = 0; i < stack.Size; i += 3)
            {
                int         cx  = stack[i + 0];
                int         cy  = stack[i + 1];
                int         ci  = stack[i + 2];
                int         idx = cx - hp.XMin + (cy - hp.YMin) * hp.Width;
                CompactSpan cs  = chf.Spans[ci];
                hp.Data[idx] = cs.Y;
            }

            int RetractSize = 256;
            int head        = 0;

            while (head * 3 < stack.Size)
            {
                int cx = stack[head * 3 + 0];
                int cy = stack[head * 3 + 1];
                int ci = stack[head * 3 + 2];
                head++;
                if (head >= RetractSize)
                {
                    head = 0;
                    if (stack.Size > RetractSize * 3)
                    {
                        Array.Copy(stack.Data, RetractSize * 3, stack.Data, 0, stack.Size - RetractSize * 3);
                    }
                    stack.Resize(stack.Size - RetractSize * 3);
                }

                CompactSpan cs = chf.Spans[ci];
                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) == CompactHeightfield.NotConnected)
                    {
                        continue;
                    }

                    int ax = cx + Helper.GetDirOffsetX(dir);
                    int ay = cy + Helper.GetDirOffsetY(dir);

                    if (ax < hp.XMin || ax >= (hp.XMin + hp.Width) ||
                        ay < hp.YMin || ay >= (hp.YMin + hp.Height))
                    {
                        continue;
                    }

                    if (hp.Data[ax - hp.XMin + (ay - hp.YMin) * hp.Width] != UnsetHeight)
                    {
                        continue;
                    }

                    int ai = (int)chf.Cells[(ax + bs) + (ay + bs) * chf.Width].Index + cs.GetCon(dir);

                    CompactSpan aspan = chf.Spans[ai];
                    int         idx   = ax - hp.XMin + (ay - hp.YMin) * hp.Width;
                    hp.Data[idx] = aspan.Y;

                    stack.Push(ax);
                    stack.Push(ay);
                    stack.Push(ai);
                }
            }
        }
        private void SimplifyContour(ref IntArray points, ref IntArray simplified, float maxError, int maxEdgeLen, int buildFlags)
        {
            bool hasConnections = false;

            for (int i = 0; i < points.Size; i += 4)
            {
                if ((points[i + 3] & ContourRegMask) != 0)
                {
                    hasConnections = true;
                    break;
                }
            }
            if (hasConnections)
            {
                for (int i = 0, ni = points.Size / 4; i < ni; i++)
                {
                    int  ii            = (i + 1) % ni;
                    bool differentRegs = (points[i * 4 + 3] & ContourRegMask) != (points[ii * 4 + 3] & ContourRegMask);
                    bool areaBorders   = (points[i * 4 + 3] & AreaBorder) != (points[ii * 4 + 3] & AreaBorder);
                    if (differentRegs || areaBorders)
                    {
                        simplified.Push(points[i * 4 + 0]);
                        simplified.Push(points[i * 4 + 1]);
                        simplified.Push(points[i * 4 + 2]);
                        simplified.Push(i);
                    }
                }
            }

            if (simplified.Size == 0)
            {
                int llx = points[0];
                int lly = points[1];
                int llz = points[2];
                int lli = 0;
                int urx = points[0];
                int ury = points[1];
                int urz = points[2];
                int uri = 0;
                for (int i = 0; i < points.Size; i += 4)
                {
                    int x = points[i + 0];
                    int y = points[i + 1];
                    int z = points[i + 2];
                    if (x < llx || (x == llx && z < llz))
                    {
                        llx = x;
                        lly = y;
                        llz = z;
                        lli = i / 4;
                    }
                    if (x > urx || (x == urx && z > urz))
                    {
                        urx = x;
                        ury = y;
                        urz = z;
                        uri = i / 4;
                    }
                }
                simplified.Push(llx);
                simplified.Push(lly);
                simplified.Push(llz);
                simplified.Push(lli);

                simplified.Push(urx);
                simplified.Push(ury);
                simplified.Push(urz);
                simplified.Push(uri);
            }

            int pn = points.Size / 4;

            // go through all the points, as more points are added on to the end
            for (int i = 0; i < simplified.Size / 4;)
            {
                int ii = (i + 1) % (simplified.Size / 4);

                int ax = simplified[i * 4 + 0];
                int az = simplified[i * 4 + 2];
                int ai = simplified[i * 4 + 3];

                int bx = simplified[ii * 4 + 0];
                int bz = simplified[ii * 4 + 2];
                int bi = simplified[ii * 4 + 3];

                float maxd = 0;
                int   maxi = -1;
                int   ci, cinc, endi;

                if (bx > ax || (bx == ax && bz > az))
                {
                    cinc = 1;
                    ci   = (ai + cinc) % pn;
                    endi = bi;
                }
                else
                {
                    cinc = pn - 1;
                    ci   = (bi + cinc) % pn;
                    endi = ai;
                }

                if ((points[ci * 4 + 3] & ContourRegMask) == 0 || (points[ci * 4 + 3] & AreaBorder) != 0)
                {
                    while (ci != endi)
                    {
                        float d = DistancePtSeg(points[ci * 4 + 0], points[ci * 4 + 2], ax, az, bx, bz);
                        if (d > maxd)
                        {
                            maxd = d;
                            maxi = ci;
                        }
                        ci = (ci + cinc) % pn;
                    }
                }

                // if max deviation is larger than accepted error add new point
                float errorSqrd = maxError * maxError;
                if (maxi != -1 && maxd > (maxError * maxError))
                {
                    simplified.Resize(simplified.Size + 4);
                    int n = simplified.Size / 4;
                    for (int j = n - 1; j > i; --j)
                    {
                        simplified[j * 4 + 0] = simplified[(j - 1) * 4 + 0];
                        simplified[j * 4 + 1] = simplified[(j - 1) * 4 + 1];
                        simplified[j * 4 + 2] = simplified[(j - 1) * 4 + 2];
                        simplified[j * 4 + 3] = simplified[(j - 1) * 4 + 3];
                    }

                    simplified[(i + 1) * 4 + 0] = points[maxi * 4 + 0];
                    simplified[(i + 1) * 4 + 1] = points[maxi * 4 + 1];
                    simplified[(i + 1) * 4 + 2] = points[maxi * 4 + 2];
                    simplified[(i + 1) * 4 + 3] = maxi;
                }
                else
                {
                    i++;
                }
            }

            // split too long edges
            if (maxEdgeLen > 0 &&
                (buildFlags & (BuildContourFlags.ContourTessWallEdges | BuildContourFlags.ContourTessAreaEdges)) != 0)
            {
                for (int i = 0; i < simplified.Size / 4;)
                {
                    int ii = (i + 1) % (simplified.Size / 4);

                    int ax = simplified[i * 4 + 0];
                    int az = simplified[i * 4 + 2];
                    int ai = simplified[i * 4 + 3];

                    int bx = simplified[ii * 4 + 0];
                    int bz = simplified[ii * 4 + 2];
                    int bi = simplified[ii * 4 + 3];

                    int maxi = -1;
                    int ci   = (ai + 1) % pn;

                    bool tess = false;

                    if ((buildFlags & BuildContourFlags.ContourTessWallEdges) != 0 &&
                        (points[ci * 4 + 3] & ContourRegMask) == 0)
                    {
                        tess = true;
                    }
                    if ((buildFlags & BuildContourFlags.ContourTessAreaEdges) != 0 &&
                        (points[ci * 4 + 3] & AreaBorder) != 0)
                    {
                        tess = true;
                    }

                    if (tess)
                    {
                        int dx = bx - ax;
                        int dz = bz - az;
                        if (dx * dx + dz * dz > maxEdgeLen * maxEdgeLen)
                        {
                            int n = bi < ai ? (bi + pn - ai) : (bi - ai);
                            if (n > 1)
                            {
                                if (bx > ax || (bx == ax && bz > az))
                                {
                                    maxi = (int)(ai + n / 2f) % pn;
                                }
                                else
                                {
                                    maxi = (int)(ai + (n + 1) / 2f) % pn;
                                }
                            }
                        }
                    }

                    if (maxi != -1)
                    {
                        simplified.Resize(simplified.Size + 4);
                        int n = simplified.Size / 4;
                        for (int j = n - 1; j > i; j--)
                        {
                            simplified[j * 4 + 0] = simplified[(j - 1) * 4 + 0];
                            simplified[j * 4 + 1] = simplified[(j - 1) * 4 + 1];
                            simplified[j * 4 + 2] = simplified[(j - 1) * 4 + 2];
                            simplified[j * 4 + 3] = simplified[(j - 1) * 4 + 3];
                        }
                        simplified[(i + 1) * 4 + 0] = points[maxi * 4 + 0];
                        simplified[(i + 1) * 4 + 1] = points[maxi * 4 + 1];
                        simplified[(i + 1) * 4 + 2] = points[maxi * 4 + 2];
                        simplified[(i + 1) * 4 + 3] = maxi;
                    }
                    else
                    {
                        i++;
                    }
                }
            }

            for (int i = 0; i < simplified.Size / 4; i++)
            {
                int ai = (simplified[i * 4 + 3] + 1) % pn;
                int bi = simplified[i * 4 + 3];
                simplified[i * 4 + 3] = (points[ai * 4 + 3] & (ContourRegMask | AreaBorder)) |
                                        (points[bi * 4 + 3] & BorderVertex);
            }
        }
        public ContourSet(CompactHeightfield cfh, float maxError, int maxEdgeLen, int buildFlags = BuildContourFlags.ContourTessWallEdges)
        {
            int w          = cfh.Width;
            int h          = cfh.Height;
            int borderSize = cfh.BorderSize;

            BMin = new float[3];
            BMax = new float[3];
            Array.Copy(cfh.BMin, BMin, 3);
            Array.Copy(cfh.BMax, BMax, 3);

            if (borderSize > 0)
            {
                float pad = borderSize * cfh.Cs;
                BMin[0] += pad;
                BMin[2] += pad;
                BMax[0] -= pad;
                BMax[2] -= pad;
            }

            Cs         = cfh.Cs;
            Ch         = cfh.Ch;
            Width      = cfh.Width - cfh.BorderSize * 2;
            Height     = cfh.Height - cfh.BorderSize * 2;
            BorderSize = cfh.BorderSize;
            int maxContours = Math.Max(cfh.MaxRegions, 8);

            Conts = new Contour[maxContours];
            for (int i = 0; i < maxContours; i++)
            {
                Conts[i] = new Contour();
            }
            NConts = 0;

            char[] flags = new char[cfh.SpanCount];

            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    CompactCell c = cfh.Cells[x + y * w];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        if (i == 4782)
                        {
                            int z = 0;
                        }
                        int         res = 0;
                        CompactSpan s   = cfh.Spans[i];
                        if (s.Reg == 0 || (s.Reg & CompactHeightfield.BorderReg) != 0)
                        {
                            flags[i] = (char)0;
                            continue;
                        }
                        for (int dir = 0; dir < 4; dir++)
                        {
                            int r = 0;
                            if (s.GetCon(dir) != CompactHeightfield.NotConnected)
                            {
                                int ax = x + Helper.GetDirOffsetX(dir);
                                int ay = y + Helper.GetDirOffsetY(dir);
                                int ai = (int)cfh.Cells[ax + ay * w].Index + s.GetCon(dir);
                                r = cfh.Spans[ai].Reg;
                            }
                            if (r == cfh.Spans[i].Reg)
                            {
                                res |= (1 << dir);
                            }
                        }
                        flags[i] = (char)(res ^ 0xf);
                    }
                }
            }

            IntArray verts      = new IntArray(256);
            IntArray simplified = new IntArray(64);

            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    CompactCell c = cfh.Cells[x + y * w];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        if (flags[i] == 0 || flags[i] == 0xf)
                        {
                            flags[i] = (char)0;
                            continue;
                        }

                        int reg = cfh.Spans[i].Reg;
                        if (reg == 0 || (reg & CompactHeightfield.BorderReg) != 0)
                        {
                            continue;
                        }
                        uint area = cfh.Areas[i];

                        verts.Resize(0);
                        simplified.Resize(0);

                        WalkContour(x, y, i, cfh, ref flags, ref verts);

                        SimplifyContour(ref verts, ref simplified, maxError, maxEdgeLen, buildFlags);
                        RemoveDegenerateSegments(ref simplified);

                        if ((simplified.Size / 4) >= 3)
                        {
                            // We need more space than we allocated...
                            if (NConts >= maxContours)
                            {
                                int oldMax = maxContours;
                                maxContours *= 2;
                                Contour[] newConts = new Contour[maxContours];
                                for (int j = 0; j < maxContours; j++)
                                {
                                    newConts[j] = new Contour();
                                }
                                for (int j = 0; j < NConts; j++)
                                {
                                    newConts[j] = Conts[j];
                                }
                                Conts = newConts;
                            }
                            Contour cont = Conts[NConts++];
                            cont.NVerts = simplified.Size / 4;
                            cont.Verts  = new int[cont.NVerts * 4];
                            Array.Copy(simplified.ToArray(), cont.Verts, cont.NVerts * 4);
                            if (borderSize > 0)
                            {
                                for (int j = 0; j < cont.NVerts; j++)
                                {
                                    int v = j * 4;
                                    cont.Verts[v + 0] -= borderSize;
                                    cont.Verts[v + 2] -= borderSize;
                                }
                            }

                            cont.NRVerts = verts.Size / 4;
                            cont.RVerts  = new int[cont.NRVerts * 4];
                            Array.Copy(verts.ToArray(), cont.RVerts, cont.NRVerts * 4);
                            if (borderSize > 0)
                            {
                                for (int j = 0; j < cont.NRVerts; j++)
                                {
                                    int v = j * 4;
                                    cont.RVerts[v + 0] -= borderSize;
                                    cont.RVerts[v + 2] -= borderSize;
                                }
                            }

                            cont.Reg  = reg;
                            cont.Area = (short)area;
                        }
                    }
                }
            }

            // check and merge droppings
            for (int i = 0; i < NConts; i++)
            {
                Contour cont = Conts[i];
                if (CalcAreaOfPolygon2D(cont.Verts, cont.NVerts) < 0)
                {
                    int mergeIdx = -1;
                    for (int j = 0; j < NConts; j++)
                    {
                        if (i == j)
                        {
                            continue;
                        }
                        if (Conts[j].NVerts > 0 && Conts[j].Reg == cont.Reg)
                        {
                            if (CalcAreaOfPolygon2D(Conts[j].Verts, Conts[j].NVerts) > 0)
                            {
                                mergeIdx = j;
                                break;
                            }
                        }
                    }
                    if (mergeIdx == -1)
                    {
                        // error
                    }
                    else
                    {
                        Contour mcont = Conts[mergeIdx];
                        int     ia = 0, ib = 0;
                        GetClosestIndices(mcont.Verts, mcont.NVerts, cont.Verts, cont.NVerts, ref ia, ref ib);
                        if (ia == -1 || ib == -1)
                        {
                            // bad merge
                            continue;
                        }
                        if (!MergeContours(ref mcont, ref cont, ia, ib))
                        {
                            // Merge failed
                            continue;
                        }
                    }
                }
            }
        }
        private bool FilterSmallRegions(int minRegionArea, int mergeRegionArea, ref int[] srcReg)
        {
            int w = Width;
            int h = Height;

            int nreg = MaxRegions + 1;

            Region[] regions = new Region[nreg];

            for (int i = 0; i < nreg; i++)
            {
                regions[i] = new Region(i);
            }

            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    CompactCell c = Cells[x + y * w];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        int r = srcReg[i];
                        if (r <= 0 || r >= nreg)
                        {
                            continue;
                        }
                        Region reg = regions[r];
                        reg.SpanCount++;

                        for (int j = (int)c.Index; j < ni; j++)
                        {
                            if (i == j)
                            {
                                continue;
                            }
                            int floorId = srcReg[j];
                            if (floorId <= 0 || floorId >= nreg)
                            {
                                continue;
                            }
                            AddUniqueFloorRegion(ref reg, floorId);
                        }

                        if (reg.Connections.Size > 0)
                        {
                            continue;
                        }

                        reg.AreaType = (short)Areas[i];

                        int ndir = -1;
                        for (int dir = 0; dir < 4; dir++)
                        {
                            if (IsSolidEdge(srcReg, x, y, i, dir))
                            {
                                ndir = dir;
                                break;
                            }
                        }

                        if (ndir != -1)
                        {
                            WalkContour(x, y, i, ndir, ref srcReg, ref reg);
                        }
                    }
                }
            }

            // remove too small regions.
            IntArray stack = new IntArray(32);
            IntArray trace = new IntArray(32);

            for (int i = 0; i < nreg; i++)
            {
                Region reg = regions[i];
                if (reg.Id <= 0 || (reg.Id & BorderReg) != 0)
                {
                    continue;
                }
                if (reg.SpanCount == 0)
                {
                    continue;
                }
                if (reg.Visited)
                {
                    continue;
                }

                bool connectsToBorder = false;
                int  spanCount        = 0;
                stack.Resize(0);
                trace.Resize(0);

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

                while (stack.Size > 0)
                {
                    int ri = stack.Pop();

                    Region creg = regions[ri];
                    spanCount += creg.SpanCount;
                    trace.Push(ri);

                    for (int j = 0; j < creg.Connections.Size; j++)
                    {
                        if ((creg.Connections[j] & BorderReg) != 0)
                        {
                            connectsToBorder = true;
                            continue;
                        }
                        Region neireg = regions[creg.Connections[j]];
                        if (neireg.Visited)
                        {
                            continue;
                        }
                        if (neireg.Id <= 0 || (neireg.Id & BorderReg) != 0)
                        {
                            continue;
                        }
                        stack.Push(neireg.Id);
                        neireg.Visited = true;
                    }
                }

                // if the region's size is too small, remove it.
                if (spanCount < minRegionArea && !connectsToBorder)
                {
                    for (int j = 0; j < trace.Size; j++)
                    {
                        regions[trace[j]].SpanCount = 0;
                        regions[trace[j]].Id        = 0;
                    }
                }
            }

            int mergeCount = 0;

            do
            {
                mergeCount = 0;
                for (int i = 0; i < nreg; i++)
                {
                    Region reg = regions[i];
                    if (reg.Id <= 0 || (reg.Id & BorderReg) != 0)
                    {
                        continue;
                    }
                    if (reg.SpanCount == 0)
                    {
                        continue;
                    }

                    if (reg.SpanCount > mergeRegionArea && IsRegionConnectedToBorder(reg))
                    {
                        continue;
                    }

                    // small region with more than 1 connection
                    // or region which is not connected to a border at all.
                    // find smallest neighbor
                    int smallest = int.MaxValue;
                    int mergeId  = reg.Id;
                    for (int j = 0; j < reg.Connections.Size; j++)
                    {
                        if ((reg.Connections[j] & BorderReg) != 0)
                        {
                            continue;
                        }
                        Region mreg = regions[reg.Connections[j]];
                        if (mreg.Id <= 0 || (mreg.Id & BorderReg) != 0)
                        {
                            continue;
                        }
                        if (mreg.SpanCount < smallest && CanMergeWithRegion(reg, mreg) && CanMergeWithRegion(mreg, reg))
                        {
                            smallest = mreg.SpanCount;
                            mergeId  = mreg.Id;
                        }
                    }
                    if (mergeId != reg.Id)
                    {
                        int    oldId  = reg.Id;
                        Region target = regions[mergeId];

                        if (MergeRegions(ref target, ref reg))
                        {
                            for (int j = 0; j < nreg; j++)
                            {
                                if (regions[j].Id == 0 || (regions[j].Id & BorderReg) != 0)
                                {
                                    continue;
                                }
                                if (regions[j].Id == oldId)
                                {
                                    regions[j].Id = mergeId;
                                }
                                Region reg2 = regions[j];
                                ReplaceNeighbor(ref reg2, oldId, mergeId);
                            }
                            mergeCount++;
                        }
                    }
                }
            } while (mergeCount > 0);

            // compress regions Ids
            for (int i = 0; i < nreg; i++)
            {
                regions[i].Remap = false;
                if (regions[i].Id == 0 || (regions[i].Id & BorderReg) != 0)
                {
                    continue;
                }
                regions[i].Remap = true;
            }

            int regIdGen = 0;

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

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

            MaxRegions = regIdGen;

            // remap regions
            for (int i = 0; i < SpanCount; i++)
            {
                if ((srcReg[i] & BorderReg) == 0)
                {
                    srcReg[i] = regions[srcReg[i]].Id;
                }
            }

            return(true);
        }
        private int[] ExpandRegions(int maxIter, int level, ref int[] srcReg, ref int[] srcDist, ref int[] dstReg, ref int[] dstDist, ref IntArray stack)
        {
            int w = Width;
            int h = Height;

            stack.Resize(0);
            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    CompactCell c = Cells[x + y * w];
                    for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++)
                    {
                        if (Dist[i] >= level && srcReg[i] == 0 && Areas[i] != HeightField.NullArea)
                        {
                            stack.Push(x);
                            stack.Push(y);
                            stack.Push(i);
                        }
                    }
                }
            }

            int iter = 0;

            while (stack.Size > 0)
            {
                int failed = 0;

                Buffer.BlockCopy(srcReg, 0, dstReg, 0, sizeof(int) * SpanCount);
                Buffer.BlockCopy(srcDist, 0, dstDist, 0, sizeof(int) * SpanCount);
                //Array.Copy(srcReg, dstReg, SpanCount);
                //Array.Copy(srcDist, dstDist, SpanCount);

                for (int j = 0; j < stack.Size; j += 3)
                {
                    int x = stack[j + 0];
                    int y = stack[j + 1];
                    int i = stack[j + 2];
                    if (i < 0)
                    {
                        failed++;
                        continue;
                    }

                    int         r    = srcReg[i];
                    int         d2   = short.MaxValue;
                    uint        area = Areas[i];
                    CompactSpan s    = Spans[i];
                    for (int dir = 0; dir < 4; dir++)
                    {
                        if (s.GetCon(dir) == NotConnected)
                        {
                            continue;
                        }
                        int ax = x + Helper.GetDirOffsetX(dir);
                        int ay = y + Helper.GetDirOffsetY(dir);
                        int ai = (int)Cells[ax + ay * w].Index + s.GetCon(dir);
                        if (Areas[ai] != area)
                        {
                            continue;
                        }
                        if (srcReg[ai] > 0 && (srcReg[ai] & BorderReg) == 0)
                        {
                            if (srcDist[ai] + 2 < d2)
                            {
                                r  = srcReg[ai];
                                d2 = srcDist[ai] + 2;
                            }
                        }
                    }
                    if (r != 0)
                    {
                        stack[j + 2] = -1;
                        dstReg[i]    = r;
                        dstDist[i]   = d2;
                    }
                    else
                    {
                        failed++;
                    }
                }
                int[] temp = srcReg;
                srcReg  = dstReg;
                dstReg  = temp;
                temp    = srcDist;
                srcDist = dstDist;
                dstDist = temp;

                if (failed * 3 == stack.Size)
                {
                    break;
                }

                if (level > 0)
                {
                    ++iter;
                    if (iter >= maxIter)
                    {
                        break;
                    }
                }
            }
            return(srcReg);
        }
        private bool FloodRegion(int x, int y, int i, int level, int r, ref int[] srcReg, ref int[] srcDist, ref IntArray stack)
        {
            int  w    = Width;
            uint area = Areas[i];

            stack.Resize(0);
            stack.Push(x);
            stack.Push(y);
            stack.Push(i);
            srcReg[i]  = r;
            srcDist[i] = 0;

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

            while (stack.Size > 0)
            {
                int ci = stack.Pop();
                int cy = stack.Pop();
                int cx = stack.Pop();

                CompactSpan cs = Spans[ci];

                int ar = 0;
                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) != NotConnected)
                    {
                        int ax = cx + Helper.GetDirOffsetX(dir);
                        int ay = cy + Helper.GetDirOffsetY(dir);
                        int ai = (int)Cells[ax + ay * w].Index + cs.GetCon(dir);
                        if (Areas[ai] != area)
                        {
                            continue;
                        }
                        int nr = srcReg[ai];
                        if ((nr & BorderReg) != 0)
                        {
                            continue;
                        }
                        if (nr != 0 && nr != r)
                        {
                            ar = nr;
                        }
                        CompactSpan aspan = Spans[ai];

                        int dir2 = (dir + 1) & 0x3;
                        if (aspan.GetCon(dir2) != NotConnected)
                        {
                            int ax2 = ax + Helper.GetDirOffsetX(dir2);
                            int ay2 = ay + Helper.GetDirOffsetY(dir2);
                            int ai2 = (int)Cells[ax2 + ay2 * w].Index + aspan.GetCon(dir2);
                            if (Areas[ai2] != area)
                            {
                                continue;
                            }
                            int nr2 = srcReg[ai2];
                            if (nr2 != 0 && nr2 != r)
                            {
                                ar = nr2;
                            }
                        }
                    }
                }
                if (ar != 0)
                {
                    srcReg[ci] = 0;
                    continue;
                }
                count++;

                for (int dir = 0; dir < 4; dir++)
                {
                    if (cs.GetCon(dir) != NotConnected)
                    {
                        int ax = cx + Helper.GetDirOffsetX(dir);
                        int ay = cy + Helper.GetDirOffsetY(dir);
                        int ai = (int)Cells[ax + ay * w].Index + cs.GetCon(dir);
                        if (Areas[ai] != area)
                        {
                            continue;
                        }
                        if (Dist[ai] >= lev && srcReg[ai] == 0)
                        {
                            srcReg[ai]  = r;
                            srcDist[ai] = 0;
                            stack.Push(ax);
                            stack.Push(ay);
                            stack.Push(ai);
                        }
                    }
                }
            }
            return(count > 0);
        }