mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	Downgrade Clipper to 5.1.5
This commit is contained in:
		
							parent
							
								
									0933a2cf6b
								
							
						
					
					
						commit
						b6005327d6
					
				
					 5 changed files with 189 additions and 321 deletions
				
			
		|  | @ -21,9 +21,9 @@ my $build = Module::Build::WithXSpp->new( | |||
|         Module::Build               0.38 | ||||
|         Module::Build::WithXSpp     0.13 | ||||
|     )}, | ||||
|      | ||||
|     # _GLIBCXX_USE_C99 : to get the long long type for g++ | ||||
|     # HAS_BOOL         : stops Perl/lib/CORE/handy.h from doing "#  define bool char" for MSVC | ||||
|     extra_compiler_flags => [qw(-DHAS_BOOL)], | ||||
|     extra_compiler_flags => [qw(-D_GLIBCXX_USE_C99 -DHAS_BOOL)], | ||||
|      | ||||
|     # Provides extra C typemaps that are auto-merged | ||||
|     extra_typemap_modules => { | ||||
|  |  | |||
|  | @ -144,7 +144,7 @@ offset_ex(Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float de | |||
| 
 | ||||
| void | ||||
| offset2(Slic3r::Polygons &polygons, ClipperLib::Polygons &retval, const float delta1, | ||||
|     const float delta2, double scale, ClipperLib::JoinType joinType, double miterLimit) | ||||
|     const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) | ||||
| { | ||||
|     // read input
 | ||||
|     ClipperLib::Polygons* input = new ClipperLib::Polygons(); | ||||
|  | @ -168,7 +168,7 @@ offset2(Slic3r::Polygons &polygons, ClipperLib::Polygons &retval, const float de | |||
| 
 | ||||
| void | ||||
| offset2(Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1, | ||||
|     const float delta2, double scale, ClipperLib::JoinType joinType, double miterLimit) | ||||
|     const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) | ||||
| { | ||||
|     // perform offset
 | ||||
|     ClipperLib::Polygons* output = new ClipperLib::Polygons(); | ||||
|  | @ -181,7 +181,7 @@ offset2(Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1 | |||
| 
 | ||||
| void | ||||
| offset2_ex(Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta1, | ||||
|     const float delta2, double scale, ClipperLib::JoinType joinType, double miterLimit) | ||||
|     const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) | ||||
| { | ||||
|     // perform offset
 | ||||
|     ClipperLib::Polygons* output = new ClipperLib::Polygons(); | ||||
|  | @ -193,8 +193,8 @@ offset2_ex(Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float d | |||
| } | ||||
| 
 | ||||
| template <class T> | ||||
| void _clipper_do(ClipperLib::ClipType clipType, Slic3r::Polygons &subject,  | ||||
|     Slic3r::Polygons &clip, T &retval, bool safety_offset) | ||||
| void _clipper_do(const ClipperLib::ClipType clipType, Slic3r::Polygons &subject,  | ||||
|     Slic3r::Polygons &clip, T &retval, const bool safety_offset) | ||||
| { | ||||
|     // read input
 | ||||
|     ClipperLib::Polygons* input_subject = new ClipperLib::Polygons(); | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| /*******************************************************************************
 | ||||
| *                                                                              * | ||||
| * Author    :  Angus Johnson                                                   * | ||||
| * Version   :  5.1.6                                                           * | ||||
| * Date      :  23 May 2013                                                     * | ||||
| * Version   :  5.1.5                                                           * | ||||
| * Date      :  4 May 2013                                                      * | ||||
| * Website   :  http://www.angusj.com                                           *
 | ||||
| * Copyright :  Angus Johnson 2010-2013                                         * | ||||
| *                                                                              * | ||||
|  | @ -59,8 +59,6 @@ enum Direction { dRightToLeft, dLeftToRight }; | |||
| #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) | ||||
| #define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b)) | ||||
| 
 | ||||
| const char coords_range_error[] = "Coordinate exceeds range bounds."; | ||||
| 
 | ||||
| inline long64 Abs(long64 val) | ||||
| { | ||||
|   return val < 0 ? -val : val; | ||||
|  | @ -356,7 +354,7 @@ bool FullRangeNeeded(const Polygon &pts) | |||
|   for (Polygon::size_type i = 0; i <  pts.size(); ++i) | ||||
|   { | ||||
|     if (Abs(pts[i].X) > hiRange || Abs(pts[i].Y) > hiRange) | ||||
|         throw coords_range_error; | ||||
|         throw "Coordinate exceeds range bounds."; | ||||
|       else if (Abs(pts[i].X) > loRange || Abs(pts[i].Y) > loRange) | ||||
|         result = true; | ||||
|   } | ||||
|  | @ -456,17 +454,17 @@ bool PointOnLineSegment(const IntPoint pt, | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| bool PointOnPolygon(const IntPoint pt, OutPt *pp, bool UseFullInt64Range) | ||||
| bool PointOnPolygon(const IntPoint pt, | ||||
|  OutPt *pp, bool UseFullInt64Range) | ||||
| { | ||||
|   OutPt *pp2 = pp; | ||||
|   while (true) | ||||
|   for (;;) | ||||
|   { | ||||
|     if (PointOnLineSegment(pt, pp2->pt, pp2->next->pt, UseFullInt64Range)) | ||||
|       return true; | ||||
|     pp2 = pp2->next; | ||||
|     if (pp2 == pp) break; | ||||
|     if (pp2 == pp) return false; | ||||
|   }  | ||||
|   return false; | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
|  | @ -503,7 +501,7 @@ bool PointInPolygon(const IntPoint &pt, OutPt *pp, bool UseFullInt64Range) | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) | ||||
| bool SlopesEqual(TEdge &e1, TEdge &e2, bool UseFullInt64Range) | ||||
| { | ||||
|   if (UseFullInt64Range) | ||||
|     return Int128Mul(e1.deltaY, e2.deltaX) == Int128Mul(e1.deltaX, e2.deltaY); | ||||
|  | @ -669,7 +667,8 @@ void DisposeOutPts(OutPt*& pp) | |||
| void InitEdge(TEdge *e, TEdge *eNext, | ||||
|   TEdge *ePrev, const IntPoint &pt, PolyType polyType) | ||||
| { | ||||
|   std::memset(e, 0, sizeof(TEdge)); | ||||
|   std::memset( e, 0, sizeof( TEdge )); | ||||
| 
 | ||||
|   e->next = eNext; | ||||
|   e->prev = ePrev; | ||||
|   e->xcurr = pt.X; | ||||
|  | @ -861,39 +860,27 @@ ClipperBase::~ClipperBase() //destructor | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void RangeTest(const IntPoint& pt, long64& maxrange) | ||||
| { | ||||
|   if (Abs(pt.X) > maxrange) | ||||
|   { | ||||
|     if (Abs(pt.X) > hiRange)  | ||||
|       throw coords_range_error; | ||||
|     else maxrange = hiRange; | ||||
|   } | ||||
|   if (Abs(pt.Y) > maxrange) | ||||
|   { | ||||
|     if (Abs(pt.Y) > hiRange) | ||||
|       throw coords_range_error; | ||||
|     else maxrange = hiRange; | ||||
|   } | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| bool ClipperBase::AddPolygon(const Polygon &pg, PolyType polyType) | ||||
| bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) | ||||
| { | ||||
|   int len = (int)pg.size(); | ||||
|   if (len < 3) return false; | ||||
| 
 | ||||
|   long64 maxVal; | ||||
|   if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; | ||||
|   RangeTest(pg[0], maxVal); | ||||
| 
 | ||||
|   Polygon p(len); | ||||
|   p[0] = pg[0]; | ||||
|   int j = 0; | ||||
| 
 | ||||
|   long64 maxVal; | ||||
|   if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; | ||||
| 
 | ||||
|   for (int i = 0; i < len; ++i) | ||||
|   { | ||||
|     RangeTest(pg[i], maxVal); | ||||
|     if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) | ||||
|     { | ||||
|       if (Abs(pg[i].X) > hiRange || Abs(pg[i].Y) > hiRange) | ||||
|         throw "Coordinate exceeds range bounds"; | ||||
|       maxVal = hiRange; | ||||
|       m_UseFullRange = true; | ||||
|     } | ||||
| 
 | ||||
|     if (i == 0 || PointsEqual(p[j], pg[i])) continue; | ||||
|     else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange)) | ||||
|  | @ -1203,6 +1190,7 @@ void Clipper::Reset() | |||
|   while (lm) | ||||
|   { | ||||
|     InsertScanbeam(lm->Y); | ||||
|     InsertScanbeam(lm->leftBound->ytop); | ||||
|     lm = lm->next; | ||||
|   } | ||||
| } | ||||
|  | @ -1272,7 +1260,7 @@ bool Clipper::ExecuteInternal() | |||
|       if (!succeeded) break; | ||||
|       ProcessEdgesAtTopOfScanbeam(topY); | ||||
|       botY = topY; | ||||
|     } while(m_Scanbeam || m_CurrentLM); | ||||
|     } while( m_Scanbeam ); | ||||
|   } | ||||
|   catch(...) { | ||||
|     succeeded = false; | ||||
|  | @ -1470,7 +1458,6 @@ bool Clipper::IsContributing(const TEdge& edge) const | |||
|         default:  | ||||
|           return (edge.windCnt2 < 0); | ||||
|       } | ||||
|       break; | ||||
|     case ctUnion: | ||||
|       switch(pft2) | ||||
|       { | ||||
|  | @ -1482,7 +1469,6 @@ bool Clipper::IsContributing(const TEdge& edge) const | |||
|         default:  | ||||
|           return (edge.windCnt2 >= 0); | ||||
|       } | ||||
|       break; | ||||
|     case ctDifference: | ||||
|       if (edge.polyType == ptSubject) | ||||
|         switch(pft2) | ||||
|  | @ -1506,7 +1492,6 @@ bool Clipper::IsContributing(const TEdge& edge) const | |||
|           default:  | ||||
|             return (edge.windCnt2 < 0); | ||||
|         } | ||||
|       break; | ||||
|     default: | ||||
|       return true; | ||||
|   } | ||||
|  | @ -2070,12 +2055,12 @@ void Clipper::AddOutPt(TEdge *e, const IntPoint &pt) | |||
|   { | ||||
|     OutRec *outRec = CreateOutRec(); | ||||
|     e->outIdx = outRec->idx; | ||||
|     OutPt* newOp = new OutPt; | ||||
|     outRec->pts = newOp; | ||||
|     newOp->pt = pt; | ||||
|     newOp->idx = outRec->idx; | ||||
|     newOp->next = newOp; | ||||
|     newOp->prev = newOp; | ||||
|     OutPt* op = new OutPt; | ||||
|     outRec->pts = op; | ||||
|     op->pt = pt; | ||||
|     op->idx = outRec->idx; | ||||
|     op->next = op; | ||||
|     op->prev = op; | ||||
|     SetHoleState(e, outRec); | ||||
|   } else | ||||
|   { | ||||
|  | @ -2084,14 +2069,14 @@ void Clipper::AddOutPt(TEdge *e, const IntPoint &pt) | |||
|     if ((ToFront && PointsEqual(pt, op->pt)) || | ||||
|       (!ToFront && PointsEqual(pt, op->prev->pt))) return; | ||||
| 
 | ||||
|     OutPt* newOp = new OutPt; | ||||
|     newOp->pt = pt; | ||||
|     newOp->idx = outRec->idx; | ||||
|     newOp->next = op; | ||||
|     newOp->prev = op->prev; | ||||
|     newOp->prev->next = newOp; | ||||
|     op->prev = newOp; | ||||
|     if (ToFront) outRec->pts = newOp; | ||||
|     OutPt* op2 = new OutPt; | ||||
|     op2->pt = pt; | ||||
|     op2->idx = outRec->idx; | ||||
|     op2->next = op; | ||||
|     op2->prev = op->prev; | ||||
|     op2->prev->next = op2; | ||||
|     op->prev = op2; | ||||
|     if (ToFront) outRec->pts = op2; | ||||
|   } | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
|  | @ -2413,8 +2398,8 @@ void Clipper::BuildIntersectList(const long64 botY, const long64 topY) | |||
|   } | ||||
| 
 | ||||
|   //bubblesort ...
 | ||||
|   bool isModified; | ||||
|   do | ||||
|   bool isModified = true; | ||||
|   while( isModified && m_SortedEdges ) | ||||
|   { | ||||
|     isModified = false; | ||||
|     e = m_SortedEdges; | ||||
|  | @ -2431,7 +2416,7 @@ void Clipper::BuildIntersectList(const long64 botY, const long64 topY) | |||
|             pt.Y = botY; | ||||
|             pt.X = TopX(*e, pt.Y); | ||||
|         } | ||||
|         InsertIntersectNode( e, eNext, pt ); | ||||
|         AddIntersectNode( e, eNext, pt ); | ||||
|         SwapPositionsInSEL(e, eNext); | ||||
|         isModified = true; | ||||
|       } | ||||
|  | @ -2441,12 +2426,11 @@ void Clipper::BuildIntersectList(const long64 botY, const long64 topY) | |||
|     if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; | ||||
|     else break; | ||||
|   } | ||||
|   while ( isModified ); | ||||
|   m_SortedEdges = 0; //important
 | ||||
|   m_SortedEdges = 0; | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void Clipper::InsertIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) | ||||
| void Clipper::AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) | ||||
| { | ||||
|   IntersectNode* newNode = new IntersectNode; | ||||
|   newNode->edge1 = e1; | ||||
|  | @ -2463,7 +2447,7 @@ void Clipper::InsertIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) | |||
|   { | ||||
|     IntersectNode* iNode = m_IntersectNodes; | ||||
|     while(iNode->next  && newNode->pt.Y <= iNode->next->pt.Y) | ||||
|       iNode = iNode->next; | ||||
|         iNode = iNode->next; | ||||
|     newNode->next = iNode->next; | ||||
|     iNode->next = newNode; | ||||
|   } | ||||
|  | @ -2766,7 +2750,7 @@ bool Clipper::FixupIntersectionOrder() | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) | ||||
| bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) | ||||
| { | ||||
|   if (e2.xcurr == e1.xcurr)  | ||||
|   { | ||||
|  | @ -3172,151 +3156,134 @@ DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) | |||
| //------------------------------------------------------------------------------
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| class OffsetBuilder | ||||
| class PolyOffsetBuilder | ||||
| { | ||||
| private: | ||||
|   const Polygons& m_p; | ||||
|   Polygons m_p; | ||||
|   Polygon* m_curr_poly; | ||||
|   std::vector<DoublePoint> normals; | ||||
|   double m_delta, m_rmin, m_r; | ||||
|   double m_delta, m_RMin, m_R; | ||||
|   size_t m_i, m_j, m_k; | ||||
|   static const int buffLength = 128; | ||||
|   JoinType m_jointype; | ||||
|   | ||||
| public: | ||||
| 
 | ||||
| OffsetBuilder(const Polygons& in_polys, Polygons& out_polys, | ||||
|   bool isPolygon, double delta, JoinType jointype, EndType endtype, double limit): m_p(in_polys) | ||||
| PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, | ||||
|   double delta, JoinType jointype, double limit, bool autoFix) | ||||
| { | ||||
|     //precondition: &out_polys != &in_polys
 | ||||
|     //nb precondition - out_polys != ptsin_polys
 | ||||
|     if (NEAR_ZERO(delta)) | ||||
|     { | ||||
|         out_polys = in_polys; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (NEAR_ZERO(delta)) {out_polys = in_polys; return;} | ||||
|     m_rmin = 0.5; | ||||
|     m_delta = delta; | ||||
|     if (jointype == jtMiter) | ||||
|     this->m_p = in_polys; | ||||
|     this->m_delta = delta; | ||||
|     this->m_jointype = jointype; | ||||
| 
 | ||||
|     //ChecksInput - 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)  | ||||
|     { | ||||
|       if (limit > 2) m_rmin = 2.0 / (limit * limit); | ||||
|       limit = 0.25; //just in case endtype == etRound
 | ||||
|       size_t Len = m_p.size(), botI = 0; | ||||
|       while (botI < Len && m_p[botI].empty()) botI++; | ||||
|       if (botI == Len) return; | ||||
|        | ||||
|       //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point
 | ||||
|       //This point (on m_p[botI]) must be on an outer polygon ring and if 
 | ||||
|       //its orientation is false (counterclockwise) then assume all polygons 
 | ||||
|       //need reversing ...
 | ||||
|       IntPoint botPt = m_p[botI][0];       | ||||
|       for (size_t i = botI; i < Len; ++i) | ||||
|       { | ||||
|         if (m_p[i].size() < 3) continue; | ||||
|         if (UpdateBotPt(m_p[i][0], botPt)) botI = i; | ||||
|         Polygon::iterator it = m_p[i].begin() +1; | ||||
|         while (it != m_p[i].end()) | ||||
|         { | ||||
|           if (PointsEqual(*it, *(it -1))) | ||||
|             it = m_p[i].erase(it); | ||||
|           else  | ||||
|           { | ||||
|             if (UpdateBotPt(*it, botPt)) botI = i; | ||||
|             ++it; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       if (!Orientation(m_p[botI])) | ||||
|         ReversePolygons(m_p); | ||||
|     } | ||||
|     else | ||||
| 
 | ||||
|     switch (jointype) | ||||
|     { | ||||
|       if (limit <= 0) limit = 0.25; | ||||
|       else if (limit > std::fabs(delta)) limit = std::fabs(delta); | ||||
|         case jtRound:   | ||||
|             if (limit <= 0) limit = 0.25; | ||||
|             else if (limit > std::fabs(delta)) limit = std::fabs(delta); | ||||
|             break;   | ||||
|         case jtMiter:  | ||||
|             if (limit < 2) limit = 2;  | ||||
|             break;        | ||||
|         default:       //unused
 | ||||
|             limit = 1;    | ||||
|     } | ||||
|     m_RMin = 2.0/(limit*limit); | ||||
|   | ||||
|     double deltaSq = delta*delta; | ||||
|     out_polys.clear(); | ||||
|     out_polys.resize(m_p.size()); | ||||
|     for (m_i = 0; m_i < m_p.size(); m_i++) | ||||
|     { | ||||
|         m_curr_poly = &out_polys[m_i]; | ||||
|         size_t len = m_p[m_i].size(); | ||||
|         if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && | ||||
|             m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; | ||||
| 
 | ||||
|         //when 'shrinking' polygons - to minimize artefacts
 | ||||
|         //strip those polygons that have an area < pi * delta^2 ...
 | ||||
|         double a1 = Area(m_p[m_i]); | ||||
|         if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } | ||||
|         else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. area
 | ||||
| 
 | ||||
|         if (len == 0 || (len < 3 && delta <= 0)) | ||||
|             continue; | ||||
|           continue; | ||||
|         else if (len == 1) | ||||
|         { | ||||
|             out_polys[m_i] = BuildArc(m_p[m_i][0], 0, 2*pi, delta, limit); | ||||
|             Polygon arc; | ||||
|             arc = BuildArc(m_p[m_i][len-1], 0, 2 * pi, delta, limit); | ||||
|             out_polys[m_i] = arc; | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         bool forceClose = PointsEqual(m_p[m_i][0], m_p[m_i][len -1]); | ||||
|         if (forceClose) len--; | ||||
| 
 | ||||
|         //build normals ...
 | ||||
|         normals.clear(); | ||||
|         normals.resize(len); | ||||
|         normals[len-1] = GetUnitNormal(m_p[m_i][len-1], m_p[m_i][0]); | ||||
|         for (m_j = 0; m_j < len -1; ++m_j) | ||||
|             normals[m_j] = GetUnitNormal(m_p[m_i][m_j], m_p[m_i][m_j +1]); | ||||
|         if (isPolygon || forceClose)  | ||||
|           normals[len-1] = GetUnitNormal(m_p[m_i][len-1], m_p[m_i][0]); | ||||
|         else //is open polyline
 | ||||
|           normals[len-1] = normals[len-2]; | ||||
|             normals[m_j] = GetUnitNormal(m_p[m_i][m_j], m_p[m_i][m_j+1]); | ||||
|          | ||||
|         m_curr_poly = &out_polys[m_i]; | ||||
|         m_curr_poly->reserve(len); | ||||
| 
 | ||||
|         if (isPolygon || forceClose)  | ||||
|         m_k = len -1; | ||||
|         for (m_j = 0; m_j < len; ++m_j) | ||||
|         { | ||||
|           m_k = len -1; | ||||
|           for (m_j = 0; m_j < len; ++m_j) | ||||
|             OffsetPoint(jointype, limit); | ||||
| 
 | ||||
|           if (!isPolygon) | ||||
|           switch (jointype) | ||||
|           { | ||||
|             size_t j = out_polys.size(); | ||||
|             out_polys.resize(j+1); | ||||
|             m_curr_poly = &out_polys[j]; | ||||
|             m_curr_poly->reserve(len); | ||||
|             m_delta = -m_delta; | ||||
| 
 | ||||
|             m_k = len -1; | ||||
|             for (m_j = 0; m_j < len; ++m_j) | ||||
|               OffsetPoint(jointype, limit); | ||||
|             m_delta = -m_delta; | ||||
|             ReversePolygon(*m_curr_poly); | ||||
|           } | ||||
|         } | ||||
|         else //is open polyline
 | ||||
|         { | ||||
|           //offset the polyline going forward ...
 | ||||
|           m_k = 0; | ||||
|           for (m_j = 1; m_j < len -1; ++m_j) | ||||
|             OffsetPoint(jointype, limit); | ||||
| 
 | ||||
|           //handle the end (butt, round or square) ...
 | ||||
|           IntPoint pt1; | ||||
|           if (endtype == etButt) | ||||
|           { | ||||
|             m_j = len - 1; | ||||
|             pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta),  | ||||
|               Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); | ||||
|             AddPoint(pt1); | ||||
|             pt1 = IntPoint(Round(m_p[m_i][m_j].X - normals[m_j].X * m_delta),  | ||||
|               Round(m_p[m_i][m_j].Y - normals[m_j].Y * m_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 == 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; | ||||
| 
 | ||||
|           //offset the polyline going backward ...
 | ||||
|           m_k = len -1; | ||||
|           for (m_j = m_k - 1; m_j > 0; --m_j) | ||||
|             OffsetPoint(jointype, limit); | ||||
| 
 | ||||
|           //finally handle the start (butt, round or square) ...
 | ||||
|           if (endtype == etButt)  | ||||
|           { | ||||
|             pt1 = IntPoint(Round(m_p[m_i][0].X - normals[0].X * m_delta),  | ||||
|               Round(m_p[m_i][0].Y - normals[0].Y * m_delta)); | ||||
|             AddPoint(pt1); | ||||
|             pt1 = IntPoint(Round(m_p[m_i][0].X + normals[0].X * m_delta),  | ||||
|               Round(m_p[m_i][0].Y + normals[0].Y * m_delta)); | ||||
|             AddPoint(pt1); | ||||
|           } else | ||||
|           { | ||||
|             m_k = 1; | ||||
|             if (endtype == etSquare) DoSquare();  | ||||
|             else DoRound(limit); | ||||
|             case jtMiter: | ||||
|             { | ||||
|               m_R = 1 + (normals[m_j].X*normals[m_k].X +  | ||||
|                 normals[m_j].Y*normals[m_k].Y); | ||||
|               if (m_R >= m_RMin) DoMiter(); else DoSquare(limit); | ||||
|               break; | ||||
|             } | ||||
|             case jtSquare: DoSquare(1.0); break; | ||||
|             case jtRound: DoRound(limit); break; | ||||
|           } | ||||
|         m_k = m_j; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //and clean up untidy corners using Clipper ...
 | ||||
|     //finally, clean up untidy corners using Clipper ...
 | ||||
|     Clipper clpr; | ||||
|     clpr.AddPolygons(out_polys, ptSubject); | ||||
|     if (delta > 0) | ||||
|  | @ -3334,10 +3301,12 @@ OffsetBuilder(const Polygons& in_polys, Polygons& out_polys, | |||
|         outer[3] = IntPoint(r.left - 10, r.top - 10); | ||||
| 
 | ||||
|         clpr.AddPolygon(outer, ptSubject); | ||||
|         clpr.ReverseSolution(true); | ||||
|         if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative)) | ||||
|         { | ||||
|             out_polys.erase(out_polys.begin()); | ||||
|         else | ||||
|             ReversePolygons(out_polys); | ||||
| 
 | ||||
|         } else | ||||
|             out_polys.clear(); | ||||
|     } | ||||
| } | ||||
|  | @ -3345,24 +3314,6 @@ OffsetBuilder(const Polygons& in_polys, Polygons& out_polys, | |||
| 
 | ||||
| private: | ||||
| 
 | ||||
| void OffsetPoint(JoinType jointype, double limit) | ||||
| { | ||||
|     switch (jointype) | ||||
|     { | ||||
|       case jtMiter: | ||||
|       { | ||||
|         m_r = 1 + (normals[m_j].X*normals[m_k].X +  | ||||
|           normals[m_j].Y*normals[m_k].Y); | ||||
|         if (m_r >= m_rmin) DoMiter(); else DoSquare(); | ||||
|         break; | ||||
|       } | ||||
|       case jtSquare: DoSquare(); break; | ||||
|       case jtRound: DoRound(limit); break; | ||||
|     } | ||||
|     m_k = m_j; | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void AddPoint(const IntPoint& pt) | ||||
| { | ||||
|     if (m_curr_poly->size() == m_curr_poly->capacity()) | ||||
|  | @ -3371,22 +3322,24 @@ void AddPoint(const IntPoint& pt) | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void DoSquare() | ||||
| void DoSquare(double mul) | ||||
| { | ||||
|     IntPoint pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), | ||||
|         Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); | ||||
|     IntPoint pt2 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), | ||||
|         Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); | ||||
|     IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), | ||||
|         (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); | ||||
|     IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), | ||||
|         (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); | ||||
|     if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) | ||||
|     { | ||||
|       double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); | ||||
|       double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X); | ||||
|       a1 = std::fabs(a2 - a1); | ||||
|       if (a1 > pi) a1 = pi * 2 - a1; | ||||
|       double dx = std::tan((pi - a1) / 4) * std::fabs(m_delta); | ||||
|       pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), (long64)(pt1.Y + normals[m_k].X * dx)); | ||||
|       double dx = std::tan((pi - a1) / 4) * std::fabs(m_delta * mul); | ||||
|       pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), | ||||
|         (long64)(pt1.Y + normals[m_k].X * dx)); | ||||
|       AddPoint(pt1); | ||||
|       pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), (long64)(pt2.Y -normals[m_j].X * dx)); | ||||
|       pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), | ||||
|         (long64)(pt2.Y -normals[m_j].X * dx)); | ||||
|       AddPoint(pt2); | ||||
|     } | ||||
|     else | ||||
|  | @ -3402,16 +3355,17 @@ void DoMiter() | |||
| { | ||||
|     if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) | ||||
|     { | ||||
|         double q = m_delta / m_r; | ||||
|         AddPoint(IntPoint(Round(m_p[m_i][m_j].X + (normals[m_k].X + normals[m_j].X) * q), | ||||
|             Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); | ||||
|         double q = m_delta / m_R; | ||||
|         AddPoint(IntPoint((long64)Round(m_p[m_i][m_j].X +  | ||||
|             (normals[m_k].X + normals[m_j].X) * q), | ||||
|             (long64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         IntPoint pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta),  | ||||
|           Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); | ||||
|         IntPoint pt2 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta),  | ||||
|           Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); | ||||
|         IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * | ||||
|           m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); | ||||
|         IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * | ||||
|           m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); | ||||
|         AddPoint(pt1); | ||||
|         AddPoint(m_p[m_i][m_j]); | ||||
|         AddPoint(pt2); | ||||
|  | @ -3421,10 +3375,10 @@ void DoMiter() | |||
| 
 | ||||
| void DoRound(double limit) | ||||
| { | ||||
|     IntPoint pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), | ||||
|         Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); | ||||
|     IntPoint pt2 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), | ||||
|         Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); | ||||
|     IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), | ||||
|         (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); | ||||
|     IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), | ||||
|         (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); | ||||
|     AddPoint(pt1); | ||||
|     //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg).
 | ||||
|     if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0) | ||||
|  | @ -3446,11 +3400,6 @@ void DoRound(double limit) | |||
| } | ||||
| //--------------------------------------------------------------------------
 | ||||
| 
 | ||||
| }; //end PolyOffsetBuilder
 | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| bool UpdateBotPt(const IntPoint &pt, IntPoint &botPt) | ||||
| { | ||||
|     if (pt.Y > botPt.Y || (pt.Y == botPt.Y && pt.X < botPt.X)) | ||||
|  | @ -3462,93 +3411,20 @@ bool UpdateBotPt(const IntPoint &pt, IntPoint &botPt) | |||
| } | ||||
| //--------------------------------------------------------------------------
 | ||||
| 
 | ||||
| }; //end PolyOffsetBuilder
 | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, | ||||
|   double delta, JoinType jointype, double limit, bool autoFix) | ||||
| { | ||||
|   if (!autoFix && &in_polys != &out_polys) | ||||
|   if (&out_polys == &in_polys) | ||||
|   { | ||||
|     OffsetBuilder(in_polys, out_polys, true, delta, jointype, etClosed, limit); | ||||
|     return; | ||||
|     Polygons poly2(in_polys); | ||||
|     PolyOffsetBuilder(poly2, out_polys, delta, jointype, limit, autoFix); | ||||
|   } | ||||
| 
 | ||||
|   Polygons inPolys = Polygons(in_polys); | ||||
|   out_polys.clear(); | ||||
| 
 | ||||
|   //ChecksInput - 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)  | ||||
|   { | ||||
|     size_t polyCount = inPolys.size(), botPoly = 0; | ||||
|     while (botPoly < polyCount && inPolys[botPoly].empty()) botPoly++; | ||||
|     if (botPoly == polyCount) return; | ||||
|        | ||||
|     //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point
 | ||||
|     //This point (on m_p[botPoly]) must be on an outer polygon ring and if 
 | ||||
|     //its orientation is false (counterclockwise) then assume all polygons 
 | ||||
|     //need reversing ...
 | ||||
|     IntPoint botPt = inPolys[botPoly][0];       | ||||
|     for (size_t i = botPoly; i < polyCount; ++i) | ||||
|     { | ||||
|       if (inPolys[i].size() < 3) { inPolys[i].clear(); continue; } | ||||
|       if (UpdateBotPt(inPolys[i][0], botPt)) botPoly = i; | ||||
|       Polygon::iterator it = inPolys[i].begin() +1; | ||||
|       while (it != inPolys[i].end()) | ||||
|       { | ||||
|         if (PointsEqual(*it, *(it -1))) | ||||
|           it = inPolys[i].erase(it); | ||||
|         else  | ||||
|         { | ||||
|           if (UpdateBotPt(*it, botPt)) botPoly = i; | ||||
|           ++it; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     if (!Orientation(inPolys[botPoly])) | ||||
|       ReversePolygons(inPolys); | ||||
|   } | ||||
|   OffsetBuilder(inPolys, out_polys, true, delta, jointype, etClosed, limit); | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void OffsetPolyLines(const Polygons &in_lines, Polygons &out_lines, | ||||
|   double delta, JoinType jointype, EndType endtype,  | ||||
|   double limit, bool autoFix) | ||||
| { | ||||
|   if (!autoFix && endtype != etClosed && &in_lines != &out_lines) | ||||
|   { | ||||
|     OffsetBuilder(in_lines, out_lines, false, delta, jointype, endtype, limit); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   Polygons inLines = Polygons(in_lines); | ||||
|   if (autoFix)  | ||||
|     for (size_t i = 0; i < inLines.size(); ++i) | ||||
|     { | ||||
|       if (inLines[i].size() < 2) { inLines[i].clear(); continue; } | ||||
|       Polygon::iterator it = inLines[i].begin() +1; | ||||
|       while (it != inLines[i].end()) | ||||
|       { | ||||
|         if (PointsEqual(*it, *(it -1))) | ||||
|           it = inLines[i].erase(it); | ||||
|         else | ||||
|           ++it; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|   if (endtype == etClosed) | ||||
|   { | ||||
|     size_t sz = inLines.size(); | ||||
|     inLines.resize(sz * 2); | ||||
|     for (size_t i = 0; i < sz; ++i) | ||||
|     { | ||||
|       inLines[sz+i] = inLines[i]; | ||||
|       ReversePolygon(inLines[sz+i]); | ||||
|     } | ||||
|     OffsetBuilder(inLines, out_lines, true, delta, jointype, endtype, limit); | ||||
|   }  | ||||
|   else | ||||
|     OffsetBuilder(inLines, out_lines, false, delta, jointype, endtype, limit); | ||||
|   else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, limit, autoFix); | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
|  | @ -3616,7 +3492,7 @@ bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance) | ||||
| void CleanPolygon(Polygon& in_poly, Polygon& out_poly, double distance) | ||||
| { | ||||
|   //distance = proximity in units/pixels below which vertices
 | ||||
|   //will be stripped. Default ~= sqrt(2).
 | ||||
|  | @ -3625,17 +3501,15 @@ void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance) | |||
|   while (highI > 0 && PointsAreClose(in_poly[highI], in_poly[0], distSqrd)) highI--; | ||||
|   if (highI < 2) { out_poly.clear(); return; } | ||||
|    | ||||
|   if (&in_poly != &out_poly)  | ||||
|     out_poly.resize(highI + 1); | ||||
| 
 | ||||
|   out_poly.resize(highI + 1); | ||||
|   IntPoint pt = in_poly[highI]; | ||||
|   int i = 0, k = 0; | ||||
|   for (;;) | ||||
|   { | ||||
|     while (i < highI && PointsAreClose(pt, in_poly[i+1], distSqrd)) i+=2; | ||||
|     while (i <= highI && PointsAreClose(pt, in_poly[i+1], distSqrd)) i+=2; | ||||
|     int i2 = i; | ||||
|     while (i < highI && (PointsAreClose(in_poly[i], in_poly[i+1], distSqrd) || | ||||
|       SlopesNearColinear(pt, in_poly[i], in_poly[i+1], distSqrd))) i++; | ||||
|     while (i <= highI && PointsAreClose(in_poly[i], in_poly[i+1], distSqrd) || | ||||
|       SlopesNearColinear(pt, in_poly[i], in_poly[+1], distSqrd)) i++; | ||||
|     if (i >= highI) break; | ||||
|     else if (i != i2) continue; | ||||
|     pt = in_poly[i++]; | ||||
|  | @ -3648,14 +3522,14 @@ void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance) | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void CleanPolygons(const Polygons& in_polys, Polygons& out_polys, double distance) | ||||
| void CleanPolygons(Polygons& in_polys, Polygons& out_polys, double distance) | ||||
| { | ||||
|   for (Polygons::size_type i = 0; i < in_polys.size(); ++i) | ||||
|     CleanPolygon(in_polys[i], out_polys[i], distance); | ||||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void AddPolyNodeToPolygons(const PolyNode& polynode, Polygons& polygons) | ||||
| void AddPolyNodeToPolygons(PolyNode& polynode, Polygons& polygons) | ||||
| { | ||||
|   if (!polynode.Contour.empty()) | ||||
|     polygons.push_back(polynode.Contour); | ||||
|  | @ -3664,7 +3538,7 @@ void AddPolyNodeToPolygons(const PolyNode& polynode, Polygons& polygons) | |||
| } | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void PolyTreeToPolygons(const PolyTree& polytree, Polygons& polygons) | ||||
| void PolyTreeToPolygons(PolyTree& polytree, Polygons& polygons) | ||||
| { | ||||
|   polygons.resize(0);  | ||||
|   polygons.reserve(polytree.Total()); | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| /*******************************************************************************
 | ||||
| *                                                                              * | ||||
| * Author    :  Angus Johnson                                                   * | ||||
| * Version   :  5.1.6                                                           * | ||||
| * Date      :  23 May 2013                                                     * | ||||
| * Version   :  5.1.5                                                           * | ||||
| * Date      :  4 May 2013                                                      * | ||||
| * Website   :  http://www.angusj.com                                           *
 | ||||
| * Copyright :  Angus Johnson 2010-2013                                         * | ||||
| *                                                                              * | ||||
|  | @ -101,7 +101,6 @@ private: | |||
| }; | ||||
|          | ||||
| enum JoinType { jtSquare, jtRound, jtMiter }; | ||||
| enum EndType { etClosed, etButt, etSquare, etRound}; | ||||
| 
 | ||||
| bool Orientation(const Polygon &poly); | ||||
| double Area(const Polygon &poly); | ||||
|  | @ -109,17 +108,14 @@ double Area(const Polygon &poly); | |||
| void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, | ||||
|   double delta, JoinType jointype = jtSquare, double limit = 0, bool autoFix = true); | ||||
| 
 | ||||
| void OffsetPolyLines(const Polygons &in_lines, Polygons &out_lines, | ||||
|   double delta, JoinType jointype = jtSquare, EndType endtype = etSquare, double limit = 0, bool autoFix = true); | ||||
| 
 | ||||
| void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd); | ||||
| void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd); | ||||
| void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd); | ||||
| 
 | ||||
| void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance = 1.415); | ||||
| void CleanPolygons(const Polygons& in_polys, Polygons& out_polys, double distance = 1.415); | ||||
| void CleanPolygon(Polygon& in_poly, Polygon& out_poly, double distance = 1.415); | ||||
| void CleanPolygons(Polygons& in_polys, Polygons& out_polys, double distance = 1.415); | ||||
| 
 | ||||
| void PolyTreeToPolygons(const PolyTree& polytree, Polygons& polygons); | ||||
| void PolyTreeToPolygons(PolyTree& polytree, Polygons& polygons); | ||||
| 
 | ||||
| void ReversePolygon(Polygon& p); | ||||
| void ReversePolygons(Polygons& p); | ||||
|  | @ -127,8 +123,6 @@ void ReversePolygons(Polygons& p); | |||
| //used internally ...
 | ||||
| enum EdgeSide { esLeft = 1, esRight = 2}; | ||||
| enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 }; | ||||
| //inline IntersectProtects operator|(IntersectProtects a, IntersectProtects b)
 | ||||
| //{return static_cast<IntersectProtects>(static_cast<int>(a) | static_cast<int>(b));}
 | ||||
| 
 | ||||
| struct TEdge { | ||||
|   long64 xbot; | ||||
|  | @ -304,7 +298,7 @@ private: | |||
|   void DisposeAllPolyPts(); | ||||
|   void DisposeOutRec(PolyOutList::size_type index); | ||||
|   bool ProcessIntersections(const long64 botY, const long64 topY); | ||||
|   void InsertIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt); | ||||
|   void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt); | ||||
|   void BuildIntersectList(const long64 botY, const long64 topY); | ||||
|   void ProcessIntersectList(); | ||||
|   void ProcessEdgesAtTopOfScanbeam(const long64 topY); | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square); | |||
|         [15000000, 15000000], | ||||
|         [15000000, 5000000], | ||||
|     ]); | ||||
|     my $result = Slic3r::Geometry::Clipper::offset2_ex([ @$expolygon2 ], +49900, -49900); | ||||
|     my $result = Slic3r::Geometry::Clipper::offset2_ex([ @$expolygon2 ], -1, +1); | ||||
|     is_deeply $result->[0]->pp, $expolygon2->pp, 'offset2_ex'; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci