public PolyOffsetBuilder(Polygons pts, Polygons solution, double delta, JoinType jointype, double MiterLimit = 2)
            {
                //precondtion: solution != pts

                if (delta == 0)
                {
                    solution = pts;
                    return;
                }

                this.pts = pts;
                this.delta = delta;
                if (MiterLimit <= 1) MiterLimit = 1;
                double RMin = 2/(MiterLimit*MiterLimit);

                normals = new List<DoublePoint>();

                double deltaSq = delta*delta;
                solution.Clear();
                solution.Capacity = pts.Count;
                for (m_i = 0; m_i < pts.Count; m_i++)
                {
                    int len = pts[m_i].Count;
                    if (len > 1 && pts[m_i][0].X == pts[m_i][len - 1].X &&
                        pts[m_i][0].Y == pts[m_i][len - 1].Y) len--;

                    if (len == 0 || (len < 3 && delta <= 0))
                        continue;
                    else if (len == 1)
                    {
                        Polygon arc;
                        arc = BuildArc(pts[m_i][len - 1], 0, 2 * Math.PI, delta);
                        solution.Add(arc);
                        continue;
                    }

                    //build normals ...
                    normals.Clear();
                    normals.Capacity = len;
                    for (int j = 0; j < len -1; ++j)
                        normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j+1]));
                    normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0]));

                    currentPoly = new Polygon();
                    m_k = len - 1;
                    for (m_j = 0; m_j < len; ++m_j)
                    {
                        switch (jointype)
                        {
                            case JoinType.jtMiter:
                            {
                                m_R = 1 + (normals[m_j].X*normals[m_k].X +
                                    normals[m_j].Y*normals[m_k].Y);
                                if (m_R >= RMin) DoMiter(); else DoSquare(MiterLimit);
                                break;
                            }
                            case JoinType.jtRound:
                                DoRound();
                                break;
                            case JoinType.jtSquare:
                                DoSquare(1);
                                break;
                        }
                        m_k = m_j;
                    }
                    solution.Add(currentPoly);
                }

                //finally, clean up untidy corners ...
                Clipper clpr = new Clipper();
                clpr.AddPolygons(solution, PolyType.ptSubject);
                if (delta > 0)
                {
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
                }
                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);
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
                    if (solution.Count > 0)
                    {
                        solution.RemoveAt(0);
                        for (int i = 0; i < solution.Count; i++)
                            solution[i].Reverse();
                    }
                }
            }
