Example #1
0
        //------------------------------------------------------------------------------
        public static Polygons OffsetPolygons(Polygons pts, double delta)
        {
            double deltaSq = delta*delta;
              Polygons result = new Polygons(pts.Count);

              for (int j = 0; j < pts.Count; ++j)
              {
            int highI = pts[j].Count -1;
            //to minimize artefacts, strip out those polygons where
            //it's shrinking and where its area < Sqr(delta) ...
            double a1 = Area(pts[j]);
            if (delta < 0) { if (a1 > 0 && a1 < deltaSq) highI = 0;}
            else if (a1 < 0 && -a1 < deltaSq) highI = 0; //nb: a hole if area < 0

            Polygon pg = new Polygon(highI*2+2);

            if (highI < 2)
            {
              result.Add(pg);
              continue;
            }

            List<DoublePoint> normals = new List<DoublePoint>(highI+1);
            normals.Add(GetUnitNormal(pts[j][highI], pts[j][0]));
            for (int i = 1; i <= highI; ++i)
              normals.Add(GetUnitNormal(pts[j][i-1], pts[j][i]));

            for (int i = 0; i < highI; ++i)
            {
              pg.Add(new IntPoint(Round(pts[j][i].X + delta *normals[i].X),
                (int)(pts[j][i].Y + delta *normals[i].Y)));
              pg.Add(new IntPoint(Round(pts[j][i].X + delta * normals[i + 1].X),
                (int)(pts[j][i].Y + delta *normals[i+1].Y)));
            }
            pg.Add(new IntPoint(Round(pts[j][highI].X + delta * normals[highI].X),
              (int)(pts[j][highI].Y + delta *normals[highI].Y)));
            pg.Add(new IntPoint(Round(pts[j][highI].X + delta * normals[0].X),
              (int)(pts[j][highI].Y + delta *normals[0].Y)));

            //round off reflex angles (ie > 180 deg) unless it's almost flat (ie < 10deg angle) ...
            //cross product normals < 0 . reflex angle; dot product normals == 1 . no angle
            if ((normals[highI].X *normals[0].Y - normals[0].X *normals[highI].Y) *delta > 0 &&
            (normals[0].X *normals[highI].X + normals[0].Y *normals[highI].Y) < 0.985)
            {
              double at1 = Math.Atan2(normals[highI].Y, normals[highI].X);
              double at2 = Math.Atan2(normals[0].Y, normals[0].X);
              if (delta > 0 && at2 < at1) at2 = at2 + Math.PI*2;
              else if (delta < 0 && at2 > at1) at2 = at2 - Math.PI*2;
              Polygon arc = BuildArc(pts[j][highI], at1, at2, delta);
              pg.InsertRange(highI * 2 + 1, arc);
            }
            for (int i = highI; i > 0; --i)
              if ((normals[i-1].X*normals[i].Y - normals[i].X*normals[i-1].Y) *delta > 0 &&
              (normals[i].X*normals[i-1].X + normals[i].Y*normals[i-1].Y) < 0.985)
              {
                double at1 = Math.Atan2(normals[i-1].Y, normals[i-1].X);
                double at2 = Math.Atan2(normals[i].Y, normals[i].X);
                if (delta > 0 && at2 < at1) at2 = at2 + Math.PI*2;
                else if (delta < 0 && at2 > at1) at2 = at2 - Math.PI*2;
                Polygon arc = BuildArc(pts[j][i-1], at1, at2, delta);
                pg.InsertRange((i - 1) * 2 + 1, arc);
              }
            result.Add(pg);
              }

              //finally, clean up untidy corners ...
              Clipper clpr = new Clipper();
              clpr.AddPolygons(result, PolyType.ptSubject);
              if (delta > 0){
            if(!clpr.Execute(ClipType.ctUnion, result, PolyFillType.pftNonZero, PolyFillType.pftNonZero))
              result.Clear();
              }
              else
              {
            IntRect r = clpr.GetBounds();
            Polygon outer = new Polygon(4);
                outer.Add(new IntPoint(r.left - 10, r.bottom + 10));
                outer.Add(new IntPoint(r.right + 10, r.bottom + 10));
                outer.Add(new IntPoint(r.right + 10, r.top - 10));
                outer.Add(new IntPoint(r.left - 10, r.top - 10));
                clpr.AddPolygon(outer, PolyType.ptSubject);
                if (clpr.Execute(ClipType.ctUnion, result, PolyFillType.pftNonZero, PolyFillType.pftNonZero))
                    result.RemoveAt(0);
                else
                    result.Clear();
              }
              return result;
        }