Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / basegfx / source / polygon / b2dpolypolygoncutter.cxx
blobb93b271b64d0b976a1d6ef944c3e0118c771d80d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <basegfx/numeric/ftools.hxx>
22 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
23 #include <basegfx/point/b2dpoint.hxx>
24 #include <basegfx/vector/b2dvector.hxx>
25 #include <basegfx/polygon/b2dpolygon.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
28 #include <basegfx/range/b2drange.hxx>
29 #include <basegfx/polygon/b2dpolypolygontools.hxx>
30 #include <basegfx/curve/b2dcubicbezier.hxx>
31 #include <vector>
32 #include <algorithm>
34 //////////////////////////////////////////////////////////////////////////////
36 namespace basegfx
38 namespace
40 //////////////////////////////////////////////////////////////////////////////
42 struct StripHelper
44 B2DRange maRange;
45 sal_Int32 mnDepth;
46 B2VectorOrientation meOrinetation;
49 //////////////////////////////////////////////////////////////////////////////
51 struct PN
53 public:
54 B2DPoint maPoint;
55 sal_uInt32 mnI;
56 sal_uInt32 mnIP;
57 sal_uInt32 mnIN;
60 //////////////////////////////////////////////////////////////////////////////
62 struct VN
64 public:
65 B2DVector maPrev;
66 B2DVector maNext;
68 // to have the correct curve segments in the crossover checks,
69 // it is necessary to keep the original next vectors, too. Else,
70 // it may happen to use a already switched next vector which
71 // would interpolate the wrong comparison point
72 B2DVector maOriginalNext;
75 //////////////////////////////////////////////////////////////////////////////
77 struct SN
79 public:
80 PN* mpPN;
82 bool operator<(const SN& rComp) const
84 if(fTools::equal(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX()))
86 if(fTools::equal(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY()))
88 return (mpPN->mnI < rComp.mpPN->mnI);
90 else
92 return fTools::less(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY());
95 else
97 return fTools::less(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX());
102 //////////////////////////////////////////////////////////////////////////////
104 typedef ::std::vector< PN > PNV;
105 typedef ::std::vector< VN > VNV;
106 typedef ::std::vector< SN > SNV;
107 typedef ::std::pair< basegfx::B2DPoint /*orig*/, basegfx::B2DPoint /*repl*/ > CorrectionPair;
108 typedef ::std::vector< CorrectionPair > CorrectionTable;
110 //////////////////////////////////////////////////////////////////////////////
112 class solver
114 private:
115 const B2DPolyPolygon maOriginal;
116 PNV maPNV;
117 VNV maVNV;
118 SNV maSNV;
119 CorrectionTable maCorrectionTable;
121 unsigned mbIsCurve : 1;
122 unsigned mbChanged : 1;
124 void impAddPolygon(const sal_uInt32 aPos, const B2DPolygon& rGeometry)
126 const sal_uInt32 nCount(rGeometry.count());
127 PN aNewPN;
128 VN aNewVN;
129 SN aNewSN;
131 for(sal_uInt32 a(0); a < nCount; a++)
133 const B2DPoint aPoint(rGeometry.getB2DPoint(a));
134 aNewPN.maPoint = aPoint;
135 aNewPN.mnI = aPos + a;
136 aNewPN.mnIP = aPos + ((a != 0) ? a - 1 : nCount - 1);
137 aNewPN.mnIN = aPos + ((a + 1 == nCount) ? 0 : a + 1);
138 maPNV.push_back(aNewPN);
140 if(mbIsCurve)
142 aNewVN.maPrev = rGeometry.getPrevControlPoint(a) - aPoint;
143 aNewVN.maNext = rGeometry.getNextControlPoint(a) - aPoint;
144 aNewVN.maOriginalNext = aNewVN.maNext;
145 maVNV.push_back(aNewVN);
148 aNewSN.mpPN = &maPNV[maPNV.size() - 1];
149 maSNV.push_back(aNewSN);
153 bool impLeftOfEdges(const B2DVector& rVecA, const B2DVector& rVecB, const B2DVector& rTest)
155 // tests if rTest is left of both directed line segments along the line -rVecA, rVecB. Test is
156 // with border.
157 if(rVecA.cross(rVecB) > 0.0)
159 // b is left turn seen from a, test if Test is left of both and so inside (left is seeen as inside)
160 const bool bBoolA(fTools::moreOrEqual(rVecA.cross(rTest), 0.0));
161 const bool bBoolB(fTools::lessOrEqual(rVecB.cross(rTest), 0.0));
163 return (bBoolA && bBoolB);
165 else
167 // b is right turn seen from a, test if Test is right of both and so outside (left is seeen as inside)
168 const bool bBoolA(fTools::lessOrEqual(rVecA.cross(rTest), 0.0));
169 const bool bBoolB(fTools::moreOrEqual(rVecB.cross(rTest), 0.0));
171 return (!(bBoolA && bBoolB));
175 void impSwitchNext(PN& rPNa, PN& rPNb)
177 ::std::swap(rPNa.mnIN, rPNb.mnIN);
179 if(mbIsCurve)
181 VN& rVNa = maVNV[rPNa.mnI];
182 VN& rVNb = maVNV[rPNb.mnI];
184 ::std::swap(rVNa.maNext, rVNb.maNext);
187 if(!mbChanged)
189 mbChanged = true;
193 B2DCubicBezier createSegment(const PN& rPN, bool bPrev) const
195 const B2DPoint& rStart(rPN.maPoint);
196 const B2DPoint& rEnd(maPNV[bPrev ? rPN.mnIP : rPN.mnIN].maPoint);
197 const B2DVector& rCPA(bPrev ? maVNV[rPN.mnI].maPrev : maVNV[rPN.mnI].maNext);
198 // Use maOriginalNext, not maNext to create the original (yet unchanged)
199 // curve segment. Otherwise, this segment would NOT ne correct.
200 const B2DVector& rCPB(bPrev ? maVNV[maPNV[rPN.mnIP].mnI].maOriginalNext : maVNV[maPNV[rPN.mnIN].mnI].maPrev);
202 return B2DCubicBezier(rStart, rStart + rCPA, rEnd + rCPB, rEnd);
205 void impHandleCommon(PN& rPNa, PN& rPNb)
207 if(mbIsCurve)
209 const B2DCubicBezier aNextA(createSegment(rPNa, false));
210 const B2DCubicBezier aPrevA(createSegment(rPNa, true));
212 if(aNextA.equal(aPrevA))
214 // deadend on A (identical edge)
215 return;
218 const B2DCubicBezier aNextB(createSegment(rPNb, false));
219 const B2DCubicBezier aPrevB(createSegment(rPNb, true));
221 if(aNextB.equal(aPrevB))
223 // deadend on B (identical edge)
224 return;
227 if(aPrevA.equal(aPrevB))
229 // common edge in same direction
230 return;
232 else if(aPrevA.equal(aNextB))
234 // common edge in opposite direction
235 if(aNextA.equal(aPrevB))
237 // common edge in opposite direction continues
238 return;
240 else
242 // common edge in opposite direction leave
243 impSwitchNext(rPNa, rPNb);
246 else if(aNextA.equal(aNextB))
248 // common edge in same direction enter
249 // search leave edge
250 PN* pPNa2 = &maPNV[rPNa.mnIN];
251 PN* pPNb2 = &maPNV[rPNb.mnIN];
252 bool bOnEdge(true);
256 const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
257 const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
259 if(aNextA2.equal(aNextB2))
261 pPNa2 = &maPNV[pPNa2->mnIN];
262 pPNb2 = &maPNV[pPNb2->mnIN];
264 else
266 bOnEdge = false;
269 while(bOnEdge && pPNa2 != &rPNa && pPNb2 != &rPNb);
271 if(bOnEdge)
273 // loop over two identical polygon paths
274 return;
276 else
278 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
279 // at enter/leave. Check for crossover.
280 const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
281 const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
282 const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
283 const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
285 const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
286 const B2DCubicBezier aPrevA2(createSegment(*pPNa2, true));
287 const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
288 const B2DVector aPrevCA2(aPrevA2.interpolatePoint(0.5) - aPrevA2.getStartPoint());
289 const B2DVector aNextCA2(aNextA2.interpolatePoint(0.5) - aNextA2.getStartPoint());
290 const B2DVector aNextCB2(aNextB2.interpolatePoint(0.5) - aNextB2.getStartPoint());
291 const bool bLeave(impLeftOfEdges(aPrevCA2, aNextCA2, aNextCB2));
293 if(bEnter != bLeave)
295 // crossover
296 impSwitchNext(rPNa, rPNb);
300 else if(aNextA.equal(aPrevB))
302 // common edge in opposite direction enter
303 impSwitchNext(rPNa, rPNb);
305 else
307 // no common edges, check for crossover
308 const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
309 const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
310 const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
311 const B2DVector aNextCB(aNextB.interpolatePoint(0.5) - aNextB.getStartPoint());
313 const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
314 const bool bLeave(impLeftOfEdges(aPrevCA, aNextCA, aNextCB));
316 if(bEnter != bLeave)
318 // crossover
319 impSwitchNext(rPNa, rPNb);
323 else
325 const B2DPoint& rNextA(maPNV[rPNa.mnIN].maPoint);
326 const B2DPoint& rPrevA(maPNV[rPNa.mnIP].maPoint);
328 if(rNextA.equal(rPrevA))
330 // deadend on A
331 return;
334 const B2DPoint& rNextB(maPNV[rPNb.mnIN].maPoint);
335 const B2DPoint& rPrevB(maPNV[rPNb.mnIP].maPoint);
337 if(rNextB.equal(rPrevB))
339 // deadend on B
340 return;
343 if(rPrevA.equal(rPrevB))
345 // common edge in same direction
346 return;
348 else if(rPrevA.equal(rNextB))
350 // common edge in opposite direction
351 if(rNextA.equal(rPrevB))
353 // common edge in opposite direction continues
354 return;
356 else
358 // common edge in opposite direction leave
359 impSwitchNext(rPNa, rPNb);
362 else if(rNextA.equal(rNextB))
364 // common edge in same direction enter
365 // search leave edge
366 PN* pPNa2 = &maPNV[rPNa.mnIN];
367 PN* pPNb2 = &maPNV[rPNb.mnIN];
368 bool bOnEdge(true);
372 const B2DPoint& rNextA2(maPNV[pPNa2->mnIN].maPoint);
373 const B2DPoint& rNextB2(maPNV[pPNb2->mnIN].maPoint);
375 if(rNextA2.equal(rNextB2))
377 pPNa2 = &maPNV[pPNa2->mnIN];
378 pPNb2 = &maPNV[pPNb2->mnIN];
380 else
382 bOnEdge = false;
385 while(bOnEdge && pPNa2 != &rPNa && pPNb2 != &rPNb);
387 if(bOnEdge)
389 // loop over two identical polygon paths
390 return;
392 else
394 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
395 // at enter/leave. Check for crossover.
396 const B2DPoint& aPointE(rPNa.maPoint);
397 const B2DVector aPrevAE(rPrevA - aPointE);
398 const B2DVector aNextAE(rNextA - aPointE);
399 const B2DVector aPrevBE(rPrevB - aPointE);
401 const B2DPoint& aPointL(pPNa2->maPoint);
402 const B2DVector aPrevAL(maPNV[pPNa2->mnIP].maPoint - aPointL);
403 const B2DVector aNextAL(maPNV[pPNa2->mnIN].maPoint - aPointL);
404 const B2DVector aNextBL(maPNV[pPNb2->mnIN].maPoint - aPointL);
406 const bool bEnter(impLeftOfEdges(aPrevAE, aNextAE, aPrevBE));
407 const bool bLeave(impLeftOfEdges(aPrevAL, aNextAL, aNextBL));
409 if(bEnter != bLeave)
411 // crossover; switch start or end
412 impSwitchNext(rPNa, rPNb);
416 else if(rNextA.equal(rPrevB))
418 // common edge in opposite direction enter
419 impSwitchNext(rPNa, rPNb);
421 else
423 // no common edges, check for crossover
424 const B2DPoint& aPoint(rPNa.maPoint);
425 const B2DVector aPrevA(rPrevA - aPoint);
426 const B2DVector aNextA(rNextA - aPoint);
427 const B2DVector aPrevB(rPrevB - aPoint);
428 const B2DVector aNextB(rNextB - aPoint);
430 const bool bEnter(impLeftOfEdges(aPrevA, aNextA, aPrevB));
431 const bool bLeave(impLeftOfEdges(aPrevA, aNextA, aNextB));
433 if(bEnter != bLeave)
435 // crossover
436 impSwitchNext(rPNa, rPNb);
442 void impSolve()
444 // sort by point to identify common nodes easier
445 ::std::sort(maSNV.begin(), maSNV.end());
447 // handle common nodes
448 const sal_uInt32 nNodeCount(maSNV.size());
449 sal_uInt32 a(0);
451 // snap unsharp-equal points
452 if(nNodeCount)
454 basegfx::B2DPoint* pLast(&maSNV[0].mpPN->maPoint);
456 for(a = 1; a < nNodeCount; a++)
458 basegfx::B2DPoint* pCurrent(&maSNV[a].mpPN->maPoint);
460 if(pLast->equal(*pCurrent) && (pLast->getX() != pCurrent->getX() || pLast->getY() != pCurrent->getY()))
462 const basegfx::B2DPoint aMiddle((*pLast + *pCurrent) * 0.5);
464 if(pLast->getX() != aMiddle.getX() || pLast->getY() != aMiddle.getY())
466 maCorrectionTable.push_back(CorrectionPair(*pLast, aMiddle));
467 *pLast = aMiddle;
470 if(pCurrent->getX() != aMiddle.getX() || pCurrent->getY() != aMiddle.getY())
472 maCorrectionTable.push_back(CorrectionPair(*pCurrent, aMiddle));
473 *pCurrent = aMiddle;
477 pLast = pCurrent;
481 for(a = 0; a < nNodeCount - 1; a++)
483 // test a before using it, not after. Also use nPointCount instead of aSortNodes.size()
484 PN& rPNb = *(maSNV[a].mpPN);
486 for(sal_uInt32 b(a + 1); b < nNodeCount && rPNb.maPoint.equal(maSNV[b].mpPN->maPoint); b++)
488 impHandleCommon(rPNb, *maSNV[b].mpPN);
493 public:
494 explicit solver(const B2DPolygon& rOriginal)
495 : maOriginal(B2DPolyPolygon(rOriginal)),
496 mbIsCurve(false),
497 mbChanged(false)
499 const sal_uInt32 nOriginalCount(rOriginal.count());
501 if(nOriginalCount)
503 B2DPolygon aGeometry(tools::addPointsAtCutsAndTouches(rOriginal));
504 aGeometry.removeDoublePoints();
505 aGeometry = tools::simplifyCurveSegments(aGeometry);
506 mbIsCurve = aGeometry.areControlPointsUsed();
508 const sal_uInt32 nPointCount(aGeometry.count());
510 // If it's not a pezier polygon, at least four points are needed to create
511 // a self-intersection. If it's a bezier polygon, the minimum point number
512 // is two, since with a single point You get a curve, but no self-intersection
513 if(nPointCount > 3 || (nPointCount > 1 && mbIsCurve))
515 // reserve space in point, control and sort vector.
516 maSNV.reserve(nPointCount);
517 maPNV.reserve(nPointCount);
518 maVNV.reserve(mbIsCurve ? nPointCount : 0);
520 // fill data
521 impAddPolygon(0, aGeometry);
523 // solve common nodes
524 impSolve();
529 explicit solver(const B2DPolyPolygon& rOriginal)
530 : maOriginal(rOriginal),
531 mbIsCurve(false),
532 mbChanged(false)
534 sal_uInt32 nOriginalCount(maOriginal.count());
536 if(nOriginalCount)
538 B2DPolyPolygon aGeometry(tools::addPointsAtCutsAndTouches(maOriginal, true));
539 aGeometry.removeDoublePoints();
540 aGeometry = tools::simplifyCurveSegments(aGeometry);
541 mbIsCurve = aGeometry.areControlPointsUsed();
542 nOriginalCount = aGeometry.count();
544 if(nOriginalCount)
546 sal_uInt32 nPointCount(0);
547 sal_uInt32 a(0);
549 // count points
550 for(a = 0; a < nOriginalCount; a++)
552 const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
553 const sal_uInt32 nCandCount(aCandidate.count());
555 // If it's not a bezier curve, at least three points would be needed to have a
556 // topological relevant (not empty) polygon. Since its not known here if trivial
557 // edges (dead ends) will be kept or sorted out, add non-bezier polygons with
558 // more than one point.
559 // For bezier curves, the minimum for defining an area is also one.
560 if(nCandCount)
562 nPointCount += nCandCount;
566 if(nPointCount)
568 // reserve space in point, control and sort vector.
569 maSNV.reserve(nPointCount);
570 maPNV.reserve(nPointCount);
571 maVNV.reserve(mbIsCurve ? nPointCount : 0);
573 // fill data
574 sal_uInt32 nInsertIndex(0);
576 for(a = 0; a < nOriginalCount; a++)
578 const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
579 const sal_uInt32 nCandCount(aCandidate.count());
581 // use same condition as above, the data vector is
582 // pre-allocated
583 if(nCandCount)
585 impAddPolygon(nInsertIndex, aCandidate);
586 nInsertIndex += nCandCount;
590 // solve common nodes
591 impSolve();
597 B2DPolyPolygon getB2DPolyPolygon()
599 if(mbChanged)
601 B2DPolyPolygon aRetval;
602 const sal_uInt32 nCount(maPNV.size());
603 sal_uInt32 nCountdown(nCount);
605 for(sal_uInt32 a(0); nCountdown && a < nCount; a++)
607 PN& rPN = maPNV[a];
609 if(SAL_MAX_UINT32 != rPN.mnI)
611 // unused node, start new part polygon
612 B2DPolygon aNewPart;
613 PN* pPNCurr = &rPN;
617 const B2DPoint& rPoint = pPNCurr->maPoint;
618 aNewPart.append(rPoint);
620 if(mbIsCurve)
622 const VN& rVNCurr = maVNV[pPNCurr->mnI];
624 if(!rVNCurr.maPrev.equalZero())
626 aNewPart.setPrevControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maPrev);
629 if(!rVNCurr.maNext.equalZero())
631 aNewPart.setNextControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maNext);
635 pPNCurr->mnI = SAL_MAX_UINT32;
636 nCountdown--;
637 pPNCurr = &(maPNV[pPNCurr->mnIN]);
639 while(pPNCurr != &rPN && SAL_MAX_UINT32 != pPNCurr->mnI);
641 // close and add
642 aNewPart.setClosed(true);
643 aRetval.append(aNewPart);
647 return aRetval;
649 else
651 const sal_uInt32 nCorrectionSize(maCorrectionTable.size());
653 // no change, return original
654 if(!nCorrectionSize)
656 return maOriginal;
659 // apply coordinate corrections to ensure inside/outside correctness after solving
660 const sal_uInt32 nPolygonCount(maOriginal.count());
661 basegfx::B2DPolyPolygon aRetval(maOriginal);
663 for(sal_uInt32 a(0); a < nPolygonCount; a++)
665 basegfx::B2DPolygon aTemp(aRetval.getB2DPolygon(a));
666 const sal_uInt32 nPointCount(aTemp.count());
667 bool bChanged(false);
669 for(sal_uInt32 b(0); b < nPointCount; b++)
671 const basegfx::B2DPoint aCandidate(aTemp.getB2DPoint(b));
673 for(sal_uInt32 c(0); c < nCorrectionSize; c++)
675 if(maCorrectionTable[c].first.getX() == aCandidate.getX() && maCorrectionTable[c].first.getY() == aCandidate.getY())
677 aTemp.setB2DPoint(b, maCorrectionTable[c].second);
678 bChanged = true;
683 if(bChanged)
685 aRetval.setB2DPolygon(a, aTemp);
689 return aRetval;
694 //////////////////////////////////////////////////////////////////////////////
696 } // end of anonymous namespace
697 } // end of namespace basegfx
699 //////////////////////////////////////////////////////////////////////////////
701 namespace basegfx
703 namespace tools
705 //////////////////////////////////////////////////////////////////////////////
707 B2DPolyPolygon solveCrossovers(const B2DPolyPolygon& rCandidate)
709 if(rCandidate.count() > 1L)
711 solver aSolver(rCandidate);
712 return aSolver.getB2DPolyPolygon();
714 else
716 return rCandidate;
720 //////////////////////////////////////////////////////////////////////////////
722 B2DPolyPolygon solveCrossovers(const B2DPolygon& rCandidate)
724 solver aSolver(rCandidate);
725 return aSolver.getB2DPolyPolygon();
728 //////////////////////////////////////////////////////////////////////////////
730 B2DPolyPolygon stripNeutralPolygons(const B2DPolyPolygon& rCandidate)
732 B2DPolyPolygon aRetval;
734 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
736 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
738 if(ORIENTATION_NEUTRAL != tools::getOrientation(aCandidate))
740 aRetval.append(aCandidate);
744 return aRetval;
747 //////////////////////////////////////////////////////////////////////////////
749 B2DPolyPolygon createNonzeroConform(const B2DPolyPolygon& rCandidate)
751 B2DPolyPolygon aCandidate;
753 // remove all self-intersections and intersections
754 if(rCandidate.count() == 1)
756 aCandidate = basegfx::tools::solveCrossovers(rCandidate.getB2DPolygon(0));
758 else
760 aCandidate = basegfx::tools::solveCrossovers(rCandidate);
763 // cleanup evtl. neutral polygons
764 aCandidate = basegfx::tools::stripNeutralPolygons(aCandidate);
766 // remove all polygons which have the same orientation as the polygon they are directly contained in
767 const sal_uInt32 nCount(aCandidate.count());
769 if(nCount > 1)
771 sal_uInt32 a, b;
772 ::std::vector< StripHelper > aHelpers;
773 aHelpers.resize(nCount);
775 for(a = 0; a < nCount; a++)
777 const B2DPolygon aCand(aCandidate.getB2DPolygon(a));
778 StripHelper* pNewHelper = &(aHelpers[a]);
779 pNewHelper->maRange = tools::getRange(aCand);
780 pNewHelper->meOrinetation = tools::getOrientation(aCand);
782 // initialize with own orientation
783 pNewHelper->mnDepth = (ORIENTATION_NEGATIVE == pNewHelper->meOrinetation ? -1 : 1);
786 for(a = 0; a < nCount - 1; a++)
788 const B2DPolygon aCandA(aCandidate.getB2DPolygon(a));
789 StripHelper& rHelperA = aHelpers[a];
791 for(b = a + 1; b < nCount; b++)
793 const B2DPolygon aCandB(aCandidate.getB2DPolygon(b));
794 StripHelper& rHelperB = aHelpers[b];
795 const bool bAInB(rHelperB.maRange.isInside(rHelperA.maRange) && tools::isInside(aCandB, aCandA, true));
797 if(bAInB)
799 // A is inside B, add orientation of B to A
800 rHelperA.mnDepth += (ORIENTATION_NEGATIVE == rHelperB.meOrinetation ? -1 : 1);
803 const bool bBInA(rHelperA.maRange.isInside(rHelperB.maRange) && tools::isInside(aCandA, aCandB, true));
805 if(bBInA)
807 // B is inside A, add orientation of A to B
808 rHelperB.mnDepth += (ORIENTATION_NEGATIVE == rHelperA.meOrinetation ? -1 : 1);
813 const B2DPolyPolygon aSource(aCandidate);
814 aCandidate.clear();
816 for(a = 0L; a < nCount; a++)
818 const StripHelper& rHelper = aHelpers[a];
819 // for contained unequal oriented polygons sum will be 0
820 // for contained equal it will be >=2 or <=-2
821 // for free polygons (not contained) it will be 1 or -1
822 // -> accept all which are >=-1 && <= 1
823 bool bAcceptEntry(rHelper.mnDepth >= -1 && rHelper.mnDepth <= 1);
825 if(bAcceptEntry)
827 aCandidate.append(aSource.getB2DPolygon(a));
832 return aCandidate;
835 //////////////////////////////////////////////////////////////////////////////
837 B2DPolyPolygon stripDispensablePolygons(const B2DPolyPolygon& rCandidate, bool bKeepAboveZero)
839 const sal_uInt32 nCount(rCandidate.count());
840 B2DPolyPolygon aRetval;
842 if(nCount)
844 if(nCount == 1L)
846 if(!bKeepAboveZero && ORIENTATION_POSITIVE == tools::getOrientation(rCandidate.getB2DPolygon(0L)))
848 aRetval = rCandidate;
851 else
853 sal_uInt32 a, b;
854 ::std::vector< StripHelper > aHelpers;
855 aHelpers.resize(nCount);
857 for(a = 0L; a < nCount; a++)
859 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
860 StripHelper* pNewHelper = &(aHelpers[a]);
861 pNewHelper->maRange = tools::getRange(aCandidate);
862 pNewHelper->meOrinetation = tools::getOrientation(aCandidate);
863 pNewHelper->mnDepth = (ORIENTATION_NEGATIVE == pNewHelper->meOrinetation ? -1L : 0L);
866 for(a = 0L; a < nCount - 1L; a++)
868 const B2DPolygon aCandA(rCandidate.getB2DPolygon(a));
869 StripHelper& rHelperA = aHelpers[a];
871 for(b = a + 1L; b < nCount; b++)
873 const B2DPolygon aCandB(rCandidate.getB2DPolygon(b));
874 StripHelper& rHelperB = aHelpers[b];
875 const bool bAInB(rHelperB.maRange.isInside(rHelperA.maRange) && tools::isInside(aCandB, aCandA, true));
876 const bool bBInA(rHelperA.maRange.isInside(rHelperB.maRange) && tools::isInside(aCandA, aCandB, true));
878 if(bAInB && bBInA)
880 // congruent
881 if(rHelperA.meOrinetation == rHelperB.meOrinetation)
883 // two polys or two holes. Lower one of them to get one of them out of the way.
884 // Since each will be contained in the other one, both will be increased, too.
885 // So, for lowering, increase only one of them
886 rHelperA.mnDepth++;
888 else
890 // poly and hole. They neutralize, so get rid of both. Move securely below zero.
891 rHelperA.mnDepth = -((sal_Int32)nCount);
892 rHelperB.mnDepth = -((sal_Int32)nCount);
895 else
897 if(bAInB)
899 if(ORIENTATION_NEGATIVE == rHelperB.meOrinetation)
901 rHelperA.mnDepth--;
903 else
905 rHelperA.mnDepth++;
908 else if(bBInA)
910 if(ORIENTATION_NEGATIVE == rHelperA.meOrinetation)
912 rHelperB.mnDepth--;
914 else
916 rHelperB.mnDepth++;
923 for(a = 0L; a < nCount; a++)
925 const StripHelper& rHelper = aHelpers[a];
926 bool bAcceptEntry(bKeepAboveZero ? 1L <= rHelper.mnDepth : 0L == rHelper.mnDepth);
928 if(bAcceptEntry)
930 aRetval.append(rCandidate.getB2DPolygon(a));
936 return aRetval;
939 //////////////////////////////////////////////////////////////////////////////
941 B2DPolyPolygon prepareForPolygonOperation(const B2DPolygon& rCandidate)
943 solver aSolver(rCandidate);
944 B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
946 return correctOrientations(aRetval);
949 B2DPolyPolygon prepareForPolygonOperation(const B2DPolyPolygon& rCandidate)
951 solver aSolver(rCandidate);
952 B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
954 return correctOrientations(aRetval);
957 B2DPolyPolygon solvePolygonOperationOr(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
959 if(!rCandidateA.count())
961 return rCandidateB;
963 else if(!rCandidateB.count())
965 return rCandidateA;
967 else
969 // concatenate polygons, solve crossovers and throw away all sub-polygons
970 // which have a depth other than 0.
971 B2DPolyPolygon aRetval(rCandidateA);
973 aRetval.append(rCandidateB);
974 aRetval = solveCrossovers(aRetval);
975 aRetval = stripNeutralPolygons(aRetval);
977 return stripDispensablePolygons(aRetval, false);
981 B2DPolyPolygon solvePolygonOperationXor(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
983 if(!rCandidateA.count())
985 return rCandidateB;
987 else if(!rCandidateB.count())
989 return rCandidateA;
991 else
993 // XOR is pretty simple: By definition it is the simple concatenation of
994 // the single polygons since we imply XOR fill rule. Make it intersection-free
995 // and correct orientations
996 B2DPolyPolygon aRetval(rCandidateA);
998 aRetval.append(rCandidateB);
999 aRetval = solveCrossovers(aRetval);
1000 aRetval = stripNeutralPolygons(aRetval);
1002 return correctOrientations(aRetval);
1006 B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
1008 if(!rCandidateA.count())
1010 return B2DPolyPolygon();
1012 else if(!rCandidateB.count())
1014 return B2DPolyPolygon();
1016 else
1018 // concatenate polygons, solve crossovers and throw away all sub-polygons
1019 // with a depth of < 1. This means to keep all polygons where at least two
1020 // polygons do overlap.
1021 B2DPolyPolygon aRetval(rCandidateA);
1023 aRetval.append(rCandidateB);
1024 aRetval = solveCrossovers(aRetval);
1025 aRetval = stripNeutralPolygons(aRetval);
1027 return stripDispensablePolygons(aRetval, true);
1031 B2DPolyPolygon solvePolygonOperationDiff(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
1033 if(!rCandidateA.count())
1035 return B2DPolyPolygon();
1037 else if(!rCandidateB.count())
1039 return rCandidateA;
1041 else
1043 // Make B topologically to holes and append to A
1044 B2DPolyPolygon aRetval(rCandidateB);
1046 aRetval.flip();
1047 aRetval.append(rCandidateA);
1049 // solve crossovers and throw away all sub-polygons which have a
1050 // depth other than 0.
1051 aRetval = basegfx::tools::solveCrossovers(aRetval);
1052 aRetval = basegfx::tools::stripNeutralPolygons(aRetval);
1054 return basegfx::tools::stripDispensablePolygons(aRetval, false);
1058 B2DPolyPolygon mergeToSinglePolyPolygon(const B2DPolyPolygonVector& rInput)
1060 B2DPolyPolygonVector aInput(rInput);
1062 // first step: prepareForPolygonOperation and simple merge of non-overlapping
1063 // PolyPolygons for speedup; this is possible for the wanted OR-operation
1064 if(!aInput.empty())
1066 B2DPolyPolygonVector aResult;
1067 aResult.reserve(aInput.size());
1069 for(sal_uInt32 a(0); a < aInput.size(); a++)
1071 const basegfx::B2DPolyPolygon aCandidate(prepareForPolygonOperation(aInput[a]));
1073 if(!aResult.empty())
1075 const B2DRange aCandidateRange(aCandidate.getB2DRange());
1076 bool bCouldMergeSimple(false);
1078 for(sal_uInt32 b(0); !bCouldMergeSimple && b < aResult.size(); b++)
1080 basegfx::B2DPolyPolygon aTarget(aResult[b]);
1081 const B2DRange aTargetRange(aTarget.getB2DRange());
1083 if(!aCandidateRange.overlaps(aTargetRange))
1085 aTarget.append(aCandidate);
1086 aResult[b] = aTarget;
1087 bCouldMergeSimple = true;
1091 if(!bCouldMergeSimple)
1093 aResult.push_back(aCandidate);
1096 else
1098 aResult.push_back(aCandidate);
1102 aInput = aResult;
1105 // second step: melt pairwise to a single PolyPolygon
1106 while(aInput.size() > 1)
1108 B2DPolyPolygonVector aResult;
1109 aResult.reserve((aInput.size() / 2) + 1);
1111 for(sal_uInt32 a(0); a < aInput.size(); a += 2)
1113 if(a + 1 < aInput.size())
1115 // a pair for processing
1116 aResult.push_back(solvePolygonOperationOr(aInput[a], aInput[a + 1]));
1118 else
1120 // last single PolyPolygon; copy to target to not lose it
1121 aResult.push_back(aInput[a]);
1125 aInput = aResult;
1128 // third step: get result
1129 if(1 == aInput.size())
1131 return aInput[0];
1134 return B2DPolyPolygon();
1137 //////////////////////////////////////////////////////////////////////////////
1139 } // end of namespace tools
1140 } // end of namespace basegfx
1142 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */