public void Add(Segment s)
        {
            // j iterates through the openPolygon chains.
            for (int j=0; j<openPolygons.Count; j++) {
                PointChain chain = openPolygons [j];
                if (!chain.LinkSegment (s)) continue;

                if (chain.closed) {
                    if (chain.pointList.Count == 2) {
                    // We tried linking the same segment (but flipped end and start) to
                    // a chain. (i.e. chain was <p0, p1>, we tried linking Segment(p1, p0)
                    // so the chain was closed illegally.
                        chain.closed = false;
                        return;
                    }

                    closedPolygons.Add (chain);
                    openPolygons.RemoveAt (j);
                    return;
                }

                int k = openPolygons.Count;
                for (int i=j+1; i<k; i++) {
                    // Try to connect this open link to the rest of the chains.
                    // We won't be able to connect this to any of the chains preceding this one
                    // because we know that linkSegment failed on those.
                    if (chain.LinkPointChain (openPolygons [i])) {
                        openPolygons.RemoveAt (i);
                        return;
                    }
                }
                return;
            }

            PointChain newChain = new PointChain (s);
            openPolygons.Add (newChain);
        }
        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 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);
                    }
                }
            }
        }
        void ProcessSegment(Segment segment, PolygonType polygonType)
        {
            if (Point.EqualsBoth(segment.start, segment.end)) return;
            SweepEvent e1 = new SweepEvent(segment.start, true, polygonType);
            SweepEvent e2 = new SweepEvent(segment.end, true, polygonType, e1);
            e1.otherSE = e2;

            if (e1.p.x < e2.p.x - Point.PRECISION ) {
                e2.isLeft = false;
            } else if (e1.p.x > e2.p.x + Point.PRECISION) {
                e1.isLeft = false;
            } else if (e1.p.y < e2.p.y - Point.PRECISION) { // the segment is vertical. The bottom endpoint is processed before the top endpoint
                e2.isLeft = false;
            } else {
                e1.isLeft = false;
            }

            // Pushing it so the que is sorted from left to right, with object on the left
            // having the highest priority.
            eventQueue.Enqueue(e1);
            eventQueue.Enqueue(e2);
        }
        IntersectResult FindIntersection(Segment seg0, Segment seg1)
        {
            Point pi0 = Point.zero;
            Point pi1 = Point.zero;

            Point p0 = seg0.start;
            double d0x = seg0.end.x - p0.x;
            double d0y = seg0.end.y - p0.y;

            Point p1 = seg1.start;
            double d1x = seg1.end.x - p1.x;
            double d1y = seg1.end.y - p1.y;

            double Ex = p1.x - p0.x;
            double Ey =  p1.y - p0.y;

            double kross = d0x * d1y - d0y * d1x;

            if (kross > Point.PRECISION || kross < -Point.PRECISION) { //sqrEpsilon) { // * sqrLen0 * sqrLen1) {
                // lines of the segments are not parallel
                double s = (Ex * d1y - Ey * d1x) / kross;
                if (s < 0 || s > 1) {
                    return new IntersectResult (max: 0, point1: pi0, point2: pi1);
                }
                double t = (Ex * d0y - Ey * d0x) / kross;
                if (t < 0 || t > 1) {
                    return new IntersectResult (max: 0, point1: pi0, point2: pi1);
                }
                // intersection of lines is a point an each segment
                pi0.x = p0.x + s * d0x;
                pi0.y = p0.y + s * d0y;

                return new IntersectResult ( max: 1, point1: pi0, point2: pi1 );
            }

            // lines of the segments are parallel
            kross = Ex * d0y - Ey * d0x;
            if (kross > Point.PRECISION || kross < -Point.PRECISION) { // sqrEpsilon ) { //* sqrLen0 * sqrLenE) {
                // lines of the segment are different
                return new IntersectResult ( max: 0, point1: pi0, point2: pi1 );
            }

            // Lines of the segments are the same. Need to test for overlap of segments.
            double sqrLen0 = Math.Sqrt (d0x*d0x+d0y*d0y); // d0.magnitude;
            double s0 = (d0x * Ex + d0y * Ey) / sqrLen0;  // so = Dot (D0, E) * sqrLen0
            double s1 = s0 + (d0x * d1x + d0y * d1y) / sqrLen0;  // s1 = s0 + Dot (D0, D1) * sqrLen0
            double smin = Math.Min(s0, s1);
            double smax = Math.Max(s0, s1);
            double[] w = new double[2];
            int imax = FindIntersection2(0, 1, smin, smax, w);

            if (imax > 0) {
                pi0.x = p0.x + w[0] * d0x;
                pi0.y = p0.y + w[0] * d0y;
                if (imax > 1) {
                    pi1.x = p0.x + w[1] * d0x;
                    pi1.y = p0.y + w[1] * d0y;
                }
            }
            return new IntersectResult (max: imax, point1: pi0, point2: pi1);
        }