clipper.cpp | clipper.cpp | |||
---|---|---|---|---|
/************************************************************************** ***** | /************************************************************************** ***** | |||
* * | * * | |||
* Author : Angus Johnson * | * Author : Angus Johnson * | |||
* Version : 4.9.4 | * Version : 4.9.6 | |||
* | * | |||
* Date : 2 November 2012 | * Date : 9 November 2012 | |||
* | * | |||
* Website : http://www.angusj.com * | * Website : http://www.angusj.com * | |||
* Copyright : Angus Johnson 2010-2012 * | * Copyright : Angus Johnson 2010-2012 * | |||
* * | * * | |||
* License: * | * License: * | |||
* Use, modification & distribution is subject to Boost Software License Ver 1. * | * Use, modification & distribution is subject to Boost Software License Ver 1. * | |||
* http://www.boost.org/LICENSE_1_0.txt * | * http://www.boost.org/LICENSE_1_0.txt * | |||
* * | * * | |||
* Attributions: * | * Attributions: * | |||
* The code in this library is an extension of Bala Vatti's clipping algorit hm: * | * The code in this library is an extension of Bala Vatti's clipping algorit hm: * | |||
* "A generic solution to polygon clipping" * | * "A generic solution to polygon clipping" * | |||
skipping to change at line 369 | skipping to change at line 369 | |||
inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) | inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) | |||
{ | { | |||
return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); | return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
bool Orientation(OutRec *outRec, bool UseFullInt64Range) | bool Orientation(OutRec *outRec, bool UseFullInt64Range) | |||
{ | { | |||
//first make sure bottomPt is correctly assigned ... | //first make sure bottomPt is correctly assigned ... | |||
OutPt *opBottom = outRec->pts, *op = outRec->pts->next; | OutPt *opBottom = outRec->pts; | |||
if (!opBottom) return true; | ||||
OutPt *op = outRec->pts->next; | ||||
while (op != outRec->pts) | while (op != outRec->pts) | |||
{ | { | |||
if (op->pt.Y >= opBottom->pt.Y) | if (op->pt.Y >= opBottom->pt.Y) | |||
{ | { | |||
if (op->pt.Y > opBottom->pt.Y || op->pt.X < opBottom->pt.X) | if (op->pt.Y > opBottom->pt.Y || op->pt.X < opBottom->pt.X) | |||
opBottom = op; | opBottom = op; | |||
} | } | |||
op = op->next; | op = op->next; | |||
} | } | |||
outRec->bottomPt = opBottom; | outRec->bottomPt = opBottom; | |||
skipping to change at line 432 | skipping to change at line 434 | |||
for (int i = 0; i < highI; ++i) | for (int i = 0; i < highI; ++i) | |||
a += (double)poly[i].X * poly[i+1].Y - (double)poly[i+1].X * poly[i]. Y; | a += (double)poly[i].X * poly[i+1].Y - (double)poly[i+1].X * poly[i]. Y; | |||
return a/2; | return a/2; | |||
} | } | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
double Area(const OutRec &outRec, bool UseFullInt64Range) | double Area(const OutRec &outRec, bool UseFullInt64Range) | |||
{ | { | |||
OutPt *op = outRec.pts; | OutPt *op = outRec.pts; | |||
if (!op) return 0; | ||||
if (UseFullInt64Range) { | if (UseFullInt64Range) { | |||
Int128 a(0); | Int128 a(0); | |||
do { | do { | |||
a += (Int128(op->prev->pt.X) * Int128(op->pt.Y)) - | a += (Int128(op->prev->pt.X) * Int128(op->pt.Y)) - | |||
Int128(op->pt.X) * Int128(op->prev->pt.Y); | Int128(op->pt.X) * Int128(op->prev->pt.Y); | |||
op = op->next; | op = op->next; | |||
} while (op != outRec.pts); | } while (op != outRec.pts); | |||
return a.AsDouble() / 2; | return a.AsDouble() / 2; | |||
} | } | |||
else | else | |||
skipping to change at line 619 | skipping to change at line 622 | |||
ip.Y = edge1.ybot; | ip.Y = edge1.ybot; | |||
} else | } else | |||
{ | { | |||
b1 = edge1.ybot - (edge1.xbot/edge1.dx); | b1 = edge1.ybot - (edge1.xbot/edge1.dx); | |||
ip.Y = Round(ip.X/edge1.dx + b1); | ip.Y = Round(ip.X/edge1.dx + b1); | |||
} | } | |||
} else | } else | |||
{ | { | |||
b1 = edge1.xbot - edge1.ybot * edge1.dx; | b1 = edge1.xbot - edge1.ybot * edge1.dx; | |||
b2 = edge2.xbot - edge2.ybot * edge2.dx; | b2 = edge2.xbot - edge2.ybot * edge2.dx; | |||
b2 = (b2-b1)/(edge1.dx - edge2.dx); | double q = (b2-b1)/(edge1.dx - edge2.dx); | |||
ip.Y = Round(b2); | ip.Y = Round(q); | |||
ip.X = Round(edge1.dx * b2 + b1); | if (std::fabs(edge1.dx) < std::fabs(edge2.dx)) | |||
ip.X = Round(edge1.dx * q + b1); | ||||
else | ||||
ip.X = Round(edge2.dx * q + b2); | ||||
} | } | |||
if (ip.Y < edge1.ytop || ip.Y < edge2.ytop) | if (ip.Y < edge1.ytop || ip.Y < edge2.ytop) | |||
{ | { | |||
if (edge1.ytop > edge2.ytop) | if (edge1.ytop > edge2.ytop) | |||
{ | { | |||
ip.X = edge1.xtop; | ip.X = edge1.xtop; | |||
ip.Y = edge1.ytop; | ip.Y = edge1.ytop; | |||
return TopX(edge2, edge1.ytop) < edge1.xtop; | return TopX(edge2, edge1.ytop) < edge1.xtop; | |||
} else | } else | |||
skipping to change at line 643 | skipping to change at line 649 | |||
ip.X = edge2.xtop; | ip.X = edge2.xtop; | |||
ip.Y = edge2.ytop; | ip.Y = edge2.ytop; | |||
return TopX(edge1, edge2.ytop) > edge2.xtop; | return TopX(edge1, edge2.ytop) > edge2.xtop; | |||
} | } | |||
} | } | |||
else | else | |||
return true; | return true; | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
void ReversePolyPtLinks(OutPt &pp) | void ReversePolyPtLinks(OutPt *pp) | |||
{ | { | |||
if (!pp) return; | ||||
OutPt *pp1, *pp2; | OutPt *pp1, *pp2; | |||
pp1 = &pp; | pp1 = pp; | |||
do { | do { | |||
pp2 = pp1->next; | pp2 = pp1->next; | |||
pp1->next = pp1->prev; | pp1->next = pp1->prev; | |||
pp1->prev = pp2; | pp1->prev = pp2; | |||
pp1 = pp2; | pp1 = pp2; | |||
} while( pp1 != &pp ); | } while( pp1 != pp ); | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
void DisposeOutPts(OutPt*& pp) | void DisposeOutPts(OutPt*& pp) | |||
{ | { | |||
if (pp == 0) return; | if (pp == 0) return; | |||
pp->prev->next = 0; | pp->prev->next = 0; | |||
while( pp ) | while( pp ) | |||
{ | { | |||
OutPt *tmpPp = pp; | OutPt *tmpPp = pp; | |||
skipping to change at line 868 | skipping to change at line 875 | |||
ClipperBase::~ClipperBase() //destructor | ClipperBase::~ClipperBase() //destructor | |||
{ | { | |||
Clear(); | Clear(); | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) | bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) | |||
{ | { | |||
int len = (int)pg.size(); | int len = (int)pg.size(); | |||
if (len < 3) return false; | if (len < 3) return false; | |||
Polygon p(len); | Polygon p(len); | |||
p[0] = pg[0]; | p[0] = pg[0]; | |||
int j = 0; | int j = 0; | |||
long64 maxVal; | long64 maxVal; | |||
if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; | if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; | |||
for (int i = 0; i < len; ++i) | for (int i = 0; i < len; ++i) | |||
{ | { | |||
if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) | if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) | |||
skipping to change at line 1327 | skipping to change at line 1335 | |||
FixupOutPolygon(*outRec); | FixupOutPolygon(*outRec); | |||
if (!outRec->pts) continue; | if (!outRec->pts) continue; | |||
if (outRec->isHole && fixHoleLinkages) FixHoleLinkage(outRec); | if (outRec->isHole && fixHoleLinkages) FixHoleLinkage(outRec); | |||
if (outRec->bottomPt == outRec->bottomFlag && | if (outRec->bottomPt == outRec->bottomFlag && | |||
(Orientation(outRec, m_UseFullRange) != (Area(*outRec, m_UseFullRan ge) > 0))) | (Orientation(outRec, m_UseFullRange) != (Area(*outRec, m_UseFullRan ge) > 0))) | |||
DisposeBottomPt(*outRec); | DisposeBottomPt(*outRec); | |||
if (outRec->isHole == | if (outRec->isHole == | |||
(m_ReverseOutput ^ Orientation(outRec, m_UseFullRange))) | (m_ReverseOutput ^ Orientation(outRec, m_UseFullRange))) | |||
ReversePolyPtLinks(*outRec->pts); | ReversePolyPtLinks(outRec->pts); | |||
} | } | |||
JoinCommonEdges(fixHoleLinkages); | JoinCommonEdges(fixHoleLinkages); | |||
if (fixHoleLinkages) | if (fixHoleLinkages) | |||
std::sort(m_PolyOuts.begin(), m_PolyOuts.end(), PolySort); | std::sort(m_PolyOuts.begin(), m_PolyOuts.end(), PolySort); | |||
} | } | |||
ClearJoins(); | ClearJoins(); | |||
ClearHorzJoins(); | ClearHorzJoins(); | |||
return succeeded; | return succeeded; | |||
skipping to change at line 1676 | skipping to change at line 1684 | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
void Clipper::ClearHorzJoins() | void Clipper::ClearHorzJoins() | |||
{ | { | |||
for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++) | for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++) | |||
delete m_HorizJoins[i]; | delete m_HorizJoins[i]; | |||
m_HorizJoins.resize(0); | m_HorizJoins.resize(0); | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
void Clipper::InsertLocalMinimaIntoAEL( const long64 botY) | void Clipper::InsertLocalMinimaIntoAEL(const long64 botY) | |||
{ | { | |||
while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) | while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) | |||
{ | { | |||
TEdge* lb = m_CurrentLM->leftBound; | TEdge* lb = m_CurrentLM->leftBound; | |||
TEdge* rb = m_CurrentLM->rightBound; | TEdge* rb = m_CurrentLM->rightBound; | |||
InsertEdgeIntoAEL( lb ); | InsertEdgeIntoAEL( lb ); | |||
InsertScanbeam( lb->ytop ); | InsertScanbeam( lb->ytop ); | |||
InsertEdgeIntoAEL( rb ); | InsertEdgeIntoAEL( rb ); | |||
skipping to change at line 1780 | skipping to change at line 1788 | |||
if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already del eted | if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already del eted | |||
if( SelPrev ) SelPrev->nextInSEL = SelNext; | if( SelPrev ) SelPrev->nextInSEL = SelNext; | |||
else m_SortedEdges = SelNext; | else m_SortedEdges = SelNext; | |||
if( SelNext ) SelNext->prevInSEL = SelPrev; | if( SelNext ) SelNext->prevInSEL = SelPrev; | |||
e->nextInSEL = 0; | e->nextInSEL = 0; | |||
e->prevInSEL = 0; | e->prevInSEL = 0; | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, | void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, | |||
const IntPoint &pt, IntersectProtects protects) | const IntPoint &pt, const IntersectProtects protects) | |||
{ | { | |||
//e1 will be to the left of e2 BELOW the intersection. Therefore e1 is be fore | //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is be fore | |||
//e2 in AEL except when e1 is being inserted at the intersection point .. . | //e2 in AEL except when e1 is being inserted at the intersection point .. . | |||
bool e1stops = !(ipLeft & protects) && !e1->nextInLML && | bool e1stops = !(ipLeft & protects) && !e1->nextInLML && | |||
e1->xtop == pt.X && e1->ytop == pt.Y; | e1->xtop == pt.X && e1->ytop == pt.Y; | |||
bool e2stops = !(ipRight & protects) && !e2->nextInLML && | bool e2stops = !(ipRight & protects) && !e2->nextInLML && | |||
e2->xtop == pt.X && e2->ytop == pt.Y; | e2->xtop == pt.X && e2->ytop == pt.Y; | |||
bool e1Contributing = ( e1->outIdx >= 0 ); | bool e1Contributing = ( e1->outIdx >= 0 ); | |||
bool e2contributing = ( e2->outIdx >= 0 ); | bool e2contributing = ( e2->outIdx >= 0 ); | |||
skipping to change at line 1996 | skipping to change at line 2004 | |||
OutPt* p2_lft = outRec2->pts; | OutPt* p2_lft = outRec2->pts; | |||
OutPt* p2_rt = p2_lft->prev; | OutPt* p2_rt = p2_lft->prev; | |||
EdgeSide side; | EdgeSide side; | |||
//join e2 poly onto e1 poly and delete pointers to e2 ... | //join e2 poly onto e1 poly and delete pointers to e2 ... | |||
if( e1->side == esLeft ) | if( e1->side == esLeft ) | |||
{ | { | |||
if( e2->side == esLeft ) | if( e2->side == esLeft ) | |||
{ | { | |||
//z y x a b c | //z y x a b c | |||
ReversePolyPtLinks(*p2_lft); | ReversePolyPtLinks(p2_lft); | |||
p2_lft->next = p1_lft; | p2_lft->next = p1_lft; | |||
p1_lft->prev = p2_lft; | p1_lft->prev = p2_lft; | |||
p1_rt->next = p2_rt; | p1_rt->next = p2_rt; | |||
p2_rt->prev = p1_rt; | p2_rt->prev = p1_rt; | |||
outRec1->pts = p2_rt; | outRec1->pts = p2_rt; | |||
} else | } else | |||
{ | { | |||
//x y z a b c | //x y z a b c | |||
p2_rt->next = p1_lft; | p2_rt->next = p1_lft; | |||
p1_lft->prev = p2_rt; | p1_lft->prev = p2_rt; | |||
p2_lft->prev = p1_rt; | p2_lft->prev = p1_rt; | |||
p1_rt->next = p2_lft; | p1_rt->next = p2_lft; | |||
outRec1->pts = p2_lft; | outRec1->pts = p2_lft; | |||
} | } | |||
side = esLeft; | side = esLeft; | |||
} else | } else | |||
{ | { | |||
if( e2->side == esRight ) | if( e2->side == esRight ) | |||
{ | { | |||
//a b c z y x | //a b c z y x | |||
ReversePolyPtLinks( *p2_lft ); | ReversePolyPtLinks(p2_lft); | |||
p1_rt->next = p2_rt; | p1_rt->next = p2_rt; | |||
p2_rt->prev = p1_rt; | p2_rt->prev = p1_rt; | |||
p2_lft->next = p1_lft; | p2_lft->next = p1_lft; | |||
p1_lft->prev = p2_lft; | p1_lft->prev = p2_lft; | |||
} else | } else | |||
{ | { | |||
//a b c x y z | //a b c x y z | |||
p1_rt->next = p2_lft; | p1_rt->next = p2_lft; | |||
p2_lft->prev = p1_rt; | p2_lft->prev = p1_rt; | |||
p1_lft->prev = p2_rt; | p1_lft->prev = p2_rt; | |||
skipping to change at line 2545 | skipping to change at line 2553 | |||
else | else | |||
e = eNext; | e = eNext; | |||
} | } | |||
if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; | if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; | |||
else break; | else break; | |||
} | } | |||
m_SortedEdges = 0; | m_SortedEdges = 0; | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
bool ProcessParam1BeforeParam2(IntersectNode &node1, IntersectNode &node2) | bool ProcessParam1BeforeParam2(const IntersectNode &node1, const IntersectN ode &node2) | |||
{ | { | |||
bool result; | bool result; | |||
if (node1.pt.Y == node2.pt.Y) | if (node1.pt.Y == node2.pt.Y) | |||
{ | { | |||
if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) | if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) | |||
{ | { | |||
result = node2.pt.X > node1.pt.X; | result = node2.pt.X > node1.pt.X; | |||
return node2.edge1->dx > 0 ? !result : result; | return node2.edge1->dx > 0 ? !result : result; | |||
} | } | |||
else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) | else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) | |||
skipping to change at line 2937 | skipping to change at line 2945 | |||
void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt) | void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt) | |||
{ | { | |||
AddOutPt(edge1, pt); | AddOutPt(edge1, pt); | |||
AddOutPt(edge2, pt); | AddOutPt(edge2, pt); | |||
SwapSides( *edge1 , *edge2 ); | SwapSides( *edge1 , *edge2 ); | |||
SwapPolyIndexes( *edge1 , *edge2 ); | SwapPolyIndexes( *edge1 , *edge2 ); | |||
} | } | |||
//---------------------------------------------------------------------- | //---------------------------------------------------------------------- | |||
void Clipper::JoinCommonEdges(bool fixHoleLinkages) | bool Clipper::JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2) | |||
{ | { | |||
for (JoinList::size_type i = 0; i < m_Joins.size(); i++) | ||||
{ | ||||
JoinRec* j = m_Joins[i]; | ||||
OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; | OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; | |||
OutPt *pp1a = outRec1->pts; | ||||
OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; | OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; | |||
if (!outRec1 || !outRec2) return false; | ||||
OutPt *pp1a = outRec1->pts; | ||||
OutPt *pp2a = outRec2->pts; | OutPt *pp2a = outRec2->pts; | |||
IntPoint pt1 = j->pt2a, pt2 = j->pt2b; | IntPoint pt1 = j->pt2a, pt2 = j->pt2b; | |||
IntPoint pt3 = j->pt1a, pt4 = j->pt1b; | IntPoint pt3 = j->pt1a, pt4 = j->pt1b; | |||
if (!FindSegment(pp1a, pt1, pt2)) continue; | if (!FindSegment(pp1a, pt1, pt2)) return false; | |||
if (j->poly1Idx == j->poly2Idx) | if (outRec1 == outRec2) | |||
{ | { | |||
//we're searching the same polygon for overlapping segments so | //we're searching the same polygon for overlapping segments so | |||
//segment 2 mustn't be the same as segment 1 ... | //segment 2 mustn't be the same as segment 1 ... | |||
pp2a = pp1a->next; | pp2a = pp1a->next; | |||
if (!FindSegment(pp2a, pt3, pt4) || (pp2a == pp1a)) continue; | if (!FindSegment(pp2a, pt3, pt4) || (pp2a == pp1a)) return false; | |||
} | } | |||
else if (!FindSegment(pp2a, pt3, pt4)) continue; | else if (!FindSegment(pp2a, pt3, pt4)) return false; | |||
if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) continue; | if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) return false; | |||
OutPt *p1, *p2, *p3, *p4; | OutPt *p3, *p4, *prev = pp1a->prev; | |||
OutPt *prev = pp1a->prev; | ||||
//get p1 & p2 polypts - the overlap start & endpoints on poly1 | //get p1 & p2 polypts - the overlap start & endpoints on poly1 | |||
if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a; | if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a; | |||
else if (PointsEqual(prev->pt, pt1)) p1 = prev; | else if (PointsEqual(prev->pt, pt1)) p1 = prev; | |||
else p1 = InsertPolyPtBetween(pp1a, prev, pt1); | else p1 = InsertPolyPtBetween(pp1a, prev, pt1); | |||
if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a; | if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a; | |||
else if (PointsEqual(prev->pt, pt2)) p2 = prev; | else if (PointsEqual(prev->pt, pt2)) p2 = prev; | |||
else if ((p1 == pp1a) || (p1 == prev)) | else if ((p1 == pp1a) || (p1 == prev)) | |||
p2 = InsertPolyPtBetween(pp1a, prev, pt2); | p2 = InsertPolyPtBetween(pp1a, prev, pt2); | |||
else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2)) | else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2)) | |||
skipping to change at line 2996 | skipping to change at line 3001 | |||
p4 = InsertPolyPtBetween(pp2a, p3, pt2); else | p4 = InsertPolyPtBetween(pp2a, p3, pt2); else | |||
p4 = InsertPolyPtBetween(p3, prev, pt2); | p4 = InsertPolyPtBetween(p3, prev, pt2); | |||
//p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ... | //p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ... | |||
if (p1->next == p2 && p3->prev == p4) | if (p1->next == p2 && p3->prev == p4) | |||
{ | { | |||
p1->next = p3; | p1->next = p3; | |||
p3->prev = p1; | p3->prev = p1; | |||
p2->prev = p4; | p2->prev = p4; | |||
p4->next = p2; | p4->next = p2; | |||
return true; | ||||
} | } | |||
else if (p1->prev == p2 && p3->next == p4) | else if (p1->prev == p2 && p3->next == p4) | |||
{ | { | |||
p1->prev = p3; | p1->prev = p3; | |||
p3->next = p1; | p3->next = p1; | |||
p2->next = p4; | p2->next = p4; | |||
p4->prev = p2; | p4->prev = p2; | |||
return true; | ||||
} | } | |||
else | else | |||
continue; //an orientation is probably wrong | return false; //an orientation is probably wrong | |||
} | ||||
//---------------------------------------------------------------------- | ||||
if (j->poly2Idx == j->poly1Idx) | void Clipper::FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx) | |||
{ | ||||
for (JoinList::size_type k = startIdx; k < m_Joins.size(); k++) | ||||
{ | ||||
JoinRec* j2 = m_Joins[k]; | ||||
if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, pt)) | ||||
j2->poly1Idx = j->poly2Idx; | ||||
if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, pt)) | ||||
j2->poly2Idx = j->poly2Idx; | ||||
} | ||||
} | ||||
//---------------------------------------------------------------------- | ||||
void Clipper::JoinCommonEdges(bool fixHoleLinkages) | ||||
{ | ||||
for (JoinList::size_type i = 0; i < m_Joins.size(); i++) | ||||
{ | ||||
JoinRec* j = m_Joins[i]; | ||||
OutPt *p1, *p2; | ||||
if (!JoinPoints(j, p1, p2)) return; | ||||
OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; | ||||
OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; | ||||
if (outRec1 == outRec2) | ||||
{ | { | |||
//instead of joining two polygons, we've just created a new one by | //instead of joining two polygons, we've just created a new one by | |||
//splitting one polygon into two. | //splitting one polygon into two. | |||
outRec1->pts = GetBottomPt(p1); | outRec1->pts = GetBottomPt(p1); | |||
outRec1->bottomPt = outRec1->pts; | outRec1->bottomPt = outRec1->pts; | |||
outRec1->bottomPt->idx = outRec1->idx; | outRec1->bottomPt->idx = outRec1->idx; | |||
outRec2 = CreateOutRec(); | outRec2 = CreateOutRec(); | |||
m_PolyOuts.push_back(outRec2); | m_PolyOuts.push_back(outRec2); | |||
outRec2->idx = (int)m_PolyOuts.size()-1; | outRec2->idx = (int)m_PolyOuts.size()-1; | |||
j->poly2Idx = outRec2->idx; | j->poly2Idx = outRec2->idx; | |||
outRec2->pts = GetBottomPt(p2); | outRec2->pts = GetBottomPt(p2); | |||
outRec2->bottomPt = outRec2->pts; | outRec2->bottomPt = outRec2->pts; | |||
outRec2->bottomPt->idx = outRec2->idx; | outRec2->bottomPt->idx = outRec2->idx; | |||
int K = 0; | ||||
if (PointInPolygon(outRec2->pts->pt, outRec1->pts, m_UseFullRange)) | if (PointInPolygon(outRec2->pts->pt, outRec1->pts, m_UseFullRange)) | |||
{ | { | |||
//outRec2 is contained by outRec1 ... | //outRec2 is contained by outRec1 ... | |||
K = 1; | ||||
outRec2->isHole = !outRec1->isHole; | outRec2->isHole = !outRec1->isHole; | |||
outRec2->FirstLeft = outRec1; | outRec2->FirstLeft = outRec1; | |||
FixupJoinRecs(j, p2, i+1); | ||||
FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation | ||||
FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs() | ||||
if (outRec2->pts && outRec2->isHole == | ||||
(m_ReverseOutput ^ Orientation(outRec2, m_UseFullRange))) | ||||
ReversePolyPtLinks(outRec2->pts); | ||||
} else if (PointInPolygon(outRec1->pts->pt, outRec2->pts, m_UseFullRa nge)) | } else if (PointInPolygon(outRec1->pts->pt, outRec2->pts, m_UseFullRa nge)) | |||
{ | { | |||
//outRec1 is contained by outRec2 ... | //outRec1 is contained by outRec2 ... | |||
K = 2; | ||||
outRec2->isHole = outRec1->isHole; | outRec2->isHole = outRec1->isHole; | |||
outRec1->isHole = !outRec2->isHole; | outRec1->isHole = !outRec2->isHole; | |||
outRec2->FirstLeft = outRec1->FirstLeft; | outRec2->FirstLeft = outRec1->FirstLeft; | |||
outRec1->FirstLeft = outRec2; | outRec1->FirstLeft = outRec2; | |||
} else | ||||
FixupJoinRecs(j, p2, i+1); | ||||
FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation | ||||
FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs() | ||||
if (outRec1->isHole == | ||||
(m_ReverseOutput ^ Orientation(outRec1, m_UseFullRange))) | ||||
ReversePolyPtLinks(outRec1->pts); | ||||
//make sure any contained holes now link to the correct polygon ... | ||||
if (fixHoleLinkages && outRec1->isHole) | ||||
for (PolyOutList::size_type k = 0; k < m_PolyOuts.size(); ++k) | ||||
{ | ||||
OutRec *orec = m_PolyOuts[k]; | ||||
if (orec->isHole && orec->bottomPt && orec->FirstLeft == outRec | ||||
1) | ||||
orec->FirstLeft = outRec2; | ||||
} | ||||
} | ||||
else | ||||
{ | { | |||
//the 2 polygons are completely separate ... | ||||
outRec2->isHole = outRec1->isHole; | outRec2->isHole = outRec1->isHole; | |||
outRec2->FirstLeft = outRec1->FirstLeft; | outRec2->FirstLeft = outRec1->FirstLeft; | |||
} | ||||
//now fixup any subsequent joins that match this polygon | ||||
for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) | ||||
{ | ||||
JoinRec* j2 = m_Joins[k]; | ||||
if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, p2)) | ||||
j2->poly1Idx = j->poly2Idx; | ||||
if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, p2)) | ||||
j2->poly2Idx = j->poly2Idx; | ||||
} | ||||
FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation | FixupJoinRecs(j, p2, i+1); | |||
FixupOutPolygon(*outRec2); // but AFTER calling PointIsVertex() | FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation | |||
FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs() | ||||
switch( K ) { | if (fixHoleLinkages && outRec2->pts) | |||
case 1: | for (PolyOutList::size_type k = 0; k < m_PolyOuts.size(); ++k) | |||
{ | { | |||
if (outRec2->pts && outRec2->isHole == | OutRec *orec = m_PolyOuts[k]; | |||
(m_ReverseOutput ^ Orientation(outRec2, m_UseFullRange))) | if (orec->isHole && orec->bottomPt && orec->FirstLeft == outRec | |||
ReversePolyPtLinks(*outRec2->pts); | 1 && | |||
break; | PointInPolygon(orec->bottomPt->pt, outRec2->pts, m_UseFullRan | |||
} | ge)) | |||
case 2: | orec->FirstLeft = outRec2; | |||
{ | ||||
if (outRec1->pts) | ||||
{ | ||||
if (outRec1->isHole == | ||||
(m_ReverseOutput ^ Orientation(outRec1, m_UseFullRange))) | ||||
ReversePolyPtLinks(*outRec1->pts); | ||||
//make sure any contained holes now link to the correct polyg | ||||
on ... | ||||
if (fixHoleLinkages && outRec1->isHole) | ||||
for (PolyOutList::size_type k = 0; k < m_PolyOuts.size(); + | ||||
+k) | ||||
{ | ||||
OutRec *orec = m_PolyOuts[k]; | ||||
if (orec->isHole && orec->bottomPt && orec->FirstLeft == | ||||
outRec1) | ||||
orec->FirstLeft = outRec2; | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
default: | ||||
{ | ||||
//make sure any contained holes now link to the correct polygon | ||||
... | ||||
if (fixHoleLinkages) | ||||
for (PolyOutList::size_type k = 0; k < m_PolyOuts.size(); ++k | ||||
) | ||||
{ | ||||
OutRec *orec = m_PolyOuts[k]; | ||||
if (orec->isHole && orec->bottomPt && orec->FirstLeft == ou | ||||
tRec1 && | ||||
!PointInPolygon(orec->bottomPt->pt, outRec1->pts, m_UseFu | ||||
llRange)) | ||||
orec->FirstLeft = outRec2; | ||||
} | ||||
} | } | |||
} | } | |||
if (outRec1->pts && Orientation(outRec1, m_UseFullRange) != (Area(*ou tRec1, m_UseFullRange) > 0)) | if (Orientation(outRec1, m_UseFullRange) != (Area(*outRec1, m_UseFull Range) >= 0)) | |||
DisposeBottomPt(*outRec1); | DisposeBottomPt(*outRec1); | |||
if (outRec2->pts && Orientation(outRec2, m_UseFullRange) != (Area(*ou tRec2, m_UseFullRange) > 0)) | if (Orientation(outRec2, m_UseFullRange) != (Area(*outRec2, m_UseFull Range) >= 0)) | |||
DisposeBottomPt(*outRec2); | DisposeBottomPt(*outRec2); | |||
} else | } else | |||
{ | { | |||
//joined 2 polygons together ... | //joined 2 polygons together ... | |||
//make sure any holes contained by outRec2 now link to outRec1 ... | //make sure any holes contained by outRec2 now link to outRec1 ... | |||
if (fixHoleLinkages) | if (fixHoleLinkages) | |||
for (PolyOutList::size_type k = 0; k < m_PolyOuts.size(); ++k) | for (PolyOutList::size_type k = 0; k < m_PolyOuts.size(); ++k) | |||
if (m_PolyOuts[k]->isHole && m_PolyOuts[k]->bottomPt && | if (m_PolyOuts[k]->isHole && m_PolyOuts[k]->bottomPt && | |||
skipping to change at line 3185 | skipping to change at line 3201 | |||
for (int i = 0; i < n; ++i) | for (int i = 0; i < n; ++i) | |||
{ | { | |||
result[i].X = pt.X + Round(std::cos(a)*r); | result[i].X = pt.X + Round(std::cos(a)*r); | |||
result[i].Y = pt.Y + Round(std::sin(a)*r); | result[i].Y = pt.Y + Round(std::sin(a)*r); | |||
a += da; | a += da; | |||
} | } | |||
return result; | return result; | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
DoublePoint GetUnitNormal( const IntPoint &pt1, const IntPoint &pt2) | DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) | |||
{ | { | |||
if(pt2.X == pt1.X && pt2.Y == pt1.Y) | if(pt2.X == pt1.X && pt2.Y == pt1.Y) | |||
return DoublePoint(0, 0); | return DoublePoint(0, 0); | |||
double dx = (double)(pt2.X - pt1.X); | double dx = (double)(pt2.X - pt1.X); | |||
double dy = (double)(pt2.Y - pt1.Y); | double dy = (double)(pt2.Y - pt1.Y); | |||
double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); | double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); | |||
dx *= f; | dx *= f; | |||
dy *= f; | dy *= f; | |||
return DoublePoint(dy, -dx); | return DoublePoint(dy, -dx); | |||
skipping to change at line 3215 | skipping to change at line 3231 | |||
Polygon* m_curr_poly; | Polygon* m_curr_poly; | |||
std::vector<DoublePoint> normals; | 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; | size_t m_i, m_j, m_k; | |||
static const int buffLength = 128; | static const int buffLength = 128; | |||
JoinType m_jointype; | JoinType m_jointype; | |||
public: | public: | |||
PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, | PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, | |||
double delta, JoinType jointype, double MiterLimit) | double delta, JoinType jointype, double MiterLimit, bool CheckInputs) | |||
{ | { | |||
//nb precondition - out_polys != ptsin_polys | //nb precondition - out_polys != ptsin_polys | |||
if (NEAR_ZERO(delta)) | if (NEAR_ZERO(delta)) | |||
{ | { | |||
out_polys = in_polys; | out_polys = in_polys; | |||
return; | return; | |||
} | } | |||
this->m_p = in_polys; | this->m_p = in_polys; | |||
this->m_delta = delta; | this->m_delta = delta; | |||
this->m_jointype = jointype; | 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 (CheckInputs) | ||||
{ | ||||
size_t Len = m_p.size(), botI = 0; | ||||
while (botI < Len && m_p[botI].size() == 0) botI++; | ||||
if (botI == Len) return; | ||||
//botPt: used to find the lowermost (in inverted Y-axis) & leftmost p | ||||
oint | ||||
//This point (on m_p[botI]) must be on an outer polygon ring and if | ||||
//its orientation is false (counterclockwise) then assume all polygon | ||||
s | ||||
//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); | ||||
} | ||||
if (MiterLimit <= 1) MiterLimit = 1; | if (MiterLimit <= 1) MiterLimit = 1; | |||
m_RMin = 2/(MiterLimit*MiterLimit); | m_RMin = 2/(MiterLimit*MiterLimit); | |||
double deltaSq = delta*delta; | double deltaSq = delta*delta; | |||
out_polys.clear(); | out_polys.clear(); | |||
out_polys.resize(in_polys.size()); | out_polys.resize(m_p.size()); | |||
for (m_i = 0; m_i < in_polys.size(); m_i++) | for (m_i = 0; m_i < m_p.size(); m_i++) | |||
{ | { | |||
m_curr_poly = &out_polys[m_i]; | m_curr_poly = &out_polys[m_i]; | |||
size_t len = in_polys[m_i].size(); | size_t len = m_p[m_i].size(); | |||
if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && | 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--; | m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; | |||
//when 'shrinking' polygons - to minimize artefacts | //when 'shrinking' polygons - to minimize artefacts | |||
//strip those polygons that have an area < pi * delta^2 ... | //strip those polygons that have an area < pi * delta^2 ... | |||
double a1 = Area(in_polys[m_i]); | double a1 = Area(m_p[m_i]); | |||
if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } | if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } | |||
else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. ar ea | else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. ar ea | |||
if (len == 0 || (len < 3 && delta <= 0)) | if (len == 0 || (len < 3 && delta <= 0)) | |||
continue; | continue; | |||
else if (len == 1) | else if (len == 1) | |||
{ | { | |||
Polygon arc; | Polygon arc; | |||
arc = BuildArc(in_polys[m_i][len-1], 0, 2 * pi, delta); | arc = BuildArc(m_p[m_i][len-1], 0, 2 * pi, delta); | |||
out_polys[m_i] = arc; | out_polys[m_i] = arc; | |||
continue; | continue; | |||
} | } | |||
//build normals ... | //build normals ... | |||
normals.clear(); | normals.clear(); | |||
normals.resize(len); | normals.resize(len); | |||
normals[len-1] = GetUnitNormal(in_polys[m_i][len-1], in_polys[m_i][ 0]); | 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) | for (m_j = 0; m_j < len -1; ++m_j) | |||
normals[m_j] = GetUnitNormal(in_polys[m_i][m_j], in_polys[m_i][ m_j+1]); | normals[m_j] = GetUnitNormal(m_p[m_i][m_j], m_p[m_i][m_j+1]); | |||
m_k = len -1; | m_k = len -1; | |||
for (m_j = 0; m_j < len; ++m_j) | for (m_j = 0; m_j < len; ++m_j) | |||
{ | { | |||
switch (jointype) | switch (jointype) | |||
{ | { | |||
case jtMiter: | case jtMiter: | |||
{ | { | |||
m_R = 1 + (normals[m_j].X*normals[m_k].X + | m_R = 1 + (normals[m_j].X*normals[m_k].X + | |||
normals[m_j].Y*normals[m_k].Y); | normals[m_j].Y*normals[m_k].Y); | |||
skipping to change at line 3400 | skipping to change at line 3451 | |||
for (Polygon::size_type m = 0; m < arc.size(); m++) | for (Polygon::size_type m = 0; m < arc.size(); m++) | |||
AddPoint(arc[m]); | AddPoint(arc[m]); | |||
} | } | |||
} | } | |||
else | else | |||
AddPoint(m_p[m_i][m_j]); | AddPoint(m_p[m_i][m_j]); | |||
AddPoint(pt2); | AddPoint(pt2); | |||
} | } | |||
//------------------------------------------------------------------------- - | //------------------------------------------------------------------------- - | |||
bool UpdateBotPt(const IntPoint &pt, IntPoint &botPt) | ||||
{ | ||||
if (pt.Y > botPt.Y || (pt.Y == botPt.Y && pt.X < botPt.X)) | ||||
{ | ||||
botPt = pt; | ||||
return true; | ||||
} | ||||
else return false; | ||||
} | ||||
//------------------------------------------------------------------------- | ||||
- | ||||
}; //end PolyOffsetBuilder | }; //end PolyOffsetBuilder | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, | void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, | |||
double delta, JoinType jointype, double MiterLimit) | double delta, JoinType jointype, double MiterLimit, bool CheckInputs) | |||
{ | { | |||
if (&out_polys == &in_polys) | if (&out_polys == &in_polys) | |||
{ | { | |||
Polygons poly2(in_polys); | Polygons poly2(in_polys); | |||
PolyOffsetBuilder(poly2, out_polys, delta, jointype, MiterLimit); | PolyOffsetBuilder(poly2, out_polys, delta, jointype, MiterLimit, CheckI nputs); | |||
} | } | |||
else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, MiterLimit); | else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, MiterLimit, CheckInputs); | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillT ype fillType) | void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillT ype fillType) | |||
{ | { | |||
Clipper c; | Clipper c; | |||
c.AddPolygon(in_poly, ptSubject); | c.AddPolygon(in_poly, ptSubject); | |||
c.Execute(ctUnion, out_polys, fillType, fillType); | c.Execute(ctUnion, out_polys, fillType, fillType); | |||
} | } | |||
//------------------------------------------------------------------------- ----- | //------------------------------------------------------------------------- ----- | |||
End of changes. 53 change blocks. | ||||
106 lines changed or deleted | 167 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |