diff --git a/xs/src/ClipperUtils.cpp b/xs/src/ClipperUtils.cpp index e26b159da7..2989783eed 100644 --- a/xs/src/ClipperUtils.cpp +++ b/xs/src/ClipperUtils.cpp @@ -339,43 +339,14 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_) { - - /* Clipper will remove a polyline segment if first point coincides with last one. - Until that bug is not fixed upstream, we move one of those points slightly. */ - Slic3r::Polylines polylines = subject; // temp copy to avoid dropping the const qualifier - std::vector translated(subject.size(), false); // keep track of polylines we applied the hack to - for (Slic3r::Polylines::iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { - if (polyline->points.front().coincides_with(polyline->points.back())) { - polyline->points.front().translate(1, 0); - translated[ polyline - polylines.begin() ] = true; - } - } - // perform operation ClipperLib::PolyTree polytree; - _clipper_do(clipType, polylines, clip, polytree, ClipperLib::pftNonZero, safety_offset_); + _clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_); // convert into Polylines ClipperLib::Paths output; ClipperLib::PolyTreeToPaths(polytree, output); ClipperPaths_to_Slic3rMultiPoints(output, retval); - - // compensate for the above hack - for (Slic3r::Polylines::iterator polyline = retval.begin(); polyline != retval.end(); ++polyline) { - for (Slic3r::Polylines::const_iterator subj_polyline = polylines.begin(); subj_polyline != polylines.end(); ++subj_polyline) { - if (!translated[ subj_polyline - polylines.begin() ]) continue; - // if first point of clipped line coincides with first point of subject line, compensate for hack - if (polyline->points.front().coincides_with(subj_polyline->points.front())) { - polyline->points.front().translate(-1, 0); - break; - } - // since Clipper does not preserve orientation of polylines, check last point too - if (polyline->points.back().coincides_with(subj_polyline->points.front())) { - polyline->points.back().translate(-1, 0); - break; - } - } - } } void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index 4a720b71b1..936e2524d4 100644 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.1.3a * -* Date : 22 January 2014 * +* Version : 6.1.5 * +* Date : 22 May 2014 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2014 * * * @@ -50,15 +50,6 @@ namespace ClipperLib { -#ifdef use_int32 - static cInt const loRange = 46340; - static cInt const hiRange = 46340; -#else - static cInt const loRange = 0x3FFFFFFF; - static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; - typedef unsigned long long ulong64; -#endif - static double const pi = 3.141592653589793238; static double const two_pi = pi *2; static double const def_arc_tolerance = 0.25; @@ -240,8 +231,8 @@ bool PolyNode::IsOpen() const //------------------------------------------------------------------------------ // Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((cInt)9223372036854775807); //ie 2^63 -1 -// Int128 val2((cInt)9223372036854775807); +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); // Int128 val3 = val1 * val2; // val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) //------------------------------------------------------------------------------ @@ -249,22 +240,21 @@ bool PolyNode::IsOpen() const class Int128 { public: + ulong64 lo; + long64 hi; - cUInt lo; - cInt hi; - - Int128(cInt _lo = 0) + Int128(long64 _lo = 0) { - lo = (cUInt)_lo; + lo = (ulong64)_lo; if (_lo < 0) hi = -1; else hi = 0; } Int128(const Int128 &val): lo(val.lo), hi(val.hi){} - Int128(const cInt& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} - Int128& operator = (const cInt &val) + Int128& operator = (const long64 &val) { lo = (ulong64)val; if (val < 0) hi = -1; else hi = 0; @@ -335,85 +325,10 @@ class Int128 return Int128(~hi,~lo +1); } - Int128 operator/ (const Int128 &rhs) const - { - if (rhs.lo == 0 && rhs.hi == 0) - throw "Int128 operator/: divide by zero"; - - bool negate = (rhs.hi < 0) != (hi < 0); - Int128 dividend = *this; - Int128 divisor = rhs; - if (dividend.hi < 0) dividend = -dividend; - if (divisor.hi < 0) divisor = -divisor; - - if (divisor < dividend) - { - Int128 result = Int128(0); - Int128 cntr = Int128(1); - while (divisor.hi >= 0 && !(divisor > dividend)) - { - divisor.hi <<= 1; - if ((cInt)divisor.lo < 0) divisor.hi++; - divisor.lo <<= 1; - - cntr.hi <<= 1; - if ((cInt)cntr.lo < 0) cntr.hi++; - cntr.lo <<= 1; - } - divisor.lo >>= 1; - if ((divisor.hi & 1) == 1) - divisor.lo |= 0x8000000000000000LL; - divisor.hi = (ulong64)divisor.hi >> 1; - - cntr.lo >>= 1; - if ((cntr.hi & 1) == 1) - cntr.lo |= 0x8000000000000000LL; - cntr.hi >>= 1; - - while (cntr.hi != 0 || cntr.lo != 0) - { - if (!(dividend < divisor)) - { - dividend -= divisor; - result.hi |= cntr.hi; - result.lo |= cntr.lo; - } - divisor.lo >>= 1; - if ((divisor.hi & 1) == 1) - divisor.lo |= 0x8000000000000000LL; - divisor.hi >>= 1; - - cntr.lo >>= 1; - if ((cntr.hi & 1) == 1) - cntr.lo |= 0x8000000000000000LL; - cntr.hi >>= 1; - } - if (negate) result = -result; - return result; - } - else if (rhs.hi == this->hi && rhs.lo == this->lo) - return Int128(negate ? -1: 1); - else - return Int128(0); - } - - double AsDouble() const - { - const double shift64 = 18446744073709551616.0; //2^64 - if (hi < 0) - { - cUInt lo_ = ~lo + 1; - if (lo_ == 0) return (double)hi * shift64; - else return -(double)(lo_ + ~hi * shift64); - } - else - return (double)(lo + hi * shift64); - } - }; //------------------------------------------------------------------------------ -Int128 Int128Mul (cInt lhs, cInt rhs) +Int128 Int128Mul (long64 lhs, long64 rhs) { bool negate = (lhs < 0) != (rhs < 0); @@ -431,9 +346,9 @@ Int128 Int128Mul (cInt lhs, cInt rhs) ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; Int128 tmp; - tmp.hi = cInt(a + (c >> 32)); - tmp.lo = cInt(c << 32); - tmp.lo += cInt(b); + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); if (tmp.lo < b) tmp.hi++; if (negate) tmp = -tmp; return tmp; @@ -444,6 +359,13 @@ Int128 Int128Mul (cInt lhs, cInt rhs) // Miscellaneous global functions //------------------------------------------------------------------------------ +void Swap(cInt& val1, cInt& val2) +{ + cInt tmp = val1; + val1 = val2; + val2 = tmp; +} +//------------------------------------------------------------------------------ bool Orientation(const Path &poly) { return Area(poly) >= 0; @@ -494,6 +416,7 @@ bool PointIsVertex(const IntPoint &Pt, OutPt *pp) int PointInPolygon (const IntPoint &pt, const Path &path) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf int result = 0; size_t cnt = path.size(); @@ -539,7 +462,6 @@ int PointInPolygon (const IntPoint &pt, const Path &path) int PointInPolygon (const IntPoint &pt, OutPt *op) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf int result = 0; OutPt* startOp = op; for(;;) @@ -674,20 +596,18 @@ inline cInt TopX(TEdge &edge, const cInt currentY) } //------------------------------------------------------------------------------ -bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, - IntPoint &ip, bool UseFullInt64Range) +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) { #ifdef use_xyz ip.Z = 0; #endif + double b1, b2; - //nb: with very large coordinate values, it's possible for SlopesEqual() to - //return false but for the edge.Dx value be equal due to double precision rounding. - if (SlopesEqual(Edge1, Edge2, UseFullInt64Range) || Edge1.Dx == Edge2.Dx) + if (Edge1.Dx == Edge2.Dx) { - if (Edge2.Bot.Y > Edge1.Bot.Y) ip = Edge2.Bot; - else ip = Edge1.Bot; - return false; + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; } else if (Edge1.Delta.X == 0) { @@ -734,7 +654,15 @@ bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, else ip.X = TopX(Edge2, ip.Y); } - return true; + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } } //------------------------------------------------------------------------------ @@ -807,13 +735,9 @@ inline void ReverseHorizontal(TEdge &e) //swap horizontal edges' Top and Bottom x's so they follow the natural //progression of the bounds - ie so their xbots will align with the //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - cInt tmp = e.Top.X; - e.Top.X = e.Bot.X; - e.Bot.X = tmp; + Swap(e.Top.X, e.Bot.X); #ifdef use_xyz - tmp = e.Top.Z; - e.Top.Z = e.Bot.Z; - e.Bot.Z = tmp; + Swap(e.Top.Z, e.Bot.Z); #endif } //------------------------------------------------------------------------------ @@ -905,26 +829,6 @@ OutPt* GetBottomPt(OutPt *pp) } //------------------------------------------------------------------------------ -bool FindSegment(OutPt* &pp, bool UseFullInt64Range, - IntPoint &pt1, IntPoint &pt2) -{ - //OutPt1 & OutPt2 => the overlap segment (if the function returns true) - if (!pp) return false; - OutPt* pp2 = pp; - IntPoint pt1a = pt1, pt2a = pt2; - do - { - if (SlopesEqual(pt1a, pt2a, pp->Pt, pp->Prev->Pt, UseFullInt64Range) && - SlopesEqual(pt1a, pt2a, pp->Pt, UseFullInt64Range) && - GetOverlapSegment(pt1a, pt2a, pp->Pt, pp->Prev->Pt, pt1, pt2)) - return true; - pp = pp->Next; - } - while (pp != pp2); - return false; -} -//------------------------------------------------------------------------------ - bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, const IntPoint pt2, const IntPoint pt3) { @@ -937,41 +841,12 @@ bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, } //------------------------------------------------------------------------------ -OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint Pt) +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) { - if (p1 == p2) throw "JoinError"; - OutPt* result = new OutPt; - result->Pt = Pt; - if (p2 == p1->Next) - { - p1->Next = result; - p2->Prev = result; - result->Next = p2; - result->Prev = p1; - } else - { - p2->Next = result; - p1->Prev = result; - result->Next = p1; - result->Prev = p2; - } - return result; + if (seg1a > seg1b) Swap(seg1a, seg1b); + if (seg2a > seg2b) Swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); } -//------------------------------------------------------------------------------ - -bool HorzSegmentsOverlap(const IntPoint& pt1a, const IntPoint& pt1b, - const IntPoint& pt2a, const IntPoint& pt2b) -{ - //precondition: both segments are horizontal - if ((pt1a.X > pt2a.X) == (pt1a.X < pt2b.X)) return true; - else if ((pt1b.X > pt2a.X) == (pt1b.X < pt2b.X)) return true; - else if ((pt2a.X > pt1a.X) == (pt2a.X < pt1b.X)) return true; - else if ((pt2b.X > pt1a.X) == (pt2b.X < pt1b.X)) return true; - else if ((pt1a.X == pt2a.X) && (pt1b.X == pt2b.X)) return true; - else if ((pt1a.X == pt2b.X) && (pt1b.X == pt2a.X)) return true; - else return false; -} - //------------------------------------------------------------------------------ // ClipperBase class methods ... @@ -1030,20 +905,20 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) cInt StartX; if (IsHorizontal(*E)) { - //first we need to be careful here with open paths because this - //may not be a true local minima (ie may be following a skip edge). - //also, watch for adjacent horz edges to start heading left - //before finishing right ... - if (IsClockwise) - { - if (E->Prev->Bot.Y == E->Bot.Y) StartX = E->Prev->Bot.X; - else StartX = E->Prev->Top.X; - } - else - { - if (E->Next->Bot.Y == E->Bot.Y) StartX = E->Next->Bot.X; - else StartX = E->Next->Top.X; - } + //first we need to be careful here with open paths because this + //may not be a true local minima (ie may be following a skip edge). + //also, watch for adjacent horz edges to start heading left + //before finishing right ... + if (IsClockwise) + { + if (E->Prev->Bot.Y == E->Bot.Y) StartX = E->Prev->Bot.X; + else StartX = E->Prev->Top.X; + } + else + { + if (E->Next->Bot.Y == E->Bot.Y) StartX = E->Next->Bot.X; + else StartX = E->Next->Top.X; + } if (E->Bot.X != StartX) ReverseHorizontal(*E); } @@ -1189,7 +1064,8 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) TEdge *E = eStart, *eLoopStop = eStart; for (;;) { - if ((E->Curr == E->Next->Curr)) + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) { if (E == E->Next) break; if (E == eStart) eStart = E->Next; @@ -1215,7 +1091,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) continue; } E = E->Next; - if (E == eLoopStop) break; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; } if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) @@ -1274,6 +1150,11 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) m_edges.push_back(edges); bool clockwise; TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + for (;;) { E = FindNextLocMin(E); @@ -2028,7 +1909,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) Join* jr = m_GhostJoins[i]; //if the horizontal Rb and a 'ghost' horizontal overlap, then convert //the 'ghost' join to a real join ready for later ... - if (HorzSegmentsOverlap(jr->OutPt1->Pt, jr->OffPt, rb->Bot, rb->Top)) + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) AddJoin(jr->OutPt1, Op1, jr->OffPt); } } @@ -2098,45 +1979,34 @@ void Clipper::DeleteFromSEL(TEdge *e) //------------------------------------------------------------------------------ #ifdef use_xyz - -void Clipper::SetZ(IntPoint& pt, TEdge& e) +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) { - pt.Z = 0; - if (m_ZFill) - { - //put the 'preferred' point as first parameter ... - if (e.OutIdx < 0) - (*m_ZFill)(e.Bot, e.Top, pt); //outside a path so presume entering - else - (*m_ZFill)(e.Top, e.Bot, pt); //inside a path so presume exiting - } + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); } //------------------------------------------------------------------------------ #endif -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &Pt, bool protect) +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) { - //e1 will be to the Left of e2 BELOW the intersection. Therefore e1 is before - //e2 in AEL except when e1 is being inserted at the intersection point ... - bool e1stops = !protect && !e1->NextInLML && - e1->Top.X == Pt.X && e1->Top.Y == Pt.Y; - bool e2stops = !protect && !e2->NextInLML && - e2->Top.X == Pt.X && e2->Top.Y == Pt.Y; bool e1Contributing = ( e1->OutIdx >= 0 ); bool e2Contributing = ( e2->OutIdx >= 0 ); +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + #ifdef use_lines //if either edge is on an OPEN path ... if (e1->WindDelta == 0 || e2->WindDelta == 0) { //ignore subject-subject open path intersections UNLESS they //are both open paths, AND they are both 'contributing maximas' ... - if (e1->WindDelta == 0 && e2->WindDelta == 0) - { - if ((e1stops || e2stops) && e1Contributing && e2Contributing) - AddLocalMaxPoly(e1, e2, Pt); - } + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; //if intersecting a subj line with a subj poly ... else if (e1->PolyTyp == e2->PolyTyp && @@ -2175,13 +2045,6 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, if (e2Contributing) e2->OutIdx = Unassigned; } } - - if (e1stops) - if (e1->OutIdx < 0) DeleteFromAEL(e1); - else throw clipperException("Error intersecting polylines"); - if (e2stops) - if (e2->OutIdx < 0) DeleteFromAEL(e2); - else throw clipperException("Error intersecting polylines"); return; } #endif @@ -2246,10 +2109,11 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, if ( e1Contributing && e2Contributing ) { - if ( e1stops || e2stops || - (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - AddLocalMaxPoly(e1, e2, Pt); + { + AddLocalMaxPoly(e1, e2, Pt); + } else { AddOutPt(e1, Pt); @@ -2276,8 +2140,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, SwapPolyIndexes(*e1, *e2); } } - else if ( (e1Wc == 0 || e1Wc == 1) && - (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) { //neither edge is currently contributing ... @@ -2296,7 +2159,9 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, } if (e1->PolyTyp != e2->PolyTyp) - AddLocalMinPoly(e1, e2, Pt); + { + AddLocalMinPoly(e1, e2, Pt); + } else if (e1Wc == 1 && e2Wc == 1) switch( m_ClipType ) { case ctIntersection: @@ -2318,17 +2183,6 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, else SwapSides( *e1, *e2 ); } - - if( (e1stops != e2stops) && - ( (e1stops && (e1->OutIdx >= 0)) || (e2stops && (e2->OutIdx >= 0)) ) ) - { - SwapSides( *e1, *e2 ); - SwapPolyIndexes( *e1, *e2 ); - } - - //finally, delete any non-contributing maxima edges ... - if( e1stops ) DeleteFromAEL( e1 ); - if( e2stops ) DeleteFromAEL( e2 ); } //------------------------------------------------------------------------------ @@ -2519,12 +2373,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) newOp->Prev = newOp; if (!outRec->IsOpen) SetHoleState(e, outRec); -#ifdef use_xyz - if (pt == e->Bot) newOp->Pt = e->Bot; - else if (pt == e->Top) newOp->Pt = e->Top; - else SetZ(newOp->Pt, *e); -#endif - e->OutIdx = outRec->Idx; //nb: do this after SetZ ! + e->OutIdx = outRec->Idx; return newOp; } else { @@ -2543,11 +2392,6 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) newOp->Prev->Next = newOp; op->Prev = newOp; if (ToFront) outRec->Pts = newOp; -#ifdef use_xyz - if (pt == e->Bot) newOp->Pt = e->Bot; - else if (pt == e->Top) newOp->Pt = e->Top; - else SetZ(newOp->Pt, *e); -#endif return newOp; } } @@ -2714,37 +2558,6 @@ void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) } //------------------------------------------------------------------------ -void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam) -{ - //get the last Op for this horizontal edge - //the point may be anywhere along the horizontal ... - OutPt* outPt = m_PolyOuts[horzEdge->OutIdx]->Pts; - if (horzEdge->Side != esLeft) outPt = outPt->Prev; - - //First, match up overlapping horizontal edges (eg when one polygon's - //intermediate horz edge overlaps an intermediate horz edge of another, or - //when one polygon sits on top of another) ... - //for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) - //{ - // Join* j = m_GhostJoins[i]; - // if (HorzSegmentsOverlap(j->OutPt1->Pt, j->OffPt, horzEdge->Bot, horzEdge->Top)) - // AddJoin(j->OutPt1, outPt, j->OffPt); - //} - - //Also, since horizontal edges at the top of one SB are often removed from - //the AEL before we process the horizontal edges at the bottom of the next, - //we need to create 'ghost' Join records of 'contrubuting' horizontals that - //we can compare with horizontals at the bottom of the next SB. - if (isTopOfScanbeam) - { - if (outPt->Pt == horzEdge->Top) - AddGhostJoin(outPt, horzEdge->Bot); - else - AddGhostJoin(outPt, horzEdge->Top); - } -} -//------------------------------------------------------------------------------ - /******************************************************************************* * Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * * Bottom of a scanbeam) are processed as if layered. The order in which HEs * @@ -2784,28 +2597,42 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) if ((dir == dLeftToRight && e->Curr.X <= horzRight) || (dir == dRightToLeft && e->Curr.X >= horzLeft)) { - if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) - PrepareHorzJoins(horzEdge, isTopOfScanbeam); //so far we're still in range of the horizontal Edge but make sure //we're at the last of consec. horizontals when matching with eMaxPair if(e == eMaxPair && IsLastHorz) { - if (dir == dLeftToRight) - IntersectEdges(horzEdge, e, e->Top); - else - IntersectEdges(e, horzEdge, e->Top); - if (eMaxPair->OutIdx >= 0) throw clipperException("ProcessHorizontal error"); + + if (horzEdge->OutIdx >= 0) + { + OutPt* op1 = AddOutPt(horzEdge, horzEdge->Top); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = AddOutPt(eNextHorz, eNextHorz->Bot); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + } + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); return; } else if(dir == dLeftToRight) { IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges(horzEdge, e, Pt, true); + IntersectEdges(horzEdge, e, Pt); } else { IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); - IntersectEdges( e, horzEdge, Pt, true); + IntersectEdges( e, horzEdge, Pt); } SwapPositionsInAEL( horzEdge, e ); } @@ -2814,9 +2641,6 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) e = eNext; } //end while - if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) - PrepareHorzJoins(horzEdge, isTopOfScanbeam); - if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML)) { UpdateEdgeIntoAEL(horzEdge); @@ -2831,6 +2655,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) if(horzEdge->OutIdx >= 0) { OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); + if (isTopOfScanbeam) AddGhostJoin(op1, horzEdge->Bot); UpdateEdgeIntoAEL(horzEdge); if (horzEdge->WindDelta == 0) return; //nb: HorzEdge is no longer horizontal here @@ -2856,22 +2681,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) else UpdateEdgeIntoAEL(horzEdge); } - else if (eMaxPair) - { - if (eMaxPair->OutIdx >= 0) - { - if (dir == dLeftToRight) - IntersectEdges(horzEdge, eMaxPair, horzEdge->Top); - else - IntersectEdges(eMaxPair, horzEdge, horzEdge->Top); - if (eMaxPair->OutIdx >= 0) - throw clipperException("ProcessHorizontal error"); - } else - { - DeleteFromAEL(horzEdge); - DeleteFromAEL(eMaxPair); - } - } else + else { if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); DeleteFromAEL(horzEdge); @@ -2958,16 +2768,7 @@ void Clipper::BuildIntersectList(const cInt botY, const cInt topY) IntPoint Pt; if(e->Curr.X > eNext->Curr.X) { - if (!IntersectPoint(*e, *eNext, Pt, m_UseFullRange) && e->Curr.X > eNext->Curr.X +1) - throw clipperException("Intersection error"); - if (Pt.Y > botY) - { - Pt.Y = botY; - if (std::fabs(e->Dx) > std::fabs(eNext->Dx)) - Pt.X = TopX(*eNext, botY); else - Pt.X = TopX(*e, botY); - } - + IntersectPoint(*e, *eNext, Pt); IntersectNode * newNode = new IntersectNode; newNode->Edge1 = e; newNode->Edge2 = eNext; @@ -2995,7 +2796,7 @@ void Clipper::ProcessIntersectList() { IntersectNode* iNode = m_IntersectList[i]; { - IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt, true); + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); } delete iNode; @@ -3054,7 +2855,7 @@ void Clipper::DoMaxima(TEdge *e) TEdge* eNext = e->NextInAEL; while(eNext && eNext != eMaxPair) { - IntersectEdges(e, eNext, e->Top, true); + IntersectEdges(e, eNext, e->Top); SwapPositionsInAEL(e, eNext); eNext = e->NextInAEL; } @@ -3066,7 +2867,9 @@ void Clipper::DoMaxima(TEdge *e) } else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) { - IntersectEdges( e, eMaxPair, e->Top); + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); } #ifdef use_lines else if (e->WindDelta == 0) @@ -3134,9 +2937,13 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) { - OutPt* op = AddOutPt(ePrev, e->Curr); - OutPt* op2 = AddOutPt(e, e->Curr); - AddJoin(op, op2, e->Curr); //StrictlySimple (type-3) join + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join } } @@ -3518,6 +3325,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) (j->OffPt == j->OutPt2->Pt)) { //Strictly Simple join ... + if (outRec1 != outRec2) return false; op1b = j->OutPt1->Next; while (op1b != op1 && (op1b->Pt == j->OffPt)) op1b = op1b->Next; @@ -3858,8 +3666,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType (path[i].Y == newNode->Contour[k].Y && path[i].X < newNode->Contour[k].X)) k = j; } - if ((endType == etClosedPolygon && j < 2) || - (endType != etClosedPolygon && j < 0)) + if (endType == etClosedPolygon && j < 2) { delete newNode; return; @@ -3869,7 +3676,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType //if this path's lowest pt is lower than all the others then update m_lowest if (endType != etClosedPolygon) return; if (m_lowest.X < 0) - m_lowest = IntPoint(0, k); + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); else { IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; @@ -4159,8 +3966,20 @@ void ClipperOffset::DoOffset(double delta) void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) { + //cross product ... m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); - if (m_sinA < 0.00005 && m_sinA > -0.00005) return; + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } else if (m_sinA > 1.0) m_sinA = 1.0; else if (m_sinA < -1.0) m_sinA = -1.0; @@ -4358,7 +4177,27 @@ double DistanceFromLineSqrd( bool SlopesNearCollinear(const IntPoint& pt1, const IntPoint& pt2, const IntPoint& pt3, double distSqrd) { - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (std::abs(pt1.X - pt2.X) > std::abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } } //------------------------------------------------------------------------------ @@ -4486,8 +4325,8 @@ void Minkowski(const Path& poly, const Path& path, pp.push_back(p); } - Paths quads; - quads.reserve((pathCnt + delta) * (polyCnt + 1)); + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); for (size_t i = 0; i < pathCnt - 1 + delta; ++i) for (size_t j = 0; j < polyCnt; ++j) { @@ -4498,23 +4337,30 @@ void Minkowski(const Path& poly, const Path& path, quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); if (!Orientation(quad)) ReversePath(quad); - quads.push_back(quad); + solution.push_back(quad); } - - Clipper c; - c.AddPaths(quads, ptSubject, true); - c.Execute(ctUnion, solution, pftNonZero, pftNonZero); } //------------------------------------------------------------------------------ void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) { Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); } //------------------------------------------------------------------------------ -void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, - PolyFillType pathFillType, bool pathIsClosed) +void TranslatePath(const Path& input, Path& output, IntPoint delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) { Clipper c; for (size_t i = 0; i < paths.size(); ++i) @@ -4522,15 +4368,23 @@ void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, Paths tmp; Minkowski(pattern, paths[i], tmp, true, pathIsClosed); c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } } - if (pathIsClosed) c.AddPaths(paths, ptClip, true); - c.Execute(ctUnion, solution, pathFillType, pathFillType); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); } //------------------------------------------------------------------------------ void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) { Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); } //------------------------------------------------------------------------------ diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index 20791c78c0..7922abe188 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.1.3a * -* Date : 22 January 2014 * +* Version : 6.1.5 * +* Date : 22 May 2014 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2014 * * * @@ -34,7 +34,7 @@ #ifndef clipper_hpp #define clipper_hpp -#define CLIPPER_VERSION "6.1.3" +#define CLIPPER_VERSION "6.1.5" //use_int32: When enabled 32bit ints are used instead of 64bit ints. This //improve performance but coordinate values are limited to the range +/- 46340 @@ -69,11 +69,15 @@ enum PolyType { ptSubject, ptClip }; enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; #ifdef use_int32 -typedef int cInt; -typedef unsigned int cUInt; + typedef int cInt; + static cInt const loRange = 46340; + static cInt const hiRange = 46340; #else -typedef signed long long cInt; -typedef unsigned long long cUInt; + typedef signed long long cInt; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; #endif struct IntPoint { @@ -117,7 +121,7 @@ struct DoublePoint //------------------------------------------------------------------------------ #ifdef use_xyz -typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt); +typedef void (*TZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); #endif enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; @@ -183,8 +187,7 @@ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1. void CleanPolygons(Paths& polys, double distance = 1.415); void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); -void MinkowskiSum(const Path& pattern, const Paths& paths, - Paths& solution, PolyFillType pathFillType, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); @@ -308,15 +311,13 @@ private: bool IsTopHorz(const cInt XPos); void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); void DoMaxima(TEdge *e); - void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam); void ProcessHorizontals(bool IsTopOfScanbeam); void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); void AppendPolygon(TEdge *e1, TEdge *e2); - void IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &pt, bool protect = false); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); void DisposeAllOutRecs(); @@ -344,7 +345,7 @@ private: void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); #ifdef use_xyz - void SetZ(IntPoint& pt, TEdge& e); + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif }; //------------------------------------------------------------------------------