public Polygon GetPolygon(int edgeSubdivisions, float curvature)
 {
     Connector connector = new Connector ();
     for (int k=0; k<segments.Count; k++) {
         Segment s = segments [k];
         if (!s.deleted) {
             if (edgeSubdivisions>1) {
                 connector.AddRange (s.Subdivide(edgeSubdivisions, curvature));
             } else {
                 connector.Add (s);
             }
         }
     }
     return connector.ToPolygonFromLargestLineStrip ();
 }
        GameObject GenerateCellRegionSurface(int cellIndex, Material material)
        {
            if (cellIndex<0 || cellIndex>=cells.Count) return null;
            Region region = cells [cellIndex].region;

            // Calculate region's surface points
            int numSegments = region.segments.Count;
            Connector connector = new Connector();
            if (_terrain==null) {
                connector.AddRange(region.segments);
            } else {
                for (int i = 0; i<numSegments; i++) {
                    Segment s = region.segments[i];
                    SurfaceSegmentForSurface(s, connector);
                }
            }
            Geom.Polygon surfacedPolygon = connector.ToPolygonFromLargestLineStrip();
            List<Point> surfacedPoints = surfacedPolygon.contours[0].points;

            List<PolygonPoint> ppoints = new List<PolygonPoint>(surfacedPoints.Count);
            for (int k=0;k<surfacedPoints.Count;k++) {
                double x = surfacedPoints[k].x+2;
                double y = surfacedPoints[k].y+2;
                if (!IsTooNearPolygon(x, y, ppoints)) {
                    float h = _terrain!=null ? _terrain.SampleHeight(transform.TransformPoint((float)x-2, (float)y-2,0)): 0;
                    ppoints.Add (new PolygonPoint(x, y, h));
                }
            }
            Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ppoints);

            if (_terrain!=null) {
                if (steinerPoints==null) {
                    steinerPoints = new List<TriangulationPoint>(6000);
                } else {
                    steinerPoints.Clear();
                }

                float stepX = 1.0f / heightMapWidth;
                float smallStep = 1.0f / heightMapWidth;
                float y = region.rect2D.yMin + smallStep;
                float ymax = region.rect2D.yMax - smallStep;
                float[] acumY = new float[terrainRoughnessMapWidth];
                while(y<ymax) {
                    int j = (int)((y + 0.5f) * terrainRoughnessMapHeight); // * heightMapHeight)) / TERRAIN_CHUNK_SIZE;
                    if (j>=0) {
                    if (j>=terrainRoughnessMapHeight) j=terrainRoughnessMapHeight-1;
                    float sy = y + 2;
                    float xin = GetFirstPointInRow(sy, ppoints) + smallStep;
                    float xout = GetLastPointInRow(sy, ppoints) - smallStep;
                    int k0 = -1;
                    for (float x = xin; x<xout; x+=stepX) {
                        int k = (int)((x + 0.5f) * terrainRoughnessMapWidth); //)) / TERRAIN_CHUNK_SIZE;
                        if (k>=terrainRoughnessMapWidth) k=terrainRoughnessMapWidth-1;
                        if (k0!=k) {
                            k0=k;
                            stepX = terrainRoughnessMap[j,k];
                            if (acumY[k] >= stepX) acumY[k] = 0;
                            acumY[k] += smallStep;
                        }
                        if (acumY[k] >= stepX) {
                            // Gather precision height
                            float h = _terrain.SampleHeight (transform.TransformPoint(x,y,0));
                            float htl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y+smallStep, 0));
                            if (htl>h) h = htl;
                            float htr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y+smallStep, 0));
                            if (htr>h) h = htr;
                            float hbr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y-smallStep, 0));
                            if (hbr>h) h = hbr;
                            float hbl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y-smallStep, 0));
                            if (hbl>h) h = hbl;
                            steinerPoints.Add (new PolygonPoint (x+2, sy, h));
                        }
                    }
                    }
                    y += smallStep;
                    if (steinerPoints.Count>80000) {
                        break;
                    }
                }
                poly.AddSteinerPoints(steinerPoints);
            }

            P2T.Triangulate(poly);

            // Calculate & optimize mesh data
            int pointCount = poly.Triangles.Count*3;
            List<Vector3> meshPoints = new List<Vector3> (pointCount);
            int[] triNew = new int[pointCount];
            if (surfaceMeshHit == null)
                surfaceMeshHit = new Dictionary<TriangulationPoint, int> (2000);
            else
                surfaceMeshHit.Clear ();

            int triNewIndex =-1;
            int newPointsCount = -1;

            if (_gridNormalOffset>0) {
                for (int k=0;k<poly.Triangles.Count;k++) {
                    DelaunayTriangle dt = poly.Triangles[k];
                    TriangulationPoint p = dt.Points [0];
                    if (surfaceMeshHit.ContainsKey (p)) {
                        triNew [++triNewIndex] = surfaceMeshHit [p];
                    } else {
                        Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf);
                        np += transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(np.x+0.5f,np.y+0.5f)) * _gridNormalOffset;
                        meshPoints.Add (np);
                        surfaceMeshHit.Add (p, ++newPointsCount);
                        triNew [++triNewIndex] = newPointsCount;
                    }
                    p = dt.Points [2];
                    if (surfaceMeshHit.ContainsKey (p)) {
                        triNew [++triNewIndex] = surfaceMeshHit [p];
                    } else {
                        Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf);
                        np += transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(np.x+0.5f,np.y+0.5f)) * _gridNormalOffset;
                        meshPoints.Add (np);
                        surfaceMeshHit.Add (p, ++newPointsCount);
                        triNew [++triNewIndex] = newPointsCount;
                    }
                    p = dt.Points [1];
                    if (surfaceMeshHit.ContainsKey (p)) {
                        triNew [++triNewIndex] = surfaceMeshHit [p];
                    } else {
                        Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf);
                        np += transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(np.x+0.5f,np.y+0.5f)) * _gridNormalOffset;
                        meshPoints.Add (np);
                        surfaceMeshHit.Add (p, ++newPointsCount);
                        triNew [++triNewIndex] = newPointsCount;
                    }
                }
            } else {
                for (int k=0;k<poly.Triangles.Count;k++) {
                    DelaunayTriangle dt = poly.Triangles[k];
                    TriangulationPoint p = dt.Points [0];
                    if (surfaceMeshHit.ContainsKey (p)) {
                        triNew [++triNewIndex] = surfaceMeshHit [p];
                    } else {
                        Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf);
                        meshPoints.Add (np);
                        surfaceMeshHit.Add (p, ++newPointsCount);
                        triNew [++triNewIndex] = newPointsCount;
                    }
                    p = dt.Points [2];
                    if (surfaceMeshHit.ContainsKey (p)) {
                        triNew [++triNewIndex] = surfaceMeshHit [p];
                    } else {
                        Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf);
                        meshPoints.Add (np);
                        surfaceMeshHit.Add (p, ++newPointsCount);
                        triNew [++triNewIndex] = newPointsCount;
                    }
                    p = dt.Points [1];
                    if (surfaceMeshHit.ContainsKey (p)) {
                        triNew [++triNewIndex] = surfaceMeshHit [p];
                    } else {
                        Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf);
                        meshPoints.Add (np);
                        surfaceMeshHit.Add (p, ++newPointsCount);
                        triNew [++triNewIndex] = newPointsCount;
                    }
                }
            }

            int cacheIndex = GetCacheIndexForCellRegion (cellIndex);
            string cacheIndexSTR = cacheIndex.ToString();
            // Deletes potential residual surface
            Transform t = surfacesLayer.transform.FindChild(cacheIndexSTR);
            if (t!=null) DestroyImmediate(t.gameObject);
            GameObject surf = Drawing.CreateSurface (cacheIndexSTR, meshPoints.ToArray(), triNew, material);
            _lastVertexCount += surf.GetComponent<MeshFilter>().sharedMesh.vertexCount;
            surf.transform.SetParent (surfacesLayer.transform, false);
            surf.transform.localPosition = Vector3.zero;
            surf.layer = gameObject.layer;
            if (surfaces.ContainsKey(cacheIndex)) surfaces.Remove(cacheIndex);
            surfaces.Add (cacheIndex, surf);
            return surf;
        }
        void SurfaceSegmentForSurface(Segment s, Connector connector)
        {
            // trace the line until roughness is exceeded
            double dist = s.magnitude; // (float)Math.Sqrt ( (p1.x-p0.x)*(p1.x-p0.x) + (p1.y-p0.y)*(p1.y-p0.y));
            Point direction = s.end - s.start;

            int numSteps = (int)(dist / MIN_VERTEX_DISTANCE);
            Point t0 = s.start;
            float h0 = _terrain.SampleHeight(transform.TransformPoint(t0.vector3));
            Point ta = t0;
            float h1;
            for (int i=1;i<numSteps;i++) {
                Point t1 = s.start + direction * i / numSteps;
                h1 = _terrain.SampleHeight(transform.TransformPoint(t1.vector3));
                if (h0 < h1 || h0-h1 > effectiveRoughness) { //-effectiveRoughness) {
                    if (t0!=ta) {
                        Segment s0 = new Segment(t0, ta, s.border);
                        connector.Add (s0);
                        Segment s1 = new Segment(ta, t1, s.border);
                        connector.Add (s1);
                    } else {
                        Segment s0 = new Segment(t0, t1, s.border);
                        connector.Add (s0);
                    }
                    t0 = t1;
                    h0 = h1;
                }
                ta = t1;
            }
            // Add last point
            Segment finalSeg = new Segment(t0, s.end, s.border);
            connector.Add (finalSeg);
        }
        void FindTerritoryFrontiers()
        {
            if (territories==null) return;

            if (territoryFrontiers == null) {
                territoryFrontiers = new List<Segment>(cellNeighbourHit.Count);
            } else {
                territoryFrontiers.Clear ();
            }
            if (territoryNeighbourHit == null) {
                territoryNeighbourHit = new Dictionary<Segment, Region> (50000);
            } else {
                territoryNeighbourHit.Clear ();
            }
            Connector[] connectors = new Connector[territories.Count];
            for (int k=0;k<territories.Count;k++)
                connectors[k] = new Connector();

            for (int k=0; k<cells.Count; k++) {
                Cell cell = cells [k];
                Region region = cell.region;
                int numSegments = region.segments.Count;
                for (int i = 0; i<numSegments; i++) {
                    Segment seg = region.segments[i];
                    if (seg.border) {
                        territoryFrontiers.Add (seg);
                        int territory1 = cell.territoryIndex;
                        connectors[territory1].Add (seg);
                        continue;
                    }
                    if (territoryNeighbourHit.ContainsKey (seg)) {
                        Region neighbour = territoryNeighbourHit [seg];
                        Cell neighbourCell = (Cell)neighbour.entity;
                        if (neighbourCell.territoryIndex!=cell.territoryIndex) {
                            territoryFrontiers.Add (seg);
                            int territory1 = cell.territoryIndex;
                            connectors[territory1].Add (seg);
                            int territory2 = neighbourCell.territoryIndex;
                            connectors[territory2].Add (seg);
                        }
                    } else {
                        territoryNeighbourHit.Add (seg, region);
                    }
                }
            }

            for (int k=0;k<territories.Count;k++)
                territories[k].polygon = connectors[k].ToPolygonFromLargestLineStrip();
        }
        void SetupHexagonalGrid()
        {
            // Make cell regions: we assume cells have only 1 region but that can change in the future
            int l = _numCells;
            int qx, qy;
            int q = (int)(Math.Sqrt (l));
            q = q * 12 / 13;
            if (q<1) q= 1;
            qx=qy=q;
            int qx2 = qx * 4 / 3;

            double stepX = (transform.localScale.y / transform.localScale.x) / qx;
            double stepY = (transform.localScale.x / transform.localScale.y) / qy;

            double halfStepX = stepX*0.5;
            double halfStepY = stepY*0.5;

            Segment [,,] sides = new Segment[qx2,qy,6]; // 0 = left-up, 1 = top, 2 = right-up, 3 = right-down, 4 = down, 5 = left-down
            int c = -1;
            int subdivisions = goodGridCurvature > 0 ? 3: 1;
            for (int j=0;j<qy;j++) {
                for (int k=0;k<qx2;k++) {
                    Point center = new Point((double)k/qx-0.5+halfStepX,(double)j/qy-0.5+halfStepY);
                    center.x -= k *  halfStepX/2;
                    Cell cell = new Cell( (++c).ToString(), new Vector2((float)center.x, (float)center.y));

                    double offsetY = (k % 2==0) ? 0: -halfStepY;

                    Segment leftUp =  (k>0 && offsetY<0) ? sides[k-1, j, 3]: new Segment(center.Offset(-halfStepX, offsetY), center.Offset(-halfStepX/2, halfStepY + offsetY), k==0 || (j==qy-1 && offsetY==0));
                    sides[k, j, 0] = leftUp;

                    Segment top = new Segment(center.Offset(-halfStepX/2, halfStepY + offsetY), center.Offset(halfStepX/2, halfStepY + offsetY), j==qy-1);
                    sides[k, j, 1] = top;

                    Segment rightUp = new Segment(center.Offset(halfStepX/2, halfStepY + offsetY), center.Offset(halfStepX, offsetY), k==qx2-1 || (j==qy-1 && offsetY==0));
                    sides[k, j, 2] = rightUp;

                    Segment rightDown = (j > 0 && k<qx2-1 && offsetY<0) ? sides[k+1,j-1,0]: new Segment(center.Offset(halfStepX, offsetY), center.Offset(halfStepX/2, -halfStepY + offsetY), (j==0 && offsetY<0)|| k==qx2-1);
                    sides[k, j, 3] = rightDown;

                    Segment bottom = j>0 ? sides[k, j-1, 1] : new Segment(center.Offset(halfStepX/2, -halfStepY + offsetY), center.Offset(-halfStepX/2, -halfStepY +offsetY), true);
                    sides[k, j, 4] = bottom;

                    Segment leftDown;
                    if (offsetY<0 && j>0) {
                        leftDown = sides[k-1, j-1, 2];
                    } else if (offsetY==0 && k>0) {
                        leftDown = sides[k-1, j, 2];
                    } else {
                        leftDown = new Segment(center.Offset(-halfStepX/2, -halfStepY+offsetY), center.Offset(-halfStepX, offsetY), true);
                    }
                    sides[k, j, 5] = leftDown;

                    if (j==0) {
            //						leftDown.CropBottom();
            //						bottom.CropBottom();
            //						rightDown.CropBottom();
                    }
                    if (k==qx2-1) {
                        top.CropRight();
                        rightUp.CropRight();
                        rightDown.CropRight();
                        bottom.CropRight();
                    }

                    Region cr = new Region (cell);
                    if (subdivisions>1) {
                        if (!top.deleted) cr.segments.AddRange (top.Subdivide(subdivisions, _gridCurvature));
                        if (!rightUp.deleted) cr.segments.AddRange (rightUp.Subdivide(subdivisions, _gridCurvature));
                        if (!rightDown.deleted) cr.segments.AddRange (rightDown.Subdivide(subdivisions, _gridCurvature));
                        if (!bottom.deleted) cr.segments.AddRange (bottom.Subdivide(subdivisions, _gridCurvature));
                        if (!leftDown.deleted) cr.segments.AddRange (leftDown.Subdivide(subdivisions, _gridCurvature));
                        if (!leftUp.deleted) cr.segments.AddRange (leftUp.Subdivide(subdivisions, _gridCurvature));
                    } else {
                        if (!top.deleted) cr.segments.Add (top);
                        if (!rightUp.deleted) cr.segments.Add (rightUp);
                        if (!rightDown.deleted) cr.segments.Add (rightDown);
                        if (!bottom.deleted) cr.segments.Add (bottom);
                        if (!leftDown.deleted) cr.segments.Add (leftDown);
                        if (!leftUp.deleted) cr.segments.Add (leftUp);
                    }
                    Connector connector = new Connector();
                    connector.AddRange(cr.segments);
                    cr.polygon = connector.ToPolygon(); // FromLargestLineStrip();
                    if (cr.polygon!=null) {
                        cell.region = cr;
                        cells.Add (cell);
                    }
                }
            }
        }
        void SetupBoxGrid(bool strictQuads)
        {
            // Make cell regions: we assume cells have only 1 region but that can change in the future
            int l = _numCells;
            int qx, qy;
            int q = (int)(Math.Sqrt (l));
            if (strictQuads) {
                qx=qy=q;
            } else {
                qx=l;
                qy=1;
                if (q<1) q=1;
                if ( (int)(q*q) != l) { // not squared
                    if (!GetTwoFactors(l, out qx, out qy)) {
                        // if number > 10 and it's prime, reduce by one so we can avoid ugly accordian grids
                        if (l>10) GetTwoFactors(l-1, out qx, out qy);
                    }
                } else {
                    qx = qy = q;
                }
            }
            double stepX = (transform.localScale.y / transform.localScale.x) / qx;
            double stepY = (transform.localScale.x / transform.localScale.y) / qy;

            double halfStepX = stepX*0.5;
            double halfStepY = stepY*0.5;

            Segment [,,] sides = new Segment[qx,qy,4]; // 0 = left, 1 = top, 2 = right, 3 = bottom
            int c = -1;
            int subdivisions = goodGridCurvature > 0 ? 3: 1;
            for (int k=0;k<qx;k++) {
                for (int j=0;j<qy;j++) {
                    Point center = new Point((double)k/qx-0.5+halfStepX,(double)j/qy-0.5+halfStepY);
                    Cell cell = new Cell( (++c).ToString(), new Vector2((float)center.x, (float)center.y));

                    Segment left = k>0 ? sides[k-1, j, 2] : new Segment(center.Offset(-halfStepX, -halfStepY), center.Offset(-halfStepX, halfStepY), true);
                    sides[k, j, 0] = left;

                    Segment top = new Segment(center.Offset(-halfStepX, halfStepY), center.Offset(halfStepX, halfStepY), j==qy-1);
                    sides[k, j, 1] = top;

                    Segment right = new Segment(center.Offset(halfStepX, halfStepY), center.Offset(halfStepX, -halfStepY), k==qx-1);
                    sides[k, j, 2] = right;

                    Segment bottom = j>0 ? sides[k, j-1, 1] : new Segment(center.Offset(halfStepX, -halfStepY), center.Offset(-halfStepX, -halfStepY), true);
                    sides[k, j, 3] = bottom;

                    Region cr = new Region (cell);
                    if (subdivisions>1) {
                        cr.segments.AddRange (top.Subdivide(subdivisions, _gridCurvature));
                        cr.segments.AddRange (right.Subdivide(subdivisions, _gridCurvature));
                        cr.segments.AddRange (bottom.Subdivide(subdivisions, _gridCurvature));
                        cr.segments.AddRange (left.Subdivide(subdivisions, _gridCurvature));
                    } else {
                        cr.segments.Add (top);
                        cr.segments.Add (right);
                        cr.segments.Add (bottom);
                        cr.segments.Add (left);
                    }
                    Connector connector = new Connector();
                    connector.AddRange(cr.segments);
                    cr.polygon = connector.ToPolygon(); // FromLargestLineStrip();
                    if (cr.polygon!=null) {
                        cell.region = cr;
                        cells.Add (cell);
                    }
                }
            }
        }
        GameObject GenerateTerritoryRegionSurface(int territoryIndex, Material material, Vector2 textureScale, Vector2 textureOffset, float textureRotation)
        {
            if (territoryIndex<0 || territoryIndex>=territories.Count) return null;
            Region region = territories [territoryIndex].region;

            // Calculate region's surface points
            int numSegments = region.segments.Count;
            Connector connector = new Connector();
            if (_terrain==null) {
                connector.AddRange(region.segments);
            } else {
                for (int i = 0; i<numSegments; i++) {
                    Segment s = region.segments[i];
                    SurfaceSegmentForSurface(s, connector);
                }
            }
            Geom.Polygon surfacedPolygon = connector.ToPolygonFromLargestLineStrip();
            List<Point> surfacedPoints = surfacedPolygon.contours[0].points;

            List<PolygonPoint> ppoints = new List<PolygonPoint>(surfacedPoints.Count);
            for (int k=0;k<surfacedPoints.Count;k++) {
                double x = surfacedPoints[k].x+2;
                double y = surfacedPoints[k].y+2;
                if (!IsTooNearPolygon(x, y, ppoints)) {
                    float h = _terrain!=null ? _terrain.SampleHeight(transform.TransformPoint((float)x-2, (float)y-2,0)): 0;
                    ppoints.Add (new PolygonPoint(x, y, h));
                }
            }
            Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ppoints);

            if (_terrain!=null) {

                if (steinerPoints==null) {
                    steinerPoints = new List<TriangulationPoint>(6000);
                } else {
                    steinerPoints.Clear();
                }

                float stepX = 1.0f / heightMapWidth;
                float smallStep = 1.0f / heightMapWidth;
                float y = region.rect2D.yMin + smallStep;
                float ymax = region.rect2D.yMax - smallStep;
                float[] acumY = new float[terrainRoughnessMapWidth];
                while(y<ymax) {
                    int j = (int)((y + 0.5f) * terrainRoughnessMapHeight); // * heightMapHeight)) / TERRAIN_CHUNK_SIZE;
                    if (j>=terrainRoughnessMapHeight) j=terrainRoughnessMapHeight-1;
                    float sy = y + 2;
                    float xin = GetFirstPointInRow(sy, ppoints) + smallStep;
                    float xout = GetLastPointInRow(sy, ppoints) - smallStep;
                    int k0 = -1;
                    for (float x = xin; x<xout; x+=stepX) {
                        int k = (int)((x + 0.5f) * terrainRoughnessMapWidth); //)) / TERRAIN_CHUNK_SIZE;
                        if (k>=terrainRoughnessMapWidth) k=terrainRoughnessMapWidth-1;
                        if (k0!=k) {
                            k0=k;
                            stepX = terrainRoughnessMap[j,k];
                            if (acumY[k] >= stepX) acumY[k] = 0;
                            acumY[k] += smallStep;
                        }
                        if (acumY[k] >= stepX) {
                            // Gather precision height
                            float h = _terrain.SampleHeight (transform.TransformPoint(x,y,0));
                            float htl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y+smallStep, 0));
                            if (htl>h) h = htl;
                            float htr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y+smallStep, 0));
                            if (htr>h) h = htr;
                            float hbr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y-smallStep, 0));
                            if (hbr>h) h = hbr;
                            float hbl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y-smallStep, 0));
                            if (hbl>h) h = hbl;
                            steinerPoints.Add (new PolygonPoint (x+2, sy, h));
                        }
                    }
                    y += smallStep;
                    if (steinerPoints.Count>80000) {
                        break;
                    }
                }
                poly.AddSteinerPoints(steinerPoints);
            }

            P2T.Triangulate(poly);

            Vector3[] revisedSurfPoints = new Vector3[poly.Triangles.Count*3];

            if (_gridNormalOffset>0) {
                for (int k=0;k<poly.Triangles.Count;k++) {
                    DelaunayTriangle dt = poly.Triangles[k];
                    float x = dt.Points[0].Xf-2;
                    float y = dt.Points[0].Yf-2;
                    float z = -dt.Points[0].Zf;
                    Vector3 nd = transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(x+0.5f,y+0.5f)) * _gridNormalOffset;
                    revisedSurfPoints[k*3].x = x + nd.x;
                    revisedSurfPoints[k*3].y = y + nd.y;
                    revisedSurfPoints[k*3].z = z + nd.z;

                    x = dt.Points[2].Xf-2;
                    y = dt.Points[2].Yf-2;
                    z = -dt.Points[2].Zf;
                    nd = transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(x+0.5f,y+0.5f)) * _gridNormalOffset;
                    revisedSurfPoints[k*3+1].x = x + nd.x;
                    revisedSurfPoints[k*3+1].y = y + nd.y;
                    revisedSurfPoints[k*3+1].z = z + nd.z;

                    x = dt.Points[1].Xf-2;
                    y = dt.Points[1].Yf-2;
                    z = -dt.Points[1].Zf;
                    nd = transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(x+0.5f,y+0.5f)) * _gridNormalOffset;
                    revisedSurfPoints[k*3+2].x = x + nd.x;
                    revisedSurfPoints[k*3+2].y = y + nd.y;
                    revisedSurfPoints[k*3+2].z = z + nd.z;
                }
            } else {
                for (int k=0;k<poly.Triangles.Count;k++) {
                    DelaunayTriangle dt = poly.Triangles[k];
                    revisedSurfPoints[k*3].x = dt.Points[0].Xf-2;
                    revisedSurfPoints[k*3].y = dt.Points[0].Yf-2;
                    revisedSurfPoints[k*3].z = -dt.Points[0].Zf;
                    revisedSurfPoints[k*3+1].x = dt.Points[2].Xf-2;
                    revisedSurfPoints[k*3+1].y = dt.Points[2].Yf-2;
                    revisedSurfPoints[k*3+1].z = -dt.Points[2].Zf;
                    revisedSurfPoints[k*3+2].x = dt.Points[1].Xf-2;
                    revisedSurfPoints[k*3+2].y = dt.Points[1].Yf-2;
                    revisedSurfPoints[k*3+2].z = -dt.Points[1].Zf;
                }
            }
            int cacheIndex = GetCacheIndexForTerritoryRegion (territoryIndex);
            string cacheIndexSTR = cacheIndex.ToString();
            // Deletes potential residual surface
            Transform t = surfacesLayer.transform.FindChild(cacheIndexSTR);
            if (t!=null) DestroyImmediate(t.gameObject);
            GameObject surf = Drawing.CreateSurface (cacheIndexSTR, revisedSurfPoints, material);
            surf.transform.SetParent (surfacesLayer.transform, false);
            surf.transform.localPosition = Vector3.zero;
            surf.layer = gameObject.layer;
            if (surfaces.ContainsKey(cacheIndex)) surfaces.Remove(cacheIndex);
            surfaces.Add (cacheIndex, surf);

            return surf;
        }
        Polygon ComputeInternal(PolygonOp operation)
        {
            Polygon result = null;
            sortedEvents = new List<SweepEvent>();

            // Init event queue
            eventQueue = new EventQueue();

            // Test 1 for trivial result case
            if (subject.contours.Count * clipping.contours.Count == 0) {
                if (operation == PolygonOp.DIFFERENCE)
                    result = subject;
                else if (operation == PolygonOp.UNION || operation == PolygonOp.XOR)
                    result = (subject.contours.Count == 0) ? clipping : subject;
                return result;
            }

            // Test 2 for trivial result case
            Rectangle subjectBB = subject.boundingBox;
            Rectangle clippingBB = clipping.boundingBox;
            if (!subjectBB.Intersects(clippingBB)) {
                if (operation == PolygonOp.DIFFERENCE)
                    result = subject;
                if (operation == PolygonOp.UNION || operation == PolygonOp.XOR) {
                    result = subject;
                    foreach(Contour c in clipping.contours)
                        result.AddContour(c);
                }

                return result;
            }

            // Add each segment to the eventQueue, sorted from left to right.
            for(int k=0;k<subject.contours.Count;k++) {
                Contour sCont = subject.contours[k];
                for (int pParse1=0;pParse1<sCont.points.Count;pParse1++)
                    ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT);
            }

            for(int k=0;k<clipping.contours.Count;k++) {
                Contour cCont = clipping.contours[k];
                for (int pParse2=0;pParse2<cCont.points.Count;pParse2++)
                    ProcessSegment(cCont.GetSegment(pParse2), PolygonType.CLIPPING);
            }

            Connector connector = new Connector();

            // This is the SweepLine. That is, we go through all the polygon edges
            // by sweeping from left to right.
            SweepEventSet S = new SweepEventSet();

            double MINMAX_X = Math.Min(subjectBB.right, clippingBB.right) + Point.PRECISION;

            SweepEvent prev, next;

            int panicCounter = 0; // This is a safety check to prevent infinite loops (very rare but could happen due to floating-point issues with a high number of points)

            while (!eventQueue.isEmpty)
            {
                if (panicCounter++>10000) {
                    Debug.Log("PANIC!");
                    break;
                }
                prev = null;
                next = null;

                SweepEvent e = eventQueue.Dequeue();

                if ((operation == PolygonOp.INTERSECTION && e.p.x > MINMAX_X) || (operation == PolygonOp.DIFFERENCE && e.p.x > subjectBB.right + Point.PRECISION))
                    return connector.ToPolygonFromLargestLineStrip();

                if (operation == PolygonOp.UNION && e.p.x > MINMAX_X) {
                    // add all the non-processed line segments to the result
                    if (!e.isLeft)
                        connector.Add(e.segment);

                    while (!eventQueue.isEmpty) {
                        e = eventQueue.Dequeue();
                        if (!e.isLeft)
                            connector.Add(e.segment);
                    }
                    return connector.ToPolygonFromLargestLineStrip();
                }

                if (e.isLeft) {  // the line segment must be inserted into S
                    int pos = S.Insert(e);

                    prev = (pos > 0) ? S.eventSet[pos - 1] : null;
                    next = (pos < S.eventSet.Count - 1) ? S.eventSet[pos + 1] : null;

                    if (prev == null) {
                        e.inside = e.inOut = false;
                    } else if (prev.edgeType != EdgeType.NORMAL) {
                        if (pos - 2 < 0) { // e overlaps with prev
                            // Not sure how to handle the case when pos - 2 < 0, but judging
                            // from the C++ implementation this looks like how it should be handled.
                            e.inside = e.inOut = false;
                            if (prev.polygonType != e.polygonType)
                                e.inside = true;
                            else
                                e.inOut = true;
                        } else {
                            SweepEvent prevTwo = S.eventSet[pos - 2];
                            if (prev.polygonType == e.polygonType) {
                                e.inOut = !prev.inOut;
                                e.inside = !prevTwo.inOut;
                            } else {
                                e.inOut = !prevTwo.inOut;
                                e.inside = !prev.inOut;
                            }
                        }
                    } else if (e.polygonType == prev.polygonType) {
                        e.inside = prev.inside;
                        e.inOut = !prev.inOut;
                    } else {
                        e.inside = !prev.inOut;
                        e.inOut = prev.inside;
                    }

                    // Process a possible intersection between "e" and its next neighbor in S
                    if (next != null)
                        PossibleIntersection(e, next);

                    // Process a possible intersection between "e" and its previous neighbor in S
                    if (prev != null)
                        PossibleIntersection(prev, e);
                } else { // the line segment must be removed from S

                    // Get the next and previous line segments to "e" in S
                    int otherPos = -1;
                    for (int evt=0;evt<S.eventSet.Count;evt++) {
                        if (e.otherSE.Equals(S.eventSet[evt])) {
                            otherPos = evt;
                            break;
                        }
                    }
                    if (otherPos != -1) {
                        prev = (otherPos > 0) ? S.eventSet[otherPos - 1] : null;
                        next = (otherPos < S.eventSet.Count - 1) ? S.eventSet[otherPos + 1] : null;
                    }

                    switch (e.edgeType) {
                    case EdgeType.NORMAL:
                        switch (operation) {
                        case PolygonOp.INTERSECTION:
                            if (e.otherSE.inside)
                                connector.Add(e.segment);
                            break;
                        case PolygonOp.UNION:
                            if (!e.otherSE.inside)
                                connector.Add(e.segment);
                            break;
                        case PolygonOp.DIFFERENCE:
                            if ((e.polygonType == PolygonType.SUBJECT && !e.otherSE.inside) || (e.polygonType == PolygonType.CLIPPING && e.otherSE.inside))
                                connector.Add(e.segment);
                            break;
                        case PolygonOp.XOR:
                            connector.Add (e.segment);
                            break;
                        }
                        break;
                    case EdgeType.SAME_TRANSITION:
                        if (operation == PolygonOp.INTERSECTION || operation == PolygonOp.UNION)
                            connector.Add(e.segment);
                        break;
                    case EdgeType.DIFFERENT_TRANSITION:
                        if (operation == PolygonOp.DIFFERENCE)
                            connector.Add(e.segment);
                        break;
                    }

                    if (otherPos != -1)
                        S.Remove(S.eventSet[otherPos]);

                    if (next != null && prev != null)
                        PossibleIntersection(prev, next);
                }
            }

            return connector.ToPolygonFromLargestLineStrip();
        }
