Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / basegfx / source / polygon / b2dpolypolygoncutter.cxx
blob4fd7bf369d857d43fd5e8d8e3418f118853afb76
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 <basegfx/numeric/ftools.hxx>
21 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
22 #include <basegfx/point/b2dpoint.hxx>
23 #include <basegfx/vector/b2dvector.hxx>
24 #include <basegfx/polygon/b2dpolygon.hxx>
25 #include <basegfx/polygon/b2dpolygontools.hxx>
26 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
27 #include <basegfx/range/b2drange.hxx>
28 #include <basegfx/polygon/b2dpolypolygontools.hxx>
29 #include <basegfx/curve/b2dcubicbezier.hxx>
30 #include <vector>
31 #include <algorithm>
33 namespace basegfx
35 namespace
38 struct StripHelper
40 B2DRange maRange;
41 sal_Int32 mnDepth;
42 B2VectorOrientation meOrinetation;
45 struct PN
47 public:
48 B2DPoint maPoint;
49 sal_uInt32 mnI;
50 sal_uInt32 mnIP;
51 sal_uInt32 mnIN;
54 struct VN
56 public:
57 B2DVector maPrev;
58 B2DVector maNext;
60 // to have the correct curve segments in the crossover checks,
61 // it is necessary to keep the original next vectors, too. Else,
62 // it may happen to use a already switched next vector which
63 // would interpolate the wrong comparison point
64 B2DVector maOriginalNext;
67 struct SN
69 public:
70 PN* mpPN;
72 bool operator<(const SN& rComp) const
74 if(fTools::equal(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX()))
76 if(fTools::equal(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY()))
78 return (mpPN->mnI < rComp.mpPN->mnI);
80 else
82 return fTools::less(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY());
85 else
87 return fTools::less(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX());
92 typedef std::vector< PN > PNV;
93 typedef std::vector< VN > VNV;
94 typedef std::vector< SN > SNV;
95 typedef std::pair< basegfx::B2DPoint /*orig*/, basegfx::B2DPoint /*repl*/ > CorrectionPair;
97 class solver
99 private:
100 const B2DPolyPolygon maOriginal;
101 PNV maPNV;
102 VNV maVNV;
103 SNV maSNV;
104 std::vector< CorrectionPair >
105 maCorrectionTable;
107 bool mbIsCurve : 1;
108 bool mbChanged : 1;
110 void impAddPolygon(const sal_uInt32 aPos, const B2DPolygon& rGeometry)
112 const sal_uInt32 nCount(rGeometry.count());
113 PN aNewPN;
114 VN aNewVN;
115 SN aNewSN;
117 for(sal_uInt32 a(0); a < nCount; a++)
119 const B2DPoint aPoint(rGeometry.getB2DPoint(a));
120 aNewPN.maPoint = aPoint;
121 aNewPN.mnI = aPos + a;
122 aNewPN.mnIP = aPos + ((a != 0) ? a - 1 : nCount - 1);
123 aNewPN.mnIN = aPos + ((a + 1 == nCount) ? 0 : a + 1);
124 maPNV.push_back(aNewPN);
126 if(mbIsCurve)
128 aNewVN.maPrev = rGeometry.getPrevControlPoint(a) - aPoint;
129 aNewVN.maNext = rGeometry.getNextControlPoint(a) - aPoint;
130 aNewVN.maOriginalNext = aNewVN.maNext;
131 maVNV.push_back(aNewVN);
134 aNewSN.mpPN = &maPNV[maPNV.size() - 1];
135 maSNV.push_back(aNewSN);
139 static bool impLeftOfEdges(const B2DVector& rVecA, const B2DVector& rVecB, const B2DVector& rTest)
141 // tests if rTest is left of both directed line segments along the line -rVecA, rVecB. Test is
142 // with border.
143 if(rVecA.cross(rVecB) > 0.0)
145 // b is left turn seen from a, test if Test is left of both and so inside (left is seeen as inside)
146 const bool bBoolA(fTools::moreOrEqual(rVecA.cross(rTest), 0.0));
147 const bool bBoolB(fTools::lessOrEqual(rVecB.cross(rTest), 0.0));
149 return (bBoolA && bBoolB);
151 else
153 // b is right turn seen from a, test if Test is right of both and so outside (left is seeen as inside)
154 const bool bBoolA(fTools::lessOrEqual(rVecA.cross(rTest), 0.0));
155 const bool bBoolB(fTools::moreOrEqual(rVecB.cross(rTest), 0.0));
157 return (!(bBoolA && bBoolB));
161 void impSwitchNext(PN& rPNa, PN& rPNb)
163 std::swap(rPNa.mnIN, rPNb.mnIN);
165 if(mbIsCurve)
167 VN& rVNa = maVNV[rPNa.mnI];
168 VN& rVNb = maVNV[rPNb.mnI];
170 std::swap(rVNa.maNext, rVNb.maNext);
173 if(!mbChanged)
175 mbChanged = true;
179 B2DCubicBezier createSegment(const PN& rPN, bool bPrev) const
181 const B2DPoint& rStart(rPN.maPoint);
182 const B2DPoint& rEnd(maPNV[bPrev ? rPN.mnIP : rPN.mnIN].maPoint);
183 const B2DVector& rCPA(bPrev ? maVNV[rPN.mnI].maPrev : maVNV[rPN.mnI].maNext);
184 // Use maOriginalNext, not maNext to create the original (yet unchanged)
185 // curve segment. Otherwise, this segment would NOT ne correct.
186 const B2DVector& rCPB(bPrev ? maVNV[maPNV[rPN.mnIP].mnI].maOriginalNext : maVNV[maPNV[rPN.mnIN].mnI].maPrev);
188 return B2DCubicBezier(rStart, rStart + rCPA, rEnd + rCPB, rEnd);
191 void impHandleCommon(PN& rPNa, PN& rPNb)
193 if(mbIsCurve)
195 const B2DCubicBezier aNextA(createSegment(rPNa, false));
196 const B2DCubicBezier aPrevA(createSegment(rPNa, true));
198 if(aNextA.equal(aPrevA))
200 // deadend on A (identical edge)
201 return;
204 const B2DCubicBezier aNextB(createSegment(rPNb, false));
205 const B2DCubicBezier aPrevB(createSegment(rPNb, true));
207 if(aNextB.equal(aPrevB))
209 // deadend on B (identical edge)
210 return;
213 if(aPrevA.equal(aPrevB))
215 // common edge in same direction
216 return;
218 else if(aPrevA.equal(aNextB))
220 // common edge in opposite direction
221 if(aNextA.equal(aPrevB))
223 // common edge in opposite direction continues
224 return;
226 else
228 // common edge in opposite direction leave
229 impSwitchNext(rPNa, rPNb);
232 else if(aNextA.equal(aNextB))
234 // common edge in same direction enter
235 // search leave edge
236 PN* pPNa2 = &maPNV[rPNa.mnIN];
237 PN* pPNb2 = &maPNV[rPNb.mnIN];
238 bool bOnEdge(true);
242 const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
243 const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
245 if(aNextA2.equal(aNextB2))
247 pPNa2 = &maPNV[pPNa2->mnIN];
248 pPNb2 = &maPNV[pPNb2->mnIN];
250 else
252 bOnEdge = false;
255 while(bOnEdge && pPNa2 != &rPNa && pPNb2 != &rPNb);
257 if(bOnEdge)
259 // loop over two identical polygon paths
260 return;
262 else
264 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
265 // at enter/leave. Check for crossover.
266 const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
267 const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
268 const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
269 const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
271 const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
272 const B2DCubicBezier aPrevA2(createSegment(*pPNa2, true));
273 const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
274 const B2DVector aPrevCA2(aPrevA2.interpolatePoint(0.5) - aPrevA2.getStartPoint());
275 const B2DVector aNextCA2(aNextA2.interpolatePoint(0.5) - aNextA2.getStartPoint());
276 const B2DVector aNextCB2(aNextB2.interpolatePoint(0.5) - aNextB2.getStartPoint());
277 const bool bLeave(impLeftOfEdges(aPrevCA2, aNextCA2, aNextCB2));
279 if(bEnter != bLeave)
281 // crossover
282 impSwitchNext(rPNa, rPNb);
286 else if(aNextA.equal(aPrevB))
288 // common edge in opposite direction enter
289 impSwitchNext(rPNa, rPNb);
291 else
293 // no common edges, check for crossover
294 const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
295 const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
296 const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
297 const B2DVector aNextCB(aNextB.interpolatePoint(0.5) - aNextB.getStartPoint());
299 const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
300 const bool bLeave(impLeftOfEdges(aPrevCA, aNextCA, aNextCB));
302 if(bEnter != bLeave)
304 // crossover
305 impSwitchNext(rPNa, rPNb);
309 else
311 const B2DPoint& rNextA(maPNV[rPNa.mnIN].maPoint);
312 const B2DPoint& rPrevA(maPNV[rPNa.mnIP].maPoint);
314 if(rNextA.equal(rPrevA))
316 // deadend on A
317 return;
320 const B2DPoint& rNextB(maPNV[rPNb.mnIN].maPoint);
321 const B2DPoint& rPrevB(maPNV[rPNb.mnIP].maPoint);
323 if(rNextB.equal(rPrevB))
325 // deadend on B
326 return;
329 if(rPrevA.equal(rPrevB))
331 // common edge in same direction
332 return;
334 else if(rPrevA.equal(rNextB))
336 // common edge in opposite direction
337 if(rNextA.equal(rPrevB))
339 // common edge in opposite direction continues
340 return;
342 else
344 // common edge in opposite direction leave
345 impSwitchNext(rPNa, rPNb);
348 else if(rNextA.equal(rNextB))
350 // common edge in same direction enter
351 // search leave edge
352 PN* pPNa2 = &maPNV[rPNa.mnIN];
353 PN* pPNb2 = &maPNV[rPNb.mnIN];
354 bool bOnEdge(true);
358 const B2DPoint& rNextA2(maPNV[pPNa2->mnIN].maPoint);
359 const B2DPoint& rNextB2(maPNV[pPNb2->mnIN].maPoint);
361 if(rNextA2.equal(rNextB2))
363 pPNa2 = &maPNV[pPNa2->mnIN];
364 pPNb2 = &maPNV[pPNb2->mnIN];
366 else
368 bOnEdge = false;
371 while(bOnEdge && pPNa2 != &rPNa && pPNb2 != &rPNb);
373 if(bOnEdge)
375 // loop over two identical polygon paths
376 return;
378 else
380 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
381 // at enter/leave. Check for crossover.
382 const B2DPoint& aPointE(rPNa.maPoint);
383 const B2DVector aPrevAE(rPrevA - aPointE);
384 const B2DVector aNextAE(rNextA - aPointE);
385 const B2DVector aPrevBE(rPrevB - aPointE);
387 const B2DPoint& aPointL(pPNa2->maPoint);
388 const B2DVector aPrevAL(maPNV[pPNa2->mnIP].maPoint - aPointL);
389 const B2DVector aNextAL(maPNV[pPNa2->mnIN].maPoint - aPointL);
390 const B2DVector aNextBL(maPNV[pPNb2->mnIN].maPoint - aPointL);
392 const bool bEnter(impLeftOfEdges(aPrevAE, aNextAE, aPrevBE));
393 const bool bLeave(impLeftOfEdges(aPrevAL, aNextAL, aNextBL));
395 if(bEnter != bLeave)
397 // crossover; switch start or end
398 impSwitchNext(rPNa, rPNb);
402 else if(rNextA.equal(rPrevB))
404 // common edge in opposite direction enter
405 impSwitchNext(rPNa, rPNb);
407 else
409 // no common edges, check for crossover
410 const B2DPoint& aPoint(rPNa.maPoint);
411 const B2DVector aPrevA(rPrevA - aPoint);
412 const B2DVector aNextA(rNextA - aPoint);
413 const B2DVector aPrevB(rPrevB - aPoint);
414 const B2DVector aNextB(rNextB - aPoint);
416 const bool bEnter(impLeftOfEdges(aPrevA, aNextA, aPrevB));
417 const bool bLeave(impLeftOfEdges(aPrevA, aNextA, aNextB));
419 if(bEnter != bLeave)
421 // crossover
422 impSwitchNext(rPNa, rPNb);
428 void impSolve()
430 // sort by point to identify common nodes easier
431 std::sort(maSNV.begin(), maSNV.end());
433 // handle common nodes
434 const sal_uInt32 nNodeCount(maSNV.size());
435 sal_uInt32 a(0);
437 // snap unsharp-equal points
438 if(nNodeCount)
440 basegfx::B2DPoint* pLast(&maSNV[0].mpPN->maPoint);
442 for(a = 1; a < nNodeCount; a++)
444 basegfx::B2DPoint* pCurrent(&maSNV[a].mpPN->maPoint);
446 if(pLast->equal(*pCurrent) && (pLast->getX() != pCurrent->getX() || pLast->getY() != pCurrent->getY()))
448 const basegfx::B2DPoint aMiddle((*pLast + *pCurrent) * 0.5);
450 if(pLast->getX() != aMiddle.getX() || pLast->getY() != aMiddle.getY())
452 maCorrectionTable.emplace_back(*pLast, aMiddle);
453 *pLast = aMiddle;
456 if(pCurrent->getX() != aMiddle.getX() || pCurrent->getY() != aMiddle.getY())
458 maCorrectionTable.emplace_back(*pCurrent, aMiddle);
459 *pCurrent = aMiddle;
463 pLast = pCurrent;
467 for(a = 0; a < nNodeCount - 1; a++)
469 // test a before using it, not after. Also use nPointCount instead of aSortNodes.size()
470 PN& rPNb = *(maSNV[a].mpPN);
472 for(sal_uInt32 b(a + 1); b < nNodeCount && rPNb.maPoint.equal(maSNV[b].mpPN->maPoint); b++)
474 impHandleCommon(rPNb, *maSNV[b].mpPN);
479 public:
480 explicit solver(const B2DPolygon& rOriginal)
481 : maOriginal(B2DPolyPolygon(rOriginal)),
482 mbIsCurve(false),
483 mbChanged(false)
485 const sal_uInt32 nOriginalCount(rOriginal.count());
487 if(nOriginalCount)
489 B2DPolygon aGeometry(utils::addPointsAtCutsAndTouches(rOriginal));
490 aGeometry.removeDoublePoints();
491 aGeometry = utils::simplifyCurveSegments(aGeometry);
492 mbIsCurve = aGeometry.areControlPointsUsed();
494 const sal_uInt32 nPointCount(aGeometry.count());
496 // If it's not a bezier polygon, at least four points are needed to create
497 // a self-intersection. If it's a bezier polygon, the minimum point number
498 // is two, since with a single point You get a curve, but no self-intersection
499 if(nPointCount > 3 || (nPointCount > 1 && mbIsCurve))
501 // reserve space in point, control and sort vector.
502 maSNV.reserve(nPointCount);
503 maPNV.reserve(nPointCount);
504 maVNV.reserve(mbIsCurve ? nPointCount : 0);
506 // fill data
507 impAddPolygon(0, aGeometry);
509 // solve common nodes
510 impSolve();
515 explicit solver(const B2DPolyPolygon& rOriginal)
516 : maOriginal(rOriginal),
517 mbIsCurve(false),
518 mbChanged(false)
520 sal_uInt32 nOriginalCount(maOriginal.count());
522 if(nOriginalCount)
524 B2DPolyPolygon aGeometry(utils::addPointsAtCutsAndTouches(maOriginal));
525 aGeometry.removeDoublePoints();
526 aGeometry = utils::simplifyCurveSegments(aGeometry);
527 mbIsCurve = aGeometry.areControlPointsUsed();
528 nOriginalCount = aGeometry.count();
530 if(nOriginalCount)
532 sal_uInt32 nPointCount(0);
533 sal_uInt32 a(0);
535 // count points
536 for(a = 0; a < nOriginalCount; a++)
538 const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
539 const sal_uInt32 nCandCount(aCandidate.count());
541 // If it's not a bezier curve, at least three points would be needed to have a
542 // topological relevant (not empty) polygon. Since it's not known here if trivial
543 // edges (dead ends) will be kept or sorted out, add non-bezier polygons with
544 // more than one point.
545 // For bezier curves, the minimum for defining an area is also one.
546 if(nCandCount)
548 nPointCount += nCandCount;
552 if(nPointCount)
554 // reserve space in point, control and sort vector.
555 maSNV.reserve(nPointCount);
556 maPNV.reserve(nPointCount);
557 maVNV.reserve(mbIsCurve ? nPointCount : 0);
559 // fill data
560 sal_uInt32 nInsertIndex(0);
562 for(a = 0; a < nOriginalCount; a++)
564 const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
565 const sal_uInt32 nCandCount(aCandidate.count());
567 // use same condition as above, the data vector is
568 // pre-allocated
569 if(nCandCount)
571 impAddPolygon(nInsertIndex, aCandidate);
572 nInsertIndex += nCandCount;
576 // solve common nodes
577 impSolve();
583 B2DPolyPolygon getB2DPolyPolygon()
585 if(mbChanged)
587 B2DPolyPolygon aRetval;
588 const sal_uInt32 nCount(maPNV.size());
589 sal_uInt32 nCountdown(nCount);
591 for(sal_uInt32 a(0); nCountdown && a < nCount; a++)
593 PN& rPN = maPNV[a];
595 if(rPN.mnI != SAL_MAX_UINT32)
597 // unused node, start new part polygon
598 B2DPolygon aNewPart;
599 PN* pPNCurr = &rPN;
603 const B2DPoint& rPoint = pPNCurr->maPoint;
604 aNewPart.append(rPoint);
606 if(mbIsCurve)
608 const VN& rVNCurr = maVNV[pPNCurr->mnI];
610 if(!rVNCurr.maPrev.equalZero())
612 aNewPart.setPrevControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maPrev);
615 if(!rVNCurr.maNext.equalZero())
617 aNewPart.setNextControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maNext);
621 pPNCurr->mnI = SAL_MAX_UINT32;
622 nCountdown--;
623 pPNCurr = &(maPNV[pPNCurr->mnIN]);
625 while(pPNCurr != &rPN && pPNCurr->mnI != SAL_MAX_UINT32);
627 // close and add
628 aNewPart.setClosed(true);
629 aRetval.append(aNewPart);
633 return aRetval;
635 else
637 const sal_uInt32 nCorrectionSize(maCorrectionTable.size());
639 // no change, return original
640 if(!nCorrectionSize)
642 return maOriginal;
645 // apply coordinate corrections to ensure inside/outside correctness after solving
646 const sal_uInt32 nPolygonCount(maOriginal.count());
647 basegfx::B2DPolyPolygon aRetval(maOriginal);
649 for(sal_uInt32 a(0); a < nPolygonCount; a++)
651 basegfx::B2DPolygon aTemp(aRetval.getB2DPolygon(a));
652 const sal_uInt32 nPointCount(aTemp.count());
653 bool bChanged(false);
655 for(sal_uInt32 b(0); b < nPointCount; b++)
657 const basegfx::B2DPoint aCandidate(aTemp.getB2DPoint(b));
659 for(sal_uInt32 c(0); c < nCorrectionSize; c++)
661 if(maCorrectionTable[c].first.getX() == aCandidate.getX() && maCorrectionTable[c].first.getY() == aCandidate.getY())
663 aTemp.setB2DPoint(b, maCorrectionTable[c].second);
664 bChanged = true;
669 if(bChanged)
671 aRetval.setB2DPolygon(a, aTemp);
675 return aRetval;
680 } // end of anonymous namespace
681 } // end of namespace basegfx
683 namespace basegfx
685 namespace utils
688 B2DPolyPolygon solveCrossovers(const B2DPolyPolygon& rCandidate)
690 if(rCandidate.count() > 0)
692 solver aSolver(rCandidate);
693 return aSolver.getB2DPolyPolygon();
695 else
697 return rCandidate;
701 B2DPolyPolygon solveCrossovers(const B2DPolygon& rCandidate)
703 solver aSolver(rCandidate);
704 return aSolver.getB2DPolyPolygon();
707 B2DPolyPolygon stripNeutralPolygons(const B2DPolyPolygon& rCandidate)
709 B2DPolyPolygon aRetval;
711 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
713 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
715 if(utils::getOrientation(aCandidate) != B2VectorOrientation::Neutral)
717 aRetval.append(aCandidate);
721 return aRetval;
724 B2DPolyPolygon createNonzeroConform(const B2DPolyPolygon& rCandidate)
726 B2DPolyPolygon aCandidate;
728 // remove all self-intersections and intersections
729 if(rCandidate.count() == 1)
731 aCandidate = basegfx::utils::solveCrossovers(rCandidate.getB2DPolygon(0));
733 else
735 aCandidate = basegfx::utils::solveCrossovers(rCandidate);
738 // cleanup evtl. neutral polygons
739 aCandidate = basegfx::utils::stripNeutralPolygons(aCandidate);
741 // remove all polygons which have the same orientation as the polygon they are directly contained in
742 const sal_uInt32 nCount(aCandidate.count());
744 if(nCount > 1)
746 sal_uInt32 a, b;
747 std::vector< StripHelper > aHelpers;
748 aHelpers.resize(nCount);
750 for(a = 0; a < nCount; a++)
752 const B2DPolygon aCand(aCandidate.getB2DPolygon(a));
753 StripHelper* pNewHelper = &(aHelpers[a]);
754 pNewHelper->maRange = utils::getRange(aCand);
755 pNewHelper->meOrinetation = utils::getOrientation(aCand);
757 // initialize with own orientation
758 pNewHelper->mnDepth = (pNewHelper->meOrinetation == B2VectorOrientation::Negative ? -1 : 1);
761 for(a = 0; a < nCount - 1; a++)
763 const B2DPolygon aCandA(aCandidate.getB2DPolygon(a));
764 StripHelper& rHelperA = aHelpers[a];
766 for(b = a + 1; b < nCount; b++)
768 const B2DPolygon aCandB(aCandidate.getB2DPolygon(b));
769 StripHelper& rHelperB = aHelpers[b];
770 const bool bAInB(rHelperB.maRange.isInside(rHelperA.maRange) && utils::isInside(aCandB, aCandA, true));
772 if(bAInB)
774 // A is inside B, add orientation of B to A
775 rHelperA.mnDepth += (rHelperB.meOrinetation == B2VectorOrientation::Negative ? -1 : 1);
778 const bool bBInA(rHelperA.maRange.isInside(rHelperB.maRange) && utils::isInside(aCandA, aCandB, true));
780 if(bBInA)
782 // B is inside A, add orientation of A to B
783 rHelperB.mnDepth += (rHelperA.meOrinetation == B2VectorOrientation::Negative ? -1 : 1);
788 const B2DPolyPolygon aSource(aCandidate);
789 aCandidate.clear();
791 for(a = 0; a < nCount; a++)
793 const StripHelper& rHelper = aHelpers[a];
794 // for contained unequal oriented polygons sum will be 0
795 // for contained equal it will be >=2 or <=-2
796 // for free polygons (not contained) it will be 1 or -1
797 // -> accept all which are >=-1 && <= 1
798 bool bAcceptEntry(rHelper.mnDepth >= -1 && rHelper.mnDepth <= 1);
800 if(bAcceptEntry)
802 aCandidate.append(aSource.getB2DPolygon(a));
807 return aCandidate;
810 B2DPolyPolygon stripDispensablePolygons(const B2DPolyPolygon& rCandidate, bool bKeepAboveZero)
812 const sal_uInt32 nCount(rCandidate.count());
813 B2DPolyPolygon aRetval;
815 if(nCount)
817 if(nCount == 1)
819 if(!bKeepAboveZero && utils::getOrientation(rCandidate.getB2DPolygon(0)) == B2VectorOrientation::Positive)
821 aRetval = rCandidate;
824 else
826 sal_uInt32 a, b;
827 std::vector< StripHelper > aHelpers;
828 aHelpers.resize(nCount);
830 for(a = 0; a < nCount; a++)
832 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
833 StripHelper* pNewHelper = &(aHelpers[a]);
834 pNewHelper->maRange = utils::getRange(aCandidate);
835 pNewHelper->meOrinetation = utils::getOrientation(aCandidate);
836 pNewHelper->mnDepth = (pNewHelper->meOrinetation == B2VectorOrientation::Negative ? -1 : 0);
839 for(a = 0; a < nCount - 1; a++)
841 const B2DPolygon aCandA(rCandidate.getB2DPolygon(a));
842 StripHelper& rHelperA = aHelpers[a];
844 for(b = a + 1; b < nCount; b++)
846 const B2DPolygon aCandB(rCandidate.getB2DPolygon(b));
847 StripHelper& rHelperB = aHelpers[b];
848 const bool bAInB(rHelperB.maRange.isInside(rHelperA.maRange) && utils::isInside(aCandB, aCandA, true));
849 const bool bBInA(rHelperA.maRange.isInside(rHelperB.maRange) && utils::isInside(aCandA, aCandB, true));
851 if(bAInB && bBInA)
853 // congruent
854 if(rHelperA.meOrinetation == rHelperB.meOrinetation)
856 // two polys or two holes. Lower one of them to get one of them out of the way.
857 // Since each will be contained in the other one, both will be increased, too.
858 // So, for lowering, increase only one of them
859 rHelperA.mnDepth++;
861 else
863 // poly and hole. They neutralize, so get rid of both. Move securely below zero.
864 rHelperA.mnDepth = - static_cast<sal_Int32>(nCount);
865 rHelperB.mnDepth = - static_cast<sal_Int32>(nCount);
868 else
870 if(bAInB)
872 if(rHelperB.meOrinetation == B2VectorOrientation::Negative)
874 rHelperA.mnDepth--;
876 else
878 rHelperA.mnDepth++;
881 else if(bBInA)
883 if(rHelperA.meOrinetation == B2VectorOrientation::Negative)
885 rHelperB.mnDepth--;
887 else
889 rHelperB.mnDepth++;
896 for(a = 0; a < nCount; a++)
898 const StripHelper& rHelper = aHelpers[a];
899 bool bAcceptEntry(bKeepAboveZero ? 1 <= rHelper.mnDepth : rHelper.mnDepth == 0);
901 if(bAcceptEntry)
903 aRetval.append(rCandidate.getB2DPolygon(a));
909 return aRetval;
912 B2DPolyPolygon prepareForPolygonOperation(const B2DPolygon& rCandidate)
914 solver aSolver(rCandidate);
915 B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
917 return correctOrientations(aRetval);
920 B2DPolyPolygon prepareForPolygonOperation(const B2DPolyPolygon& rCandidate)
922 solver aSolver(rCandidate);
923 B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
925 return correctOrientations(aRetval);
928 B2DPolyPolygon solvePolygonOperationOr(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
930 if(!rCandidateA.count())
932 return rCandidateB;
934 else if(!rCandidateB.count())
936 return rCandidateA;
938 else
940 // concatenate polygons, solve crossovers and throw away all sub-polygons
941 // which have a depth other than 0.
942 B2DPolyPolygon aRetval(rCandidateA);
944 aRetval.append(rCandidateB);
945 aRetval = solveCrossovers(aRetval);
946 aRetval = stripNeutralPolygons(aRetval);
948 return stripDispensablePolygons(aRetval);
952 B2DPolyPolygon solvePolygonOperationXor(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
954 if(!rCandidateA.count())
956 return rCandidateB;
958 else if(!rCandidateB.count())
960 return rCandidateA;
962 else
964 // XOR is pretty simple: By definition it is the simple concatenation of
965 // the single polygons since we imply XOR fill rule. Make it intersection-free
966 // and correct orientations
967 B2DPolyPolygon aRetval(rCandidateA);
969 aRetval.append(rCandidateB);
970 aRetval = solveCrossovers(aRetval);
971 aRetval = stripNeutralPolygons(aRetval);
973 return correctOrientations(aRetval);
977 B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
979 if(!rCandidateA.count())
981 return B2DPolyPolygon();
983 else if(!rCandidateB.count())
985 return B2DPolyPolygon();
987 else
989 // concatenate polygons, solve crossovers and throw away all sub-polygons
990 // with a depth of < 1. This means to keep all polygons where at least two
991 // polygons do overlap.
992 B2DPolyPolygon aRetval(rCandidateA);
994 aRetval.append(rCandidateB);
995 aRetval = solveCrossovers(aRetval);
996 aRetval = stripNeutralPolygons(aRetval);
998 return stripDispensablePolygons(aRetval, true);
1002 B2DPolyPolygon solvePolygonOperationDiff(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
1004 if(!rCandidateA.count())
1006 return B2DPolyPolygon();
1008 else if(!rCandidateB.count())
1010 return rCandidateA;
1012 else
1014 // Make B topologically to holes and append to A
1015 B2DPolyPolygon aRetval(rCandidateB);
1017 aRetval.flip();
1018 aRetval.append(rCandidateA);
1020 // solve crossovers and throw away all sub-polygons which have a
1021 // depth other than 0.
1022 aRetval = basegfx::utils::solveCrossovers(aRetval);
1023 aRetval = basegfx::utils::stripNeutralPolygons(aRetval);
1025 return basegfx::utils::stripDispensablePolygons(aRetval);
1029 B2DPolyPolygon mergeToSinglePolyPolygon(const B2DPolyPolygonVector& rInput)
1031 B2DPolyPolygonVector aInput(rInput);
1033 // first step: prepareForPolygonOperation and simple merge of non-overlapping
1034 // PolyPolygons for speedup; this is possible for the wanted OR-operation
1035 if(!aInput.empty())
1037 B2DPolyPolygonVector aResult;
1038 aResult.reserve(aInput.size());
1040 for(basegfx::B2DPolyPolygon & a : aInput)
1042 const basegfx::B2DPolyPolygon aCandidate(prepareForPolygonOperation(a));
1044 if(!aResult.empty())
1046 const B2DRange aCandidateRange(aCandidate.getB2DRange());
1047 bool bCouldMergeSimple(false);
1049 for(auto & b: aResult)
1051 basegfx::B2DPolyPolygon aTarget(b);
1052 const B2DRange aTargetRange(aTarget.getB2DRange());
1054 if(!aCandidateRange.overlaps(aTargetRange))
1056 aTarget.append(aCandidate);
1057 b = aTarget;
1058 bCouldMergeSimple = true;
1059 break;
1063 if(!bCouldMergeSimple)
1065 aResult.push_back(aCandidate);
1068 else
1070 aResult.push_back(aCandidate);
1074 aInput = aResult;
1077 // second step: melt pairwise to a single PolyPolygon
1078 while(aInput.size() > 1)
1080 B2DPolyPolygonVector aResult;
1081 aResult.reserve((aInput.size() / 2) + 1);
1083 for(size_t a(0); a < aInput.size(); a += 2)
1085 if(a + 1 < aInput.size())
1087 // a pair for processing
1088 aResult.push_back(solvePolygonOperationOr(aInput[a], aInput[a + 1]));
1090 else
1092 // last single PolyPolygon; copy to target to not lose it
1093 aResult.push_back(aInput[a]);
1097 aInput = aResult;
1100 // third step: get result
1101 if(aInput.size() == 1)
1103 return aInput[0];
1106 return B2DPolyPolygon();
1109 } // end of namespace utils
1110 } // end of namespace basegfx
1112 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */