Update Clipper to last trunk to fix a couple Clipper bugs causing empty intersection results and failure to process polylines with coinciding endpoints. This also caused crashed in some rare circumstances

This commit is contained in:
Alessandro Ranellucci 2014-05-23 23:58:43 +02:00
parent a3bd1b5302
commit 70ceb853f1
3 changed files with 205 additions and 379 deletions

View file

@ -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<bool> 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,

View file

@ -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);
}
//------------------------------------------------------------------------------

View file

@ -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
};
//------------------------------------------------------------------------------