Beispiel #9
0
        Polygon ComputeInternal(PolygonOp operation)
        {
            Polygon result = null;

            sortedEvents = new List <SweepEvent>();

            // Init event queue
            eventQueue = new EventQueue();

            // Test 1 for trivial result case
            if (subject.contours.Count * clipping.contours.Count == 0)
            {
                if (operation == PolygonOp.DIFFERENCE)
                {
                    result = subject;
                }
                else if (operation == PolygonOp.UNION || operation == PolygonOp.XOR)
                {
                    result = (subject.contours.Count == 0) ? clipping : subject;
                }
                return(result);
            }

            // Test 2 for trivial result case
            Rectangle subjectBB  = subject.boundingBox;
            Rectangle clippingBB = clipping.boundingBox;

            if (!subjectBB.Intersects(clippingBB))
            {
                if (operation == PolygonOp.DIFFERENCE)
                {
                    result = subject;
                }
                if (operation == PolygonOp.UNION || operation == PolygonOp.XOR)
                {
                    result = subject;
                    foreach (Contour c in clipping.contours)
                    {
                        result.AddContour(c);
                    }
                }

                return(result);
            }

            // Add each segment to the eventQueue, sorted from left to right.
            for (int k = 0; k < subject.contours.Count; k++)
            {
                Contour sCont = subject.contours[k];
                for (int pParse1 = 0; pParse1 < sCont.points.Count; pParse1++)
                {
                    ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT);
                }
            }

            for (int k = 0; k < clipping.contours.Count; k++)
            {
                Contour cCont = clipping.contours[k];
                for (int pParse2 = 0; pParse2 < cCont.points.Count; pParse2++)
                {
                    ProcessSegment(cCont.GetSegment(pParse2), PolygonType.CLIPPING);
                }
            }

            Connector connector = new Connector();

            // This is the SweepLine. That is, we go through all the polygon edges
            // by sweeping from left to right.
            SweepEventSet S = new SweepEventSet();

            double MINMAX_X = Math.Min(subjectBB.right, clippingBB.right) + Point.PRECISION;

            SweepEvent prev, next;

            int panicCounter = 0;             // This is a safety check to prevent infinite loops (very rare but could happen due to floating-point issues with a high number of points)

            while (!eventQueue.isEmpty)
            {
                if (panicCounter++ > 10000)
                {
                    Debug.Log("PANIC!");
                    break;
                }
                prev = null;
                next = null;

                SweepEvent e = eventQueue.Dequeue();

                if ((operation == PolygonOp.INTERSECTION && e.p.x > MINMAX_X) || (operation == PolygonOp.DIFFERENCE && e.p.x > subjectBB.right + Point.PRECISION))
                {
                    return(connector.ToPolygonFromLargestLineStrip());
                }

                if (operation == PolygonOp.UNION && e.p.x > MINMAX_X)
                {
                    // add all the non-processed line segments to the result
                    if (!e.isLeft)
                    {
                        connector.Add(e.segment);
                    }

                    while (!eventQueue.isEmpty)
                    {
                        e = eventQueue.Dequeue();
                        if (!e.isLeft)
                        {
                            connector.Add(e.segment);
                        }
                    }
                    return(connector.ToPolygonFromLargestLineStrip());
                }

                if (e.isLeft)                    // the line segment must be inserted into S
                {
                    int pos = S.Insert(e);

                    prev = (pos > 0) ? S.eventSet[pos - 1] : null;
                    next = (pos < S.eventSet.Count - 1) ? S.eventSet[pos + 1] : null;

                    if (prev == null)
                    {
                        e.inside = e.inOut = false;
                    }
                    else if (prev.edgeType != EdgeType.NORMAL)
                    {
                        if (pos - 2 < 0)                           // e overlaps with prev
                        // Not sure how to handle the case when pos - 2 < 0, but judging
                        // from the C++ implementation this looks like how it should be handled.
                        {
                            e.inside = e.inOut = false;
                            if (prev.polygonType != e.polygonType)
                            {
                                e.inside = true;
                            }
                            else
                            {
                                e.inOut = true;
                            }
                        }
                        else
                        {
                            SweepEvent prevTwo = S.eventSet[pos - 2];
                            if (prev.polygonType == e.polygonType)
                            {
                                e.inOut  = !prev.inOut;
                                e.inside = !prevTwo.inOut;
                            }
                            else
                            {
                                e.inOut  = !prevTwo.inOut;
                                e.inside = !prev.inOut;
                            }
                        }
                    }
                    else if (e.polygonType == prev.polygonType)
                    {
                        e.inside = prev.inside;
                        e.inOut  = !prev.inOut;
                    }
                    else
                    {
                        e.inside = !prev.inOut;
                        e.inOut  = prev.inside;
                    }

                    // Process a possible intersection between "e" and its next neighbor in S
                    if (next != null)
                    {
                        PossibleIntersection(e, next);
                    }

                    // Process a possible intersection between "e" and its previous neighbor in S
                    if (prev != null)
                    {
                        PossibleIntersection(prev, e);
                    }
                }
                else                     // the line segment must be removed from S

                // Get the next and previous line segments to "e" in S
                {
                    int otherPos = -1;
                    for (int evt = 0; evt < S.eventSet.Count; evt++)
                    {
                        if (e.otherSE.Equals(S.eventSet[evt]))
                        {
                            otherPos = evt;
                            break;
                        }
                    }
                    if (otherPos != -1)
                    {
                        prev = (otherPos > 0) ? S.eventSet[otherPos - 1] : null;
                        next = (otherPos < S.eventSet.Count - 1) ? S.eventSet[otherPos + 1] : null;
                    }

                    switch (e.edgeType)
                    {
                    case EdgeType.NORMAL:
                        switch (operation)
                        {
                        case PolygonOp.INTERSECTION:
                            if (e.otherSE.inside)
                            {
                                connector.Add(e.segment);
                            }
                            break;

                        case PolygonOp.UNION:
                            if (!e.otherSE.inside)
                            {
                                connector.Add(e.segment);
                            }
                            break;

                        case PolygonOp.DIFFERENCE:
                            if ((e.polygonType == PolygonType.SUBJECT && !e.otherSE.inside) || (e.polygonType == PolygonType.CLIPPING && e.otherSE.inside))
                            {
                                connector.Add(e.segment);
                            }
                            break;

                        case PolygonOp.XOR:
                            connector.Add(e.segment);
                            break;
                        }
                        break;

                    case EdgeType.SAME_TRANSITION:
                        if (operation == PolygonOp.INTERSECTION || operation == PolygonOp.UNION)
                        {
                            connector.Add(e.segment);
                        }
                        break;

                    case EdgeType.DIFFERENT_TRANSITION:
                        if (operation == PolygonOp.DIFFERENCE)
                        {
                            connector.Add(e.segment);
                        }
                        break;
                    }

                    if (otherPos != -1)
                    {
                        S.Remove(S.eventSet[otherPos]);
                    }

                    if (next != null && prev != null)
                    {
                        PossibleIntersection(prev, next);
                    }
                }
            }

            return(connector.ToPolygonFromLargestLineStrip());
        }