Upgrade Clipper to 07b828b1 to fix self-intersecting polygons

This commit is contained in:
Eric Fischer 2016-03-18 16:31:56 -07:00
parent 7140a3dd91
commit 707036fe79
10 changed files with 549 additions and 107 deletions

View File

@ -69,7 +69,7 @@ SPACE = $(NULL) $(NULL)
test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) parallel-test
%.json.check:
./tippecanoe -f -o $@.mbtiles $(subst _, ,$(patsubst %.json.check,%,$(word 4,$(subst /, ,$@)))) $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json) < /dev/null
./tippecanoe -ad -f -o $@.mbtiles $(subst _, ,$(patsubst %.json.check,%,$(word 4,$(subst /, ,$@)))) $(wildcard $(subst $(SPACE),/,$(wordlist 1,2,$(subst /, ,$@)))/*.json) < /dev/null
./tippecanoe-decode $@.mbtiles > $@.out
cmp $(patsubst %.check,%,$@) $@.out
rm $@.out $@.mbtiles

View File

@ -633,7 +633,7 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
ip.X = TopX(Edge1, ip.Y);
return;
}
else if (Edge1.Dx == 0)
else if (Edge1.Dx == 0.0)
{
ip.X = Edge1.Bot.X;
if (IsHorizontal(Edge2))
@ -641,10 +641,12 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
else
{
b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx);
ip.Y = Round(ip.X / Edge2.Dx + b2);
if (Edge2.Bot.X == Edge1.Bot.X) ip.Y = Round(ip.X / Edge2.Dx + b2);
else if (Edge2.Bot.X < Edge1.Bot.X) ip.Y = Round((ip.X - 0.5) / Edge2.Dx + b2);
else ip.Y = Round((ip.X + 0.5) / Edge2.Dx + b2);
}
}
else if (Edge2.Dx == 0)
else if (Edge2.Dx == 0.0)
{
ip.X = Edge2.Bot.X;
if (IsHorizontal(Edge1))
@ -652,7 +654,9 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
else
{
b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx);
ip.Y = Round(ip.X / Edge1.Dx + b1);
if (Edge1.Bot.X == Edge2.Bot.X) ip.Y = Round(ip.X / Edge1.Dx + b1);
else if (Edge1.Bot.X < Edge2.Bot.X) ip.Y = Round((ip.X - 0.5) / Edge1.Dx + b1);
else ip.Y = Round((ip.X + 0.5) / Edge1.Dx + b1);
}
}
else
@ -665,6 +669,85 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
ip.X = Round(Edge1.Dx * q + b1);
else
ip.X = Round(Edge2.Dx * q + b2);
// the idea is simply to looking closer
// towards the origins of the lines (Edge1.Bot and Edge2.Bot)
// until we do not find pixels that both lines travel through
bool keep_searching = false;
double by1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx);
double by2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx);
double bx1 = Edge1.Bot.X - (Edge1.Bot.Y * Edge1.Dx);
double bx2 = Edge2.Bot.X - (Edge2.Bot.Y * Edge2.Dx);
do
{
keep_searching = false;
cInt y1 = ip.Y;
cInt y2 = ip.Y;
if (Edge1.Bot.X > ip.X)
{
y1 = Round((ip.X + 0.5) / Edge1.Dx + by1);
if (ip.Y >= Edge1.Bot.Y && y1 < Edge1.Bot.Y) y1 = Edge1.Bot.Y;
else if (ip.Y <= Edge1.Bot.Y && y1 > Edge1.Bot.Y) y1 = Edge1.Bot.Y;
}
else if (Edge1.Bot.X < ip.X)
{
y1 = Round((ip.X - 0.5) / Edge1.Dx + by1);
}
if (ip.Y >= Edge1.Bot.Y && y1 < Edge1.Bot.Y) y1 = Edge1.Bot.Y;
else if (ip.Y <= Edge1.Bot.Y && y1 > Edge1.Bot.Y) y1 = Edge1.Bot.Y;
if (Edge2.Bot.X > ip.X)
{
y2 = Round((ip.X + 0.5) / Edge2.Dx + by2);
}
else if (Edge2.Bot.X < ip.X)
{
y2 = Round((ip.X - 0.5) / Edge2.Dx + by2);
}
if (ip.Y >= Edge2.Bot.Y && y2 < Edge2.Bot.Y) y2 = Edge2.Bot.Y;
else if (ip.Y <= Edge2.Bot.Y && y2 > Edge2.Bot.Y) y2 = Edge2.Bot.Y;
cInt x1 = ip.X;
cInt x2 = ip.X;
if (Edge1.Bot.Y > ip.Y)
{
x1 = Round((ip.Y + 0.5) * Edge1.Dx + bx1);
}
else if (Edge1.Bot.Y < ip.Y)
{
x1 = Round((ip.Y - 0.5) * Edge1.Dx + bx1);
}
if (ip.X >= Edge1.Bot.X && x1 < Edge1.Bot.X) x1 = Edge1.Bot.X;
else if (ip.X <= Edge1.Bot.X && x1 > Edge1.Bot.X) x1 = Edge1.Bot.X;
if (Edge2.Bot.Y > ip.Y)
{
x2 = Round((ip.Y + 0.5) * Edge2.Dx + bx2);
}
else if (Edge2.Bot.Y < ip.Y)
{
x2 = Round((ip.Y - 0.5) * Edge2.Dx + bx2);
}
if (ip.X >= Edge2.Bot.X && x2 < Edge2.Bot.X) x2 = Edge2.Bot.X;
else if (ip.X <= Edge2.Bot.X && x2 > Edge2.Bot.X) x2 = Edge2.Bot.X;
if (y1 > ip.Y && y2 > ip.Y)
{
ip.Y = std::min(y1,y2);
keep_searching = true;
}
else if (y1 < ip.Y && y2 < ip.Y)
{
ip.Y = std::max(y1,y2);
keep_searching = true;
}
if (x1 > ip.X && x2 > ip.X)
{
ip.X = std::min(x1,x2);
keep_searching = true;
}
else if (x1 < ip.X && x2 < ip.X)
{
ip.X = std::max(x1,x2);
keep_searching = true;
}
}
while (keep_searching);
}
if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y)
@ -1618,7 +1701,10 @@ bool Clipper::ExecuteInternal()
FixupOutPolygon(*outRec);
}
if (m_StrictSimple) DoSimplePolygons();
if (m_StrictSimple)
{
DoSimplePolygons();
}
}
ClearJoins();
@ -1996,14 +2082,46 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
InsertEdgeIntoAEL(rb, 0);
SetWindingCount(*rb);
if (IsContributing(*rb))
Op1 = AddOutPt(rb, rb->Bot);
{
Op1 = AddOutPt(rb, rb->Bot);
TEdge* ePrev = rb->PrevInAEL;
if ((rb->OutIdx >= 0) && (rb->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
(ePrev->Curr.X == rb->Curr.X) && (ePrev->WindDelta != 0))
{
IntPoint pt = rb->Curr;
AddOutPt(ePrev, pt);
}
TEdge* eNext = rb->NextInAEL;
if ((rb->OutIdx >= 0) && (rb->WindDelta != 0) && eNext && (eNext->OutIdx >= 0) &&
(eNext->Curr.X == rb->Curr.X) && (eNext->WindDelta != 0))
{
IntPoint pt = rb->Curr;
AddOutPt(eNext, pt);
}
}
}
else if (!rb)
{
InsertEdgeIntoAEL(lb, 0);
SetWindingCount(*lb);
if (IsContributing(*lb))
Op1 = AddOutPt(lb, lb->Bot);
{
Op1 = AddOutPt(lb, lb->Bot);
TEdge* ePrev = lb->PrevInAEL;
if ((lb->OutIdx >= 0) && (lb->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
(ePrev->Curr.X == lb->Curr.X) && (ePrev->WindDelta != 0))
{
IntPoint pt = lb->Curr;
AddOutPt(ePrev, pt);
}
TEdge* eNext = lb->NextInAEL;
if ((lb->OutIdx >= 0) && (lb->WindDelta != 0) && eNext && (eNext->OutIdx >= 0) &&
(eNext->Curr.X == lb->Curr.X) && (eNext->WindDelta != 0))
{
IntPoint pt = lb->Curr;
AddOutPt(eNext, pt);
}
}
InsertScanbeam(lb->Top.Y);
}
else
@ -2014,7 +2132,23 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
rb->WindCnt = lb->WindCnt;
rb->WindCnt2 = lb->WindCnt2;
if (IsContributing(*lb))
Op1 = AddLocalMinPoly(lb, rb, lb->Bot);
{
Op1 = AddLocalMinPoly(lb, rb, lb->Bot);
TEdge* ePrev = lb->PrevInAEL;
if ((lb->OutIdx >= 0) && (lb->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
(ePrev->Curr.X == lb->Curr.X) && (ePrev->WindDelta != 0))
{
IntPoint pt = lb->Curr;
AddOutPt(ePrev, pt);
}
TEdge* eNext = rb->NextInAEL;
if ((rb->OutIdx >= 0) && (rb->WindDelta != 0) && eNext && (eNext->OutIdx >= 0) &&
(eNext->Curr.X == rb->Curr.X) && (eNext->WindDelta != 0))
{
IntPoint pt = rb->Curr;
AddOutPt(eNext, pt);
}
}
InsertScanbeam(lb->Top.Y);
}
@ -2793,6 +2927,25 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
if(horzEdge->OutIdx >= 0)
{
op1 = AddOutPt( horzEdge, horzEdge->Top);
//When StrictlySimple and 'horzEdge' is being touched by another edge, then
//make sure both edges have a vertex here ...
if (m_StrictSimple)
{
TEdge* ePrev = horzEdge->PrevInAEL;
if ((horzEdge->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
(ePrev->Curr.X == horzEdge->Top.X) && (ePrev->WindDelta != 0))
{
IntPoint pt = horzEdge->Top;
AddOutPt(ePrev, pt);
}
TEdge* eNext = horzEdge->NextInAEL;
if ((horzEdge->WindDelta != 0) && eNext && (eNext->OutIdx >= 0) &&
(eNext->Curr.X == horzEdge->Top.X) && (eNext->WindDelta != 0))
{
IntPoint pt = horzEdge->Top;
AddOutPt(eNext, pt);
}
}
UpdateEdgeIntoAEL(horzEdge);
if (horzEdge->WindDelta == 0) return;
//nb: HorzEdge is no longer horizontal here
@ -2962,11 +3115,20 @@ void Clipper::DoMaxima(TEdge *e)
if (!eMaxPair)
{
if (e->OutIdx >= 0)
{
AddOutPt(e, e->Top);
}
DeleteFromAEL(e);
return;
}
TEdge* ePrev = e->PrevInAEL;
if (ePrev && ePrev->Curr.X == e->Top.X && ePrev->Top != e->Top && ePrev->OutIdx >= 0 &&
ePrev->WindDelta != 0 && e->OutIdx >= 0 && e->WindDelta != 0)
{
IntPoint pt = e->Top;
AddOutPt(ePrev, pt);
}
TEdge* eNext = e->NextInAEL;
while(eNext && eNext != eMaxPair)
{
@ -2974,6 +3136,13 @@ void Clipper::DoMaxima(TEdge *e)
SwapPositionsInAEL(e, eNext);
eNext = e->NextInAEL;
}
eNext = eMaxPair->NextInAEL;
if (eNext && eNext->Curr.X == e->Top.X && eNext->Top != e->Top && eNext->OutIdx >= 0 &&
eNext->WindDelta != 0 && e->OutIdx >= 0 && e->WindDelta != 0)
{
IntPoint pt = e->Top;
AddOutPt(eNext, pt);
}
if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned)
{
@ -2982,9 +3151,9 @@ void Clipper::DoMaxima(TEdge *e)
}
else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 )
{
if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top);
DeleteFromAEL(e);
DeleteFromAEL(eMaxPair);
AddLocalMaxPoly(e, eMaxPair, e->Top);
DeleteFromAEL(e);
DeleteFromAEL(eMaxPair);
}
#ifdef use_lines
else if (e->WindDelta == 0)
@ -3038,7 +3207,14 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
{
UpdateEdgeIntoAEL(e);
if (e->OutIdx >= 0)
{
AddOutPt(e, e->Bot);
if (m_StrictSimple)
{
m_Maxima.push_back(e->Top.X);
m_Maxima.push_back(e->Bot.X);
}
}
AddEdgeToSEL(e);
}
else
@ -3471,8 +3647,46 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
if (isHorizontal && (j->OffPt == j->OutPt1->Pt) &&
(j->OffPt == j->OutPt2->Pt))
{
if (outRec1 != outRec2)
{
// First Op1 Next and Op2 Prev
op1b = j->OutPt1->Next;
while (op1b != op1 && (op1b->Pt == j->OffPt))
op1b = op1b->Next;
op2b = j->OutPt2->Prev;
while (op2b != op2 && (op2b->Pt == j->OffPt))
op2b = op2b->Prev;
if (op1b->Pt == op2b->Pt)
{
OutPt* op1b_prev = op1b->Prev;
OutPt* op2b_next = op2b->Next;
op1b->Prev = op2b;
op2b->Next = op1b;
op1b_prev->Next = op2b_next;
op2b_next->Prev = op1b_prev;
return true;
}
// Second Op1 Prev and Op2 Next
op2b = j->OutPt2->Next;
while (op2b != op2 && (op2b->Pt == j->OffPt))
op2b = op2b->Next;
op1b = j->OutPt1->Prev;
while (op1b != op1 && (op1b->Pt == j->OffPt))
op1b = op1b->Prev;
if (op2b->Pt == op1b->Pt)
{
OutPt* op2b_prev = op2b->Prev;
OutPt* op1b_next = op1b->Next;
op2b->Prev = op1b;
op1b->Next = op2b;
op2b_prev->Next = op1b_next;
op1b_next->Prev = op2b_prev;
return true;
}
return false;
}
//Strictly Simple join ...
if (outRec1 != outRec2) return false;
op1b = j->OutPt1->Next;
while (op1b != op1 && (op1b->Pt == j->OffPt))
op1b = op1b->Next;
@ -3643,7 +3857,7 @@ void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec)
//It's possible that these polygons now wrap around other polygons, so check
//every polygon that's also contained by OuterOutRec's FirstLeft container
//(including 0) to see if they've become inner to the new inner polygon ...
OutRec* orfl = OuterOutRec->FirstLeft;
OutRec* orfl = ParseFirstLeft(OuterOutRec->FirstLeft);
for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
{
OutRec* outRec = m_PolyOuts[i];
@ -3657,7 +3871,7 @@ void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec)
outRec->FirstLeft = InnerOutRec;
else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts))
outRec->FirstLeft = OuterOutRec;
else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec)
else if (firstLeft == InnerOutRec || firstLeft == OuterOutRec)
outRec->FirstLeft = orfl;
}
}
@ -4218,65 +4432,261 @@ void ClipperOffset::DoRound(int j, int k)
// Miscellaneous public functions
//------------------------------------------------------------------------------
bool SortOutPt(OutPt* op1, OutPt* op2)
{
if (op1->Pt.Y > op2->Pt.Y)
{
return true;
}
else if (op1->Pt.Y < op2->Pt.Y)
{
return false;
}
else if (op1->Pt.X < op2->Pt.X)
{
return true;
}
else if (op1->Pt.X > op2->Pt.X)
{
return false;
}
else
{
return (op1->Idx < op2->Idx);
}
}
//-----------------------------------------------------------------------------
struct OutPtIntersect
{
OutPt * op1;
OutPt * op2;
};
//-----------------------------------------------------------------------------
void Clipper::DoSimplePolygons()
{
PolyOutList::size_type i = 0;
while (i < m_PolyOuts.size())
{
OutRec* outrec = m_PolyOuts[i++];
OutPt* op = outrec->Pts;
if (!op || outrec->IsOpen) continue;
do //for each Pt in Polygon until duplicate found do ...
std::vector < OutPt*> m_OutPts;
{
OutPt* op2 = op->Next;
while (op2 != outrec->Pts)
{
if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op)
PolyOutList::size_type i = 0;
while (i < m_PolyOuts.size())
{
//split the polygon into two ...
OutPt* op3 = op->Prev;
OutPt* op4 = op2->Prev;
op->Prev = op4;
op4->Next = op;
op2->Prev = op3;
op3->Next = op2;
outrec->Pts = op;
OutRec* outrec2 = CreateOutRec();
outrec2->Pts = op2;
UpdateOutPtIdxs(*outrec2);
if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))
{
//OutRec2 is contained by OutRec1 ...
outrec2->IsHole = !outrec->IsHole;
outrec2->FirstLeft = outrec;
if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
}
else
if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))
{
//OutRec1 is contained by OutRec2 ...
outrec2->IsHole = outrec->IsHole;
outrec->IsHole = !outrec2->IsHole;
outrec2->FirstLeft = outrec->FirstLeft;
outrec->FirstLeft = outrec2;
if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
OutRec* outrec = m_PolyOuts[i++];
OutPt* op = outrec->Pts;
if (!op || outrec->IsOpen) continue;
do
{
m_OutPts.push_back(op);
op = op->Next;
}
else
{
//the 2 polygons are separate ...
outrec2->IsHole = outrec->IsHole;
outrec2->FirstLeft = outrec->FirstLeft;
if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
}
op2 = op; //ie get ready for the Next iteration
while (op != outrec->Pts);
}
}
std::stable_sort(m_OutPts.begin(), m_OutPts.end(), SortOutPt);
std::list<OutPtIntersect> dupeRec;
std::size_t count = 0;
for (std::size_t i = 1; i < m_OutPts.size(); ++i)
{
if (m_OutPts[i]->Pt == m_OutPts[i-1]->Pt)
{
++count;
continue;
}
if (count > 0)
{
for (std::size_t j = (i - count - 1); j < i; ++j)
{
if (m_OutPts[j]->Idx < 0) continue;
OutRec * outRec_j = GetOutRec(m_OutPts[j]->Idx);
int idx_j = outRec_j->Idx;
bool hole_j = outRec_j->IsHole;
for (std::size_t k = j + 1; k < i; ++k)
{
if (m_OutPts[k]->Idx < 0) continue;
OutRec * outRec_k = GetOutRec(m_OutPts[k]->Idx);
int idx_k = outRec_k->Idx;
bool hole_k = outRec_k->IsHole;
if (idx_k == idx_j)
{
OutPt * op = m_OutPts[j];
OutPt * op2 = m_OutPts[k];
OutRec * outrec = outRec_j;
if (op != op2 && op2->Next != op && op2->Prev != op)
{
//split the polygon into two ...
OutPt* op3 = op->Prev;
OutPt* op4 = op2->Prev;
op->Prev = op4;
op4->Next = op;
op2->Prev = op3;
op3->Next = op2;
outrec->Pts = op;
OutRec* outrec2 = CreateOutRec();
outrec2->Pts = op2;
UpdateOutPtIdxs(*outrec2);
if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))
{
//OutRec2 is contained by OutRec1 ...
outrec2->IsHole = !outrec->IsHole;
outrec2->FirstLeft = outrec;
if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
}
else if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))
{
//OutRec1 is contained by OutRec2 ...
outrec2->IsHole = outrec->IsHole;
outrec->IsHole = !outrec2->IsHole;
outrec2->FirstLeft = outrec->FirstLeft;
outrec->FirstLeft = outrec2;
if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
}
else
{
//the 2 polygons are separate ...
outrec2->IsHole = outrec->IsHole;
outrec2->FirstLeft = outrec->FirstLeft;
if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
}
OutPtIntersect intPt = { op, op2 };
dupeRec.push_back(intPt);
}
continue;
}
bool found = false;
if (hole_k != hole_j)
{
auto dupe = dupeRec.begin();
while (dupe != dupeRec.end())
{
int idx1 = GetOutRec(dupe->op1->Idx)->Idx;
int idx2 = GetOutRec(dupe->op2->Idx)->Idx;
if ((idx1 == idx_j && idx2 == idx_k) ||
(idx2 == idx_j && idx1 == idx_k))
{
found = true;
if (dupe->op1->Pt == m_OutPts[j]->Pt)
{
break;
}
OutPt* op1 = dupe->op1;
OutPt* op2 = dupe->op2;
OutPt* op1_next = op1->Next;
OutPt* op2_next = op2->Next;
OutPt* op1b;
OutPt* op2b;
OutRec* outRec1;
OutRec* outRec2;
bool one_is_j = false;
if (idx1 == idx_j)
{
one_is_j = true;
outRec1 = outRec_j;
outRec2 = outRec_k;
op1b = m_OutPts[j];
op2b = m_OutPts[k];
}
else
{
outRec1 = outRec_k;
outRec2 = outRec_j;
op1b = m_OutPts[k];
op2b = m_OutPts[j];
}
OutPt* op1b_next = op1b->Next;
OutPt* op2b_next = op2b->Next;
op2b->Next= op1b_next;
op1b->Next = op2b_next;
op2b_next->Prev = op1b;
op1b_next->Prev = op2b;
op2->Next= op1_next;
op1->Next = op2_next;
op2_next->Prev = op1;
op1_next->Prev = op2;
bool holeState = (Area(op1b) > 0) && m_ReverseOutput;
if (outRec1->IsHole == holeState) // outRec1 is "parent"
{
outRec1->Pts = op1b;
outRec1->BottomPt = 0;
outRec2->Pts = 0;
outRec2->BottomPt = 0;
outRec2->Idx = outRec1->Idx;
outRec2->FirstLeft = outRec1;
outRec2->IsHole = outRec1->IsHole;
if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1);
OutRec * outRec3 = CreateOutRec();
outRec3->Pts = op2b;
outRec3->IsHole = outRec1->IsHole;
UpdateOutPtIdxs(*outRec1);
UpdateOutPtIdxs(*outRec3);
outRec3->FirstLeft = outRec1->FirstLeft;
//fixup FirstLeft pointers that may need reassigning to OutRec3
if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec3);
PointCount(outRec1->Pts);
PointCount(outRec3->Pts);
if (one_is_j)
{
outRec_k = outRec3;
hole_k = outRec3->IsHole;
}
else
{
outRec_j = outRec3;
hole_j = outRec3->IsHole;
}
}
else
{
outRec2->Pts = op2b;
outRec2->BottomPt = 0;
outRec1->Pts = 0;
outRec1->BottomPt = 0;
outRec1->Idx = outRec2->Idx;
outRec1->FirstLeft = outRec2;
outRec1->IsHole = outRec2->IsHole;
if (m_UsingPolyTree) FixupFirstLefts3(outRec1, outRec2);
OutRec * outRec3 = CreateOutRec();
outRec3->Pts = op1b;
outRec3->IsHole = outRec2->IsHole;
UpdateOutPtIdxs(*outRec2);
UpdateOutPtIdxs(*outRec3);
outRec3->FirstLeft = outRec2->FirstLeft;
//fixup FirstLeft pointers that may need reassigning to OutRec3
if (m_UsingPolyTree) FixupFirstLefts1(outRec2, outRec3);
PointCount(outRec2->Pts);
PointCount(outRec3->Pts);
if (one_is_j)
{
outRec_j = outRec3;
hole_j = outRec3->IsHole;
}
else
{
outRec_k = outRec3;
hole_k = outRec3->IsHole;
}
}
dupe = dupeRec.erase(dupe);
break;
}
else
{
++dupe;
}
}
}
if (!found)
{
OutPtIntersect intPt = { m_OutPts[j], m_OutPts[k] };
dupeRec.push_back(intPt);
}
}
}
count = 0;
}
op2 = op2->Next;
}
op = op->Next;
}
while (op != outrec->Pts);
}
}
//------------------------------------------------------------------------------

View File

@ -328,6 +328,32 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int detail, int buffer, bool cl
return out;
}
void check_polygon(drawvec &geom) {
for (unsigned i = 0; i + 1 < geom.size(); i++) {
for (unsigned j = i + 1; j + 1 < geom.size(); j++) {
if (geom[i + 1].op == VT_LINETO && geom[j + 1].op == VT_LINETO) {
double s1_x = geom[i + 1].x - geom[i + 0].x;
double s1_y = geom[i + 1].y - geom[i + 0].y;
double s2_x = geom[j + 1].x - geom[j + 0].x;
double s2_y = geom[j + 1].y - geom[j + 0].y;
double s, t;
s = (-s1_y * (geom[i + 0].x - geom[j + 0].x) + s1_x * (geom[i + 0].y - geom[j + 0].y)) / (-s2_x * s1_y + s1_x * s2_y);
t = (s2_x * (geom[i + 0].y - geom[j + 0].y) - s2_y * (geom[i + 0].x - geom[j + 0].x)) / (-s2_x * s1_y + s1_x * s2_y);
if (t > 0 && t < 1 && s > 0 && s < 1) {
fprintf(stderr, "Internal error: self-intersecting polygon. %lld,%lld to %lld,%lld intersects %lld,%lld to %lld,%lld\n",
geom[i + 0].x, geom[i + 0].y,
geom[i + 1].x, geom[i + 1].y,
geom[j + 0].x, geom[j + 0].y,
geom[j + 1].x, geom[j + 1].y);
exit(EXIT_FAILURE);
}
}
}
}
}
drawvec close_poly(drawvec &geom) {
drawvec out;

View File

@ -30,3 +30,4 @@ drawvec simplify_lines(drawvec &geom, int z, int detail);
drawvec reorder_lines(drawvec &geom);
drawvec fix_polygon(drawvec &geom);
std::vector<drawvec> chop_polygon(std::vector<drawvec> &geoms);
void check_polygon(drawvec &geom);

View File

@ -204,13 +204,13 @@
,
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 15, "y": 5 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 172.309570, 55.776573 ], [ 171.562500, 54.370959 ], [ 174.023438, 47.279229 ], [ 180.439453, 45.058001 ], [ 180.439453, 40.647304 ], [ 169.991455, 40.647304 ], [ 169.425659, 40.979898 ], [ 161.718750, 45.336702 ], [ 157.500000, 53.722717 ], [ 157.060547, 54.514704 ], [ 157.060547, 55.307264 ], [ 157.648315, 56.022948 ], [ 172.441406, 56.022948 ], [ 172.309570, 55.776573 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 171.562500, 54.370959 ], [ 174.023438, 47.279229 ], [ 180.439453, 45.058001 ], [ 180.439453, 40.647304 ], [ 169.991455, 40.647304 ], [ 169.425659, 40.979898 ], [ 161.718750, 45.336702 ], [ 157.500000, 53.722717 ], [ 157.060547, 54.514704 ], [ 157.060547, 55.307264 ], [ 157.648315, 56.022948 ], [ 172.441406, 56.022948 ], [ 172.309570, 55.776573 ], [ 171.562500, 54.370959 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 15, "y": 4 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 180.000000, 61.650771 ], [ 174.023438, 58.816586 ], [ 172.309570, 55.776573 ], [ 172.172241, 55.528631 ], [ 157.241821, 55.528631 ], [ 157.500000, 55.841398 ], [ 163.476562, 62.431074 ], [ 170.507812, 64.776466 ], [ 175.187988, 66.513260 ], [ 175.676880, 66.687784 ], [ 180.439453, 66.687784 ], [ 180.439453, 61.850966 ], [ 180.000000, 61.650771 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 174.023438, 58.816586 ], [ 172.309570, 55.776573 ], [ 172.172241, 55.528631 ], [ 157.241821, 55.528631 ], [ 157.500000, 55.841398 ], [ 163.476562, 62.431074 ], [ 170.507812, 64.776466 ], [ 175.187988, 66.513260 ], [ 175.676880, 66.687784 ], [ 180.439453, 66.687784 ], [ 180.439453, 61.850966 ], [ 180.000000, 61.650771 ], [ 174.023438, 58.816586 ] ] ] } }
] }
] }
,
@ -234,7 +234,7 @@
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 0, "y": 9 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -179.868164, 61.710706 ], [ -180.219727, 61.551493 ], [ -180.219727, 61.710706 ], [ -179.868164, 61.710706 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -180.219727, 61.710706 ], [ -179.868164, 61.710706 ], [ -180.219727, 61.551493 ], [ -180.219727, 61.710706 ] ] ] } }
] }
] }
,
@ -438,25 +438,25 @@
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 11 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 173.485107, 48.922499 ], [ 174.023438, 47.279229 ], [ 180.219727, 45.135555 ], [ 180.219727, 40.813809 ], [ 169.708557, 40.813809 ], [ 169.425659, 40.979898 ], [ 168.750000, 41.376809 ], [ 168.530273, 41.504464 ], [ 168.530273, 49.066668 ], [ 173.435669, 49.066668 ], [ 173.485107, 48.922499 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 174.023438, 47.279229 ], [ 180.219727, 45.135555 ], [ 180.219727, 40.813809 ], [ 169.708557, 40.813809 ], [ 169.425659, 40.979898 ], [ 168.750000, 41.376809 ], [ 168.530273, 41.504464 ], [ 168.530273, 49.066668 ], [ 173.435669, 49.066668 ], [ 173.485107, 48.922499 ], [ 174.023438, 47.279229 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 10 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 172.309570, 55.776573 ], [ 171.562500, 54.369359 ], [ 173.485107, 48.922499 ], [ 173.531799, 48.777913 ], [ 168.530273, 48.777913 ], [ 168.530273, 55.899956 ], [ 172.375488, 55.899956 ], [ 172.309570, 55.776573 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 171.562500, 54.369359 ], [ 173.485107, 48.922499 ], [ 173.531799, 48.777913 ], [ 168.530273, 48.777913 ], [ 168.530273, 55.899956 ], [ 172.375488, 55.899956 ], [ 172.309570, 55.776573 ], [ 171.562500, 54.369359 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 9 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 179.901123, 61.606396 ], [ 174.023438, 58.815164 ], [ 172.306824, 55.776573 ], [ 172.240906, 55.652798 ], [ 168.530273, 55.652798 ], [ 168.530273, 61.710706 ], [ 180.131836, 61.710706 ], [ 179.901123, 61.606396 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 174.023438, 58.815164 ], [ 172.306824, 55.776573 ], [ 172.240906, 55.652798 ], [ 168.530273, 55.652798 ], [ 168.530273, 61.710706 ], [ 180.131836, 61.710706 ], [ 179.901123, 61.606396 ], [ 174.023438, 58.815164 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 31, "y": 8 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in" }, "features": [
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 180.000000, 61.650771 ], [ 179.901123, 61.606396 ], [ 179.670410, 61.501734 ], [ 168.530273, 61.501734 ], [ 168.530273, 64.135775 ], [ 170.507812, 64.775296 ], [ 175.190735, 66.513260 ], [ 175.435181, 66.600676 ], [ 180.219727, 66.600676 ], [ 180.219727, 61.751031 ], [ 180.000000, 61.650771 ] ] ] } }
{ "type": "Feature", "properties": { "boolean": true, "otherboolean": false, "stringify": "[\"yes\",27.00000000,27,1.4e27,{\"foo\":\"bar\"}]", "escape": "foo\u0001bar,ü\"\\/\u0008\u000c\u000a\u000d\u0009→", "prêt": "ready" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 179.901123, 61.606396 ], [ 179.670410, 61.501734 ], [ 168.530273, 61.501734 ], [ 168.530273, 64.135775 ], [ 170.507812, 64.775296 ], [ 175.190735, 66.513260 ], [ 175.435181, 66.600676 ], [ 180.219727, 66.600676 ], [ 180.219727, 61.751031 ], [ 180.000000, 61.650771 ], [ 179.901123, 61.606396 ] ] ] } }
] }
] }
,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -537,6 +537,9 @@ void *partial_feature_worker(void *v) {
// Give Clipper a chance to try to fix it.
for (unsigned i = 0; i < geoms.size(); i++) {
geoms[i] = clean_or_clip_poly(geoms[i], 0, 0, 0, false);
if (additional[A_DEBUG_POLYGON]) {
check_polygon(geoms[i]);
}
geoms[i] = close_poly(geoms[i]);
}
}

2
tile.h
View File

@ -47,6 +47,8 @@ static int additional_options[] = {
A_REORDER,
#define A_LINE_DROP ((int) 'l')
A_LINE_DROP,
#define A_DEBUG_POLYGON ((int) 'd')
A_DEBUG_POLYGON,
};
static int prevent_options[] = {