Пример #2
0
            //------------------------------------------------------------------------------
            public PolyOffsetBuilder(Polygons pts, Polygons solution, bool isPolygon, double delta,
                JoinType jointype, EndType endtype, double limit = 0)
            {
                //precondition: solution != pts

                if (delta == 0) { solution = pts; return; }
                m_p = pts;
                m_delta = delta;
                m_rmin = 0.5;

                if (jointype == JoinType.jtMiter)
                {
                    if (limit > 2) m_rmin = 2.0 / (limit * limit);
                    limit = 0.25; //just in case endtype == etRound
                }
                else
                {
                    if (limit <= 0) limit = 0.25;
                    else if (limit > Math.Abs(delta)) limit = Math.Abs(delta);
                }

                double deltaSq = delta * delta;
                solution.Clear();
                solution.Capacity = pts.Count;
                for (m_i = 0; m_i < pts.Count; m_i++)
                {
                    int len = pts[m_i].Count;
                    if (len == 0 || (len < 3 && delta <= 0))
                        continue;
                    else if (len == 1)
                    {
                        currentPoly = new Polygon();
                        currentPoly = BuildArc(pts[m_i][0], 0, 2 * Math.PI, delta, limit);
                        solution.Add(currentPoly);
                        continue;
                    }

                    bool forceClose = PointsEqual(pts[m_i][0], pts[m_i][len - 1]);
                    if (forceClose) len--;

                    //build normals ...
                    normals.Clear();
                    normals.Capacity = len;
                    for (int j = 0; j < len - 1; ++j)
                        normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j + 1]));
                    if (isPolygon || forceClose)
                        normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0]));
                    else
                        normals.Add(new DoublePoint(normals[len - 2]));

                    currentPoly = new Polygon();
                    if (isPolygon || forceClose)
                    {
                        m_k = len - 1;
                        for (m_j = 0; m_j < len; ++m_j)
                            OffsetPoint(jointype, limit);
                        solution.Add(currentPoly);
                        if (!isPolygon)
                        {
                            currentPoly = new Polygon();
                            m_delta = -m_delta;
                            m_k = len - 1;
                            for (m_j = 0; m_j < len; ++m_j)
                                OffsetPoint(jointype, limit);
                            m_delta = -m_delta;
                            currentPoly.Reverse();
                            solution.Add(currentPoly);
                        }
                    }
                    else
                    {
                        m_k = 0;
                        for (m_j = 1; m_j < len - 1; ++m_j)
                            OffsetPoint(jointype, limit);

                        IntPoint pt1;
                        if (endtype == EndType.etButt)
                        {
                            m_j = len - 1;
                            pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_j].X *
                              delta), (Int64)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta));
                            AddPoint(pt1);
                            pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X - normals[m_j].X *
                              delta), (Int64)Round(pts[m_i][m_j].Y - normals[m_j].Y * delta));
                            AddPoint(pt1);
                        }
                        else
                        {
                            m_j = len - 1;
                            m_k = len - 2;
                            normals[m_j].X = -normals[m_j].X;
                            normals[m_j].Y = -normals[m_j].Y;
                            if (endtype == EndType.etSquare) DoSquare();
                            else DoRound(limit);
                        }

                        //re-build Normals ...
                        for (int j = len - 1; j > 0; j--)
                        {
                            normals[j].X = -normals[j - 1].X;
                            normals[j].Y = -normals[j - 1].Y;
                        }
                        normals[0].X = -normals[1].X;
                        normals[0].Y = -normals[1].Y;

                        m_k = len - 1;
                        for (m_j = m_k - 1; m_j > 0; --m_j)
                            OffsetPoint(jointype, limit);

                        if (endtype == EndType.etButt)
                        {
                            pt1 = new IntPoint((Int64)Round(pts[m_i][0].X - normals[0].X * delta),
                              (Int64)Round(pts[m_i][0].Y - normals[0].Y * delta));
                            AddPoint(pt1);
                            pt1 = new IntPoint((Int64)Round(pts[m_i][0].X + normals[0].X * delta),
                              (Int64)Round(pts[m_i][0].Y + normals[0].Y * delta));
                            AddPoint(pt1);
                        }
                        else
                        {
                            m_k = 1;
                            if (endtype == EndType.etSquare) DoSquare();
                            else DoRound(limit);
                        }
                        solution.Add(currentPoly);
                    }
                }

                //finally, clean up untidy corners ...
                Clipper clpr = new Clipper();
                clpr.AddPolygons(solution, PolyType.ptSubject);
                if (delta > 0)
                {
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
                }
                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);
                    clpr.ReverseSolution = true;
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
                    if (solution.Count > 0) solution.RemoveAt(0);
                }
            }
Пример #3
0
            public PolyOffsetBuilder(Polygons pts, Polygons solution, double delta, 
                JoinType jointype, double MiterLimit = 2, bool AutoFix = true)
            {
                //precondtion: solution != pts

                if (delta == 0)
                {
                    solution = pts;
                    return;
                }

                this.pts = pts;
                this.delta = delta;

                //AutoFix - fixes polygon orientation if necessary and removes 
                //duplicate vertices. Can be set false when you're sure that polygon
                //orientation is correct and that there are no duplicate vertices.
                if (AutoFix)
                {
                    int Len = pts.Count, botI = 0;
                    while (botI < Len && pts[botI].Count == 0) botI++;
                    if (botI == Len) return;

                    //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point
                    //This point (on pts[botI]) must be on an outer polygon ring and if 
                    //its orientation is false (counterclockwise) then assume all polygons 
                    //need reversing ...
                    IntPoint botPt = pts[botI][0];
                    for (int i = botI; i < Len; ++i)
                    {
                        if (pts[i].Count == 0) continue;
                        if (UpdateBotPt(pts[i][0], ref botPt)) botI = i;
                        for (int j = pts[i].Count -1; j > 0; j--)
                        {
                            if (PointsEqual(pts[i][j], pts[i][j -1]))
                                pts[i].RemoveAt(j);
                            else if (UpdateBotPt(pts[i][j], ref botPt))
                                botI = i;
                        }
                    }
                    if (!Orientation(pts[botI]))
                        ReversePolygons(pts);
                }

                if (MiterLimit <= 1) MiterLimit = 1;
                double RMin = 2.0 / (MiterLimit*MiterLimit);

                normals = new List<DoublePoint>();

                double deltaSq = delta*delta;
                solution.Clear();
                solution.Capacity = pts.Count;
                for (m_i = 0; m_i < pts.Count; m_i++)
                {
                    int len = pts[m_i].Count;
                    if (len > 1 && pts[m_i][0].X == pts[m_i][len - 1].X &&
                        pts[m_i][0].Y == pts[m_i][len - 1].Y) len--;

                    if (len == 0 || (len < 3 && delta <= 0)) 
                        continue;
                    else if (len == 1)
                    {
                        Polygon arc;
                        arc = BuildArc(pts[m_i][len - 1], 0, 2 * Math.PI, delta);
                        solution.Add(arc);
                        continue;
                    }

                    //build normals ...
                    normals.Clear();
                    normals.Capacity = len;
                    for (int j = 0; j < len -1; ++j)
                        normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j+1]));
                    normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0]));

                    currentPoly = new Polygon();
                    m_k = len - 1;
                    for (m_j = 0; m_j < len; ++m_j)
                    {
                        switch (jointype)
                        {
                            case JoinType.jtMiter:
                            {
                                m_R = 1 + (normals[m_j].X*normals[m_k].X + 
                                    normals[m_j].Y*normals[m_k].Y);
                                if (m_R >= RMin) DoMiter(); else DoSquare(MiterLimit);
                                break;
                            }
                            case JoinType.jtRound: 
                                DoRound();
                                break;
                            case JoinType.jtSquare:
                                DoSquare(1);
                                break;
                        }
                        m_k = m_j;
                    }
                    solution.Add(currentPoly);
                }

                //finally, clean up untidy corners ...
                Clipper clpr = new Clipper();
                clpr.AddPolygons(solution, PolyType.ptSubject);
                if (delta > 0)
                {
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
                }
                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);
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
                    if (solution.Count > 0)
                    {
                        solution.RemoveAt(0);
                        for (int i = 0; i < solution.Count; i++)
                            solution[i].Reverse();
                    }
                }
            }
Пример #4
0
          //------------------------------------------------------------------------------

          public PolyOffsetBuilder(Paths pts, out Paths solution, double delta,
              JoinType jointype, EndType endtype, double limit = 0)
          {
              //precondition: solution != pts
              solution = new Paths();
              if (ClipperBase.near_zero(delta)) {solution = pts; return; }
              m_p = pts;
              if (endtype != EndType.etClosed && delta < 0) delta = -delta;
              m_delta = delta;

              if (jointype == JoinType.jtMiter)
              {
                  //m_miterVal: see offset_triginometry.svg in the documentation folder ...
                  if (limit > 2) m_miterLim = 2 / (limit * limit);
                  else m_miterLim = 0.5;
                  if (endtype == EndType.etRound) limit = 0.25;
              }
              if (jointype == JoinType.jtRound || endtype == EndType.etRound)
              {
              if (limit <= 0) limit = 0.25;
              else if (limit > Math.Abs(delta)*0.25) limit = Math.Abs(delta)*0.25;
              //m_roundVal: see offset_triginometry2.svg in the documentation folder ...
              m_Steps360 = Math.PI / Math.Acos(1 - limit / Math.Abs(delta));
              m_sin = Math.Sin(2 * Math.PI / m_Steps360);
              m_cos = Math.Cos(2 * Math.PI / m_Steps360);
              m_Steps360 /= Math.PI * 2;
              if (delta < 0) m_sin = -m_sin;
              }

              double deltaSq = delta * delta;
              solution.Capacity = pts.Count;
              for (m_i = 0; m_i < pts.Count; m_i++)
              {
                  int len = pts[m_i].Count;
                  if (len == 0 || (len < 3 && delta <= 0)) continue;
                    
                  if (len == 1)
                  {
                      if (jointype == JoinType.jtRound)
                      {
                          double X = 1.0, Y = 0.0;
                          for (cInt j = 1; j <= Round(m_Steps360 * 2 * Math.PI); j++)
                          {
                              AddPoint(new IntPoint(
                                Round(m_p[m_i][0].X + X * delta),
                                Round(m_p[m_i][0].Y + Y * delta)));
                              double X2 = X;
                              X = X * m_cos - m_sin * Y;
                              Y = X2 * m_sin + Y * m_cos;
                          }
                      }
                      else
                      {
                          double X = -1.0, Y = -1.0;
                          for (int j = 0; j < 4; ++j)
                          {
                              AddPoint(new IntPoint(Round(m_p[m_i][0].X + X * delta),
                                Round(m_p[m_i][0].Y + Y * delta)));
                              if (X < 0) X = 1;
                              else if (Y < 0) Y = 1;
                              else X = -1;
                          }
                      }
                      continue;
                  }
                    
                  //build normals ...
                  normals.Clear();
                  normals.Capacity = len;
                  for (int j = 0; j < len -1; ++j)
                      normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j+1]));
                  if (endtype == EndType.etClosed)
                      normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0]));
                  else
                      normals.Add(new DoublePoint(normals[len - 2]));

                  currentPoly = new Path();
                  if (endtype == EndType.etClosed)
                  {
                      m_k = len - 1;
                      for (m_j = 0; m_j < len; ++m_j)
                          OffsetPoint(jointype);
                      solution.Add(currentPoly); 
                  }
                  else
                  {
                      m_k = 0;
                      for (m_j = 1; m_j < len - 1; ++m_j)
                          OffsetPoint(jointype);

                      IntPoint pt1;
                      if (endtype == EndType.etButt)
                      {
                          m_j = len - 1;
                          pt1 = new IntPoint((cInt)Round(pts[m_i][m_j].X + normals[m_j].X *
                            delta), (cInt)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta));
                          AddPoint(pt1);
                          pt1 = new IntPoint((cInt)Round(pts[m_i][m_j].X - normals[m_j].X *
                            delta), (cInt)Round(pts[m_i][m_j].Y - normals[m_j].Y * delta));
                          AddPoint(pt1);
                      }
                      else
                      {
                          m_j = len - 1;
                          m_k = len - 2;
                          m_sinA = 0;
                          normals[m_j] = new DoublePoint(-normals[m_j].X, -normals[m_j].Y);
                          if (endtype == EndType.etSquare)
                            DoSquare();
                          else
                            DoRound();
                      }

                      //re-build Normals ...
                      for (int j = len - 1; j > 0; j--)
                        normals[j] = new DoublePoint(-normals[j - 1].X, -normals[j - 1].Y);
                      
                      normals[0] = new DoublePoint(-normals[1].X, -normals[1].Y);

                      m_k = len - 1;
                      for (m_j = m_k - 1; m_j > 0; --m_j)
                          OffsetPoint(jointype);

                      if (endtype == EndType.etButt)
                      {
                          pt1 = new IntPoint((cInt)Round(pts[m_i][0].X - normals[0].X * delta),
                            (cInt)Round(pts[m_i][0].Y - normals[0].Y * delta));
                          AddPoint(pt1);
                          pt1 = new IntPoint((cInt)Round(pts[m_i][0].X + normals[0].X * delta),
                            (cInt)Round(pts[m_i][0].Y + normals[0].Y * delta));
                          AddPoint(pt1);
                      }
                      else
                      {
                          m_k = 1;
                          m_sinA = 0;
                          if (endtype == EndType.etSquare) 
                            DoSquare();
                          else
                            DoRound();
                      }
                      solution.Add(currentPoly);
                  }
              }

              //finally, clean up untidy corners ...
              Clipper clpr = new Clipper();
              clpr.AddPaths(solution, PolyType.ptSubject, true);
              if (delta > 0)
              {
                  clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
              }
              else
              {
                  IntRect r = clpr.GetBounds();
                  Path outer = new Path(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.AddPath(outer, PolyType.ptSubject, true);
                  clpr.ReverseSolution = true;
                  clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
                  if (solution.Count > 0) solution.RemoveAt(0);
              }
          }