1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
30 #include <osl/diagnose.h>
31 #include <basegfx/numeric/ftools.hxx>
32 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
33 #include <basegfx/point/b2dpoint.hxx>
34 #include <basegfx/vector/b2dvector.hxx>
35 #include <basegfx/polygon/b2dpolygon.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
37 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
38 #include <basegfx/range/b2drange.hxx>
39 #include <basegfx/polygon/b2dpolypolygontools.hxx>
40 #include <basegfx/curve/b2dcubicbezier.hxx>
44 //////////////////////////////////////////////////////////////////////////////
50 //////////////////////////////////////////////////////////////////////////////
56 B2VectorOrientation meOrinetation
;
59 //////////////////////////////////////////////////////////////////////////////
70 //////////////////////////////////////////////////////////////////////////////
78 // to have the correct curve segments in the crossover checks,
79 // it is necessary to keep the original next vectors, too. Else,
80 // it may happen to use a already switched next vector which
81 // would interpolate the wrong comparison point
82 B2DVector maOriginalNext
;
85 //////////////////////////////////////////////////////////////////////////////
92 bool operator<(const SN
& rComp
) const
94 if(fTools::equal(mpPN
->maPoint
.getX(), rComp
.mpPN
->maPoint
.getX()))
96 if(fTools::equal(mpPN
->maPoint
.getY(), rComp
.mpPN
->maPoint
.getY()))
98 return (mpPN
->mnI
< rComp
.mpPN
->mnI
);
102 return fTools::less(mpPN
->maPoint
.getY(), rComp
.mpPN
->maPoint
.getY());
107 return fTools::less(mpPN
->maPoint
.getX(), rComp
.mpPN
->maPoint
.getX());
112 //////////////////////////////////////////////////////////////////////////////
114 typedef ::std::vector
< PN
> PNV
;
115 typedef ::std::vector
< VN
> VNV
;
116 typedef ::std::vector
< SN
> SNV
;
118 //////////////////////////////////////////////////////////////////////////////
123 const B2DPolyPolygon maOriginal
;
128 unsigned mbIsCurve
: 1;
129 unsigned mbChanged
: 1;
131 void impAddPolygon(const sal_uInt32 aPos
, const B2DPolygon
& rGeometry
)
133 const sal_uInt32
nCount(rGeometry
.count());
138 for(sal_uInt32
a(0); a
< nCount
; a
++)
140 const B2DPoint
aPoint(rGeometry
.getB2DPoint(a
));
141 aNewPN
.maPoint
= aPoint
;
142 aNewPN
.mnI
= aPos
+ a
;
143 aNewPN
.mnIP
= aPos
+ ((a
!= 0) ? a
- 1 : nCount
- 1);
144 aNewPN
.mnIN
= aPos
+ ((a
+ 1 == nCount
) ? 0 : a
+ 1);
145 maPNV
.push_back(aNewPN
);
149 aNewVN
.maPrev
= rGeometry
.getPrevControlPoint(a
) - aPoint
;
150 aNewVN
.maNext
= rGeometry
.getNextControlPoint(a
) - aPoint
;
151 aNewVN
.maOriginalNext
= aNewVN
.maNext
;
152 maVNV
.push_back(aNewVN
);
155 aNewSN
.mpPN
= &maPNV
[maPNV
.size() - 1];
156 maSNV
.push_back(aNewSN
);
160 bool impLeftOfEdges(const B2DVector
& rVecA
, const B2DVector
& rVecB
, const B2DVector
& rTest
)
162 // tests if rTest is left of both directed line segments along the line -rVecA, rVecB. Test is
164 if(rVecA
.cross(rVecB
) > 0.0)
166 // b is left turn seen from a, test if Test is left of both and so inside (left is seeen as inside)
167 const bool bBoolA(fTools::moreOrEqual(rVecA
.cross(rTest
), 0.0));
168 const bool bBoolB(fTools::lessOrEqual(rVecB
.cross(rTest
), 0.0));
170 return (bBoolA
&& bBoolB
);
174 // b is right turn seen from a, test if Test is right of both and so outside (left is seeen as inside)
175 const bool bBoolA(fTools::lessOrEqual(rVecA
.cross(rTest
), 0.0));
176 const bool bBoolB(fTools::moreOrEqual(rVecB
.cross(rTest
), 0.0));
178 return (!(bBoolA
&& bBoolB
));
182 void impSwitchNext(PN
& rPNa
, PN
& rPNb
)
184 ::std::swap(rPNa
.mnIN
, rPNb
.mnIN
);
188 VN
& rVNa
= maVNV
[rPNa
.mnI
];
189 VN
& rVNb
= maVNV
[rPNb
.mnI
];
191 ::std::swap(rVNa
.maNext
, rVNb
.maNext
);
200 B2DCubicBezier
createSegment(const PN
& rPN
, bool bPrev
) const
202 const B2DPoint
& rStart(rPN
.maPoint
);
203 const B2DPoint
& rEnd(maPNV
[bPrev
? rPN
.mnIP
: rPN
.mnIN
].maPoint
);
204 const B2DVector
& rCPA(bPrev
? maVNV
[rPN
.mnI
].maPrev
: maVNV
[rPN
.mnI
].maNext
);
205 // Use maOriginalNext, not maNext to create the original (yet unchanged)
206 // curve segment. Otherwise, this segment would NOT ne correct.
207 const B2DVector
& rCPB(bPrev
? maVNV
[maPNV
[rPN
.mnIP
].mnI
].maOriginalNext
: maVNV
[maPNV
[rPN
.mnIN
].mnI
].maPrev
);
209 return B2DCubicBezier(rStart
, rStart
+ rCPA
, rEnd
+ rCPB
, rEnd
);
212 void impHandleCommon(PN
& rPNa
, PN
& rPNb
)
216 const B2DCubicBezier
aNextA(createSegment(rPNa
, false));
217 const B2DCubicBezier
aPrevA(createSegment(rPNa
, true));
219 if(aNextA
.equal(aPrevA
))
221 // deadend on A (identical edge)
225 const B2DCubicBezier
aNextB(createSegment(rPNb
, false));
226 const B2DCubicBezier
aPrevB(createSegment(rPNb
, true));
228 if(aNextB
.equal(aPrevB
))
230 // deadend on B (identical edge)
234 if(aPrevA
.equal(aPrevB
))
236 // common edge in same direction
237 if(aNextA
.equal(aNextB
))
239 // common edge in same direction continues
244 // common edge in same direction leave
245 // action is done on enter
249 else if(aPrevA
.equal(aNextB
))
251 // common edge in opposite direction
252 if(aNextA
.equal(aPrevB
))
254 // common edge in opposite direction continues
259 // common edge in opposite direction leave
260 impSwitchNext(rPNa
, rPNb
);
263 else if(aNextA
.equal(aNextB
))
265 // common edge in same direction enter
267 PN
* pPNa2
= &maPNV
[rPNa
.mnIN
];
268 PN
* pPNb2
= &maPNV
[rPNb
.mnIN
];
273 const B2DCubicBezier
aNextA2(createSegment(*pPNa2
, false));
274 const B2DCubicBezier
aNextB2(createSegment(*pPNb2
, false));
276 if(aNextA2
.equal(aNextB2
))
278 pPNa2
= &maPNV
[pPNa2
->mnIN
];
279 pPNb2
= &maPNV
[pPNb2
->mnIN
];
286 while(bOnEdge
&& pPNa2
!= &rPNa
&& pPNa2
!= &rPNa
);
290 // loop over two identical polygon paths
295 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
296 // at enter/leave. Check for crossover.
297 const B2DVector
aPrevCA(aPrevA
.interpolatePoint(0.5) - aPrevA
.getStartPoint());
298 const B2DVector
aNextCA(aNextA
.interpolatePoint(0.5) - aNextA
.getStartPoint());
299 const B2DVector
aPrevCB(aPrevB
.interpolatePoint(0.5) - aPrevB
.getStartPoint());
300 const bool bEnter(impLeftOfEdges(aPrevCA
, aNextCA
, aPrevCB
));
302 const B2DCubicBezier
aNextA2(createSegment(*pPNa2
, false));
303 const B2DCubicBezier
aPrevA2(createSegment(*pPNa2
, true));
304 const B2DCubicBezier
aNextB2(createSegment(*pPNb2
, false));
305 const B2DVector
aPrevCA2(aPrevA2
.interpolatePoint(0.5) - aPrevA2
.getStartPoint());
306 const B2DVector
aNextCA2(aNextA2
.interpolatePoint(0.5) - aNextA2
.getStartPoint());
307 const B2DVector
aNextCB2(aNextB2
.interpolatePoint(0.5) - aNextB2
.getStartPoint());
308 const bool bLeave(impLeftOfEdges(aPrevCA2
, aNextCA2
, aNextCB2
));
313 impSwitchNext(rPNa
, rPNb
);
317 else if(aNextA
.equal(aPrevB
))
319 // common edge in opposite direction enter
320 impSwitchNext(rPNa
, rPNb
);
324 // no common edges, check for crossover
325 const B2DVector
aPrevCA(aPrevA
.interpolatePoint(0.5) - aPrevA
.getStartPoint());
326 const B2DVector
aNextCA(aNextA
.interpolatePoint(0.5) - aNextA
.getStartPoint());
327 const B2DVector
aPrevCB(aPrevB
.interpolatePoint(0.5) - aPrevB
.getStartPoint());
328 const B2DVector
aNextCB(aNextB
.interpolatePoint(0.5) - aNextB
.getStartPoint());
330 const bool bEnter(impLeftOfEdges(aPrevCA
, aNextCA
, aPrevCB
));
331 const bool bLeave(impLeftOfEdges(aPrevCA
, aNextCA
, aNextCB
));
336 impSwitchNext(rPNa
, rPNb
);
342 const B2DPoint
& rNextA(maPNV
[rPNa
.mnIN
].maPoint
);
343 const B2DPoint
& rPrevA(maPNV
[rPNa
.mnIP
].maPoint
);
345 if(rNextA
.equal(rPrevA
))
351 const B2DPoint
& rNextB(maPNV
[rPNb
.mnIN
].maPoint
);
352 const B2DPoint
& rPrevB(maPNV
[rPNb
.mnIP
].maPoint
);
354 if(rNextB
.equal(rPrevB
))
360 if(rPrevA
.equal(rPrevB
))
362 // common edge in same direction
363 if(rNextA
.equal(rNextB
))
365 // common edge in same direction continues
370 // common edge in same direction leave
371 // action is done on enter
375 else if(rPrevA
.equal(rNextB
))
377 // common edge in opposite direction
378 if(rNextA
.equal(rPrevB
))
380 // common edge in opposite direction continues
385 // common edge in opposite direction leave
386 impSwitchNext(rPNa
, rPNb
);
389 else if(rNextA
.equal(rNextB
))
391 // common edge in same direction enter
393 PN
* pPNa2
= &maPNV
[rPNa
.mnIN
];
394 PN
* pPNb2
= &maPNV
[rPNb
.mnIN
];
399 const B2DPoint
& rNextA2(maPNV
[pPNa2
->mnIN
].maPoint
);
400 const B2DPoint
& rNextB2(maPNV
[pPNb2
->mnIN
].maPoint
);
402 if(rNextA2
.equal(rNextB2
))
404 pPNa2
= &maPNV
[pPNa2
->mnIN
];
405 pPNb2
= &maPNV
[pPNb2
->mnIN
];
412 while(bOnEdge
&& pPNa2
!= &rPNa
&& pPNa2
!= &rPNa
);
416 // loop over two identical polygon paths
421 // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
422 // at enter/leave. Check for crossover.
423 const B2DPoint
& aPointE(rPNa
.maPoint
);
424 const B2DVector
aPrevAE(rPrevA
- aPointE
);
425 const B2DVector
aNextAE(rNextA
- aPointE
);
426 const B2DVector
aPrevBE(rPrevB
- aPointE
);
428 const B2DPoint
& aPointL(pPNa2
->maPoint
);
429 const B2DVector
aPrevAL(maPNV
[pPNa2
->mnIP
].maPoint
- aPointL
);
430 const B2DVector
aNextAL(maPNV
[pPNa2
->mnIN
].maPoint
- aPointL
);
431 const B2DVector
aNextBL(maPNV
[pPNb2
->mnIN
].maPoint
- aPointL
);
433 const bool bEnter(impLeftOfEdges(aPrevAE
, aNextAE
, aPrevBE
));
434 const bool bLeave(impLeftOfEdges(aPrevAL
, aNextAL
, aNextBL
));
438 // crossover; switch start or end
439 impSwitchNext(rPNa
, rPNb
);
443 else if(rNextA
.equal(rPrevB
))
445 // common edge in opposite direction enter
446 impSwitchNext(rPNa
, rPNb
);
450 // no common edges, check for crossover
451 const B2DPoint
& aPoint(rPNa
.maPoint
);
452 const B2DVector
aPrevA(rPrevA
- aPoint
);
453 const B2DVector
aNextA(rNextA
- aPoint
);
454 const B2DVector
aPrevB(rPrevB
- aPoint
);
455 const B2DVector
aNextB(rNextB
- aPoint
);
457 const bool bEnter(impLeftOfEdges(aPrevA
, aNextA
, aPrevB
));
458 const bool bLeave(impLeftOfEdges(aPrevA
, aNextA
, aNextB
));
463 impSwitchNext(rPNa
, rPNb
);
471 // sort by point to identify common nodes
472 ::std::sort(maSNV
.begin(), maSNV
.end());
474 // handle common nodes
475 const sal_uInt32
nNodeCount(maSNV
.size());
477 for(sal_uInt32
a(0); a
< nNodeCount
- 1; a
++)
479 // test a before using it, not after. Also use nPointCount instead of aSortNodes.size()
480 PN
& rPNb
= *(maSNV
[a
].mpPN
);
482 for(sal_uInt32
b(a
+ 1); b
< nNodeCount
&& rPNb
.maPoint
.equal(maSNV
[b
].mpPN
->maPoint
); b
++)
484 impHandleCommon(rPNb
, *maSNV
[b
].mpPN
);
490 solver(const B2DPolygon
& rOriginal
)
491 : maOriginal(B2DPolyPolygon(rOriginal
)),
495 const sal_uInt32
nOriginalCount(rOriginal
.count());
499 B2DPolygon
aGeometry(tools::addPointsAtCutsAndTouches(rOriginal
));
500 aGeometry
.removeDoublePoints();
501 aGeometry
= tools::simplifyCurveSegments(aGeometry
);
502 mbIsCurve
= aGeometry
.areControlPointsUsed();
504 const sal_uInt32
nPointCount(aGeometry
.count());
506 // If it's not a pezier polygon, at least four points are needed to create
507 // a self-intersection. If it's a bezier polygon, the minimum point number
508 // is two, since with a single point You get a curve, but no self-intersection
509 if(nPointCount
> 3 || (nPointCount
> 1 && mbIsCurve
))
511 // reserve space in point, control and sort vector.
512 maSNV
.reserve(nPointCount
);
513 maPNV
.reserve(nPointCount
);
514 maVNV
.reserve(mbIsCurve
? nPointCount
: 0);
517 impAddPolygon(0, aGeometry
);
519 // solve common nodes
525 solver(const B2DPolyPolygon
& rOriginal
)
526 : maOriginal(rOriginal
),
530 sal_uInt32
nOriginalCount(maOriginal
.count());
534 B2DPolyPolygon
aGeometry(tools::addPointsAtCutsAndTouches(maOriginal
, true));
535 aGeometry
.removeDoublePoints();
536 aGeometry
= tools::simplifyCurveSegments(aGeometry
);
537 mbIsCurve
= aGeometry
.areControlPointsUsed();
538 nOriginalCount
= aGeometry
.count();
542 sal_uInt32
nPointCount(0);
546 for(a
= 0; a
< nOriginalCount
; a
++)
548 const B2DPolygon
aCandidate(aGeometry
.getB2DPolygon(a
));
549 const sal_uInt32
nCandCount(aCandidate
.count());
551 // If it's not a bezier curve, at least three points would be needed to have a
552 // topological relevant (not empty) polygon. Since its not known here if trivial
553 // edges (dead ends) will be kept or sorted out, add non-bezier polygons with
554 // more than one point.
555 // For bezier curves, the minimum for defining an area is also one.
558 nPointCount
+= nCandCount
;
564 // reserve space in point, control and sort vector.
565 maSNV
.reserve(nPointCount
);
566 maPNV
.reserve(nPointCount
);
567 maVNV
.reserve(mbIsCurve
? nPointCount
: 0);
570 sal_uInt32
nInsertIndex(0);
572 for(a
= 0; a
< nOriginalCount
; a
++)
574 const B2DPolygon
aCandidate(aGeometry
.getB2DPolygon(a
));
575 const sal_uInt32
nCandCount(aCandidate
.count());
577 // use same condition as above, the data vector is
581 impAddPolygon(nInsertIndex
, aCandidate
);
582 nInsertIndex
+= nCandCount
;
586 // solve common nodes
593 B2DPolyPolygon
getB2DPolyPolygon()
597 B2DPolyPolygon aRetval
;
598 const sal_uInt32
nCount(maPNV
.size());
599 sal_uInt32
nCountdown(nCount
);
601 for(sal_uInt32
a(0); nCountdown
&& a
< nCount
; a
++)
605 if(SAL_MAX_UINT32
!= rPN
.mnI
)
607 // unused node, start new part polygon
613 const B2DPoint
& rPoint
= pPNCurr
->maPoint
;
614 aNewPart
.append(rPoint
);
618 const VN
& rVNCurr
= maVNV
[pPNCurr
->mnI
];
620 if(!rVNCurr
.maPrev
.equalZero())
622 aNewPart
.setPrevControlPoint(aNewPart
.count() - 1, rPoint
+ rVNCurr
.maPrev
);
625 if(!rVNCurr
.maNext
.equalZero())
627 aNewPart
.setNextControlPoint(aNewPart
.count() - 1, rPoint
+ rVNCurr
.maNext
);
631 pPNCurr
->mnI
= SAL_MAX_UINT32
;
633 pPNCurr
= &(maPNV
[pPNCurr
->mnIN
]);
635 while(pPNCurr
!= &rPN
&& SAL_MAX_UINT32
!= pPNCurr
->mnI
);
638 aNewPart
.setClosed(true);
639 aRetval
.append(aNewPart
);
647 // no change, return original
653 //////////////////////////////////////////////////////////////////////////////
655 } // end of anonymous namespace
656 } // end of namespace basegfx
658 //////////////////////////////////////////////////////////////////////////////
664 //////////////////////////////////////////////////////////////////////////////
666 B2DPolyPolygon
solveCrossovers(const B2DPolyPolygon
& rCandidate
)
668 if(rCandidate
.count() > 1L)
670 solver
aSolver(rCandidate
);
671 return aSolver
.getB2DPolyPolygon();
679 //////////////////////////////////////////////////////////////////////////////
681 B2DPolyPolygon
solveCrossovers(const B2DPolygon
& rCandidate
)
683 solver
aSolver(rCandidate
);
684 return aSolver
.getB2DPolyPolygon();
687 //////////////////////////////////////////////////////////////////////////////
689 B2DPolyPolygon
stripNeutralPolygons(const B2DPolyPolygon
& rCandidate
)
691 B2DPolyPolygon aRetval
;
693 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
695 const B2DPolygon
aCandidate(rCandidate
.getB2DPolygon(a
));
697 if(ORIENTATION_NEUTRAL
!= tools::getOrientation(aCandidate
))
699 aRetval
.append(aCandidate
);
706 //////////////////////////////////////////////////////////////////////////////
708 B2DPolyPolygon
stripDispensablePolygons(const B2DPolyPolygon
& rCandidate
, bool bKeepAboveZero
)
710 const sal_uInt32
nCount(rCandidate
.count());
711 B2DPolyPolygon aRetval
;
717 if(!bKeepAboveZero
&& ORIENTATION_POSITIVE
== tools::getOrientation(rCandidate
.getB2DPolygon(0L)))
719 aRetval
= rCandidate
;
725 ::std::vector
< StripHelper
> aHelpers
;
726 aHelpers
.resize(nCount
);
728 for(a
= 0L; a
< nCount
; a
++)
730 const B2DPolygon
aCandidate(rCandidate
.getB2DPolygon(a
));
731 StripHelper
* pNewHelper
= &(aHelpers
[a
]);
732 pNewHelper
->maRange
= tools::getRange(aCandidate
);
733 pNewHelper
->meOrinetation
= tools::getOrientation(aCandidate
);
734 pNewHelper
->mnDepth
= (ORIENTATION_NEGATIVE
== pNewHelper
->meOrinetation
? -1L : 0L);
737 for(a
= 0L; a
< nCount
- 1L; a
++)
739 const B2DPolygon
aCandA(rCandidate
.getB2DPolygon(a
));
740 StripHelper
& rHelperA
= aHelpers
[a
];
742 for(b
= a
+ 1L; b
< nCount
; b
++)
744 const B2DPolygon
aCandB(rCandidate
.getB2DPolygon(b
));
745 StripHelper
& rHelperB
= aHelpers
[b
];
746 const bool bAInB(rHelperB
.maRange
.isInside(rHelperA
.maRange
) && tools::isInside(aCandB
, aCandA
, true));
747 const bool bBInA(rHelperA
.maRange
.isInside(rHelperB
.maRange
) && tools::isInside(aCandA
, aCandB
, true));
752 if(rHelperA
.meOrinetation
== rHelperB
.meOrinetation
)
754 // two polys or two holes. Lower one of them to get one of them out of the way.
755 // Since each will be contained in the other one, both will be increased, too.
756 // So, for lowering, increase only one of them
761 // poly and hole. They neutralize, so get rid of both. Move securely below zero.
762 rHelperA
.mnDepth
= -((sal_Int32
)nCount
);
763 rHelperB
.mnDepth
= -((sal_Int32
)nCount
);
770 if(ORIENTATION_NEGATIVE
== rHelperB
.meOrinetation
)
781 if(ORIENTATION_NEGATIVE
== rHelperA
.meOrinetation
)
794 for(a
= 0L; a
< nCount
; a
++)
796 const StripHelper
& rHelper
= aHelpers
[a
];
797 bool bAcceptEntry(bKeepAboveZero
? 1L <= rHelper
.mnDepth
: 0L == rHelper
.mnDepth
);
801 aRetval
.append(rCandidate
.getB2DPolygon(a
));
810 //////////////////////////////////////////////////////////////////////////////
812 B2DPolyPolygon
prepareForPolygonOperation(const B2DPolygon
& rCandidate
)
814 solver
aSolver(rCandidate
);
815 B2DPolyPolygon
aRetval(stripNeutralPolygons(aSolver
.getB2DPolyPolygon()));
817 return correctOrientations(aRetval
);
820 B2DPolyPolygon
prepareForPolygonOperation(const B2DPolyPolygon
& rCandidate
)
822 solver
aSolver(rCandidate
);
823 B2DPolyPolygon
aRetval(stripNeutralPolygons(aSolver
.getB2DPolyPolygon()));
825 return correctOrientations(aRetval
);
828 B2DPolyPolygon
solvePolygonOperationOr(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
830 if(!rCandidateA
.count())
834 else if(!rCandidateB
.count())
840 // concatenate polygons, solve crossovers and throw away all sub-polygons
841 // which have a depth other than 0.
842 B2DPolyPolygon
aRetval(rCandidateA
);
844 aRetval
.append(rCandidateB
);
845 aRetval
= solveCrossovers(aRetval
);
846 aRetval
= stripNeutralPolygons(aRetval
);
848 return stripDispensablePolygons(aRetval
, false);
852 B2DPolyPolygon
solvePolygonOperationXor(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
854 if(!rCandidateA
.count())
858 else if(!rCandidateB
.count())
864 // XOR is pretty simple: By definition it is the simple concatenation of
865 // the single polygons since we imply XOR fill rule. Make it intersection-free
866 // and correct orientations
867 B2DPolyPolygon
aRetval(rCandidateA
);
869 aRetval
.append(rCandidateB
);
870 aRetval
= solveCrossovers(aRetval
);
871 aRetval
= stripNeutralPolygons(aRetval
);
873 return correctOrientations(aRetval
);
877 B2DPolyPolygon
solvePolygonOperationAnd(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
879 if(!rCandidateA
.count())
881 return B2DPolyPolygon();
883 else if(!rCandidateB
.count())
885 return B2DPolyPolygon();
889 // concatenate polygons, solve crossovers and throw away all sub-polygons
890 // with a depth of < 1. This means to keep all polygons where at least two
891 // polygons do overlap.
892 B2DPolyPolygon
aRetval(rCandidateA
);
894 aRetval
.append(rCandidateB
);
895 aRetval
= solveCrossovers(aRetval
);
896 aRetval
= stripNeutralPolygons(aRetval
);
898 return stripDispensablePolygons(aRetval
, true);
902 B2DPolyPolygon
solvePolygonOperationDiff(const B2DPolyPolygon
& rCandidateA
, const B2DPolyPolygon
& rCandidateB
)
904 if(!rCandidateA
.count())
906 return B2DPolyPolygon();
908 else if(!rCandidateB
.count())
914 // Make B topologically to holes and append to A
915 B2DPolyPolygon
aRetval(rCandidateB
);
918 aRetval
.append(rCandidateA
);
920 // solve crossovers and throw away all sub-polygons which have a
921 // depth other than 0.
922 aRetval
= basegfx::tools::solveCrossovers(aRetval
);
923 aRetval
= basegfx::tools::stripNeutralPolygons(aRetval
);
925 return basegfx::tools::stripDispensablePolygons(aRetval
, false);
929 B2DPolyPolygon
mergeToSinglePolyPolygon(const std::vector
< basegfx::B2DPolyPolygon
>& rInput
)
931 std::vector
< basegfx::B2DPolyPolygon
> aInput(rInput
);
933 // first step: prepareForPolygonOperation and simple merge of non-overlapping
934 // PolyPolygons for speedup; this is possible for the wanted OR-operation
937 std::vector
< basegfx::B2DPolyPolygon
> aResult
;
938 aResult
.reserve(aInput
.size());
940 for(sal_uInt32
a(0); a
< aInput
.size(); a
++)
942 const basegfx::B2DPolyPolygon
aCandidate(prepareForPolygonOperation(aInput
[a
]));
946 const B2DRange
aCandidateRange(aCandidate
.getB2DRange());
947 bool bCouldMergeSimple(false);
949 for(sal_uInt32
b(0); !bCouldMergeSimple
&& b
< aResult
.size(); b
++)
951 basegfx::B2DPolyPolygon
aTarget(aResult
[b
]);
952 const B2DRange
aTargetRange(aTarget
.getB2DRange());
954 if(!aCandidateRange
.overlaps(aTargetRange
))
956 aTarget
.append(aCandidate
);
957 aResult
[b
] = aTarget
;
958 bCouldMergeSimple
= true;
962 if(!bCouldMergeSimple
)
964 aResult
.push_back(aCandidate
);
969 aResult
.push_back(aCandidate
);
976 // second step: melt pairwise to a single PolyPolygon
977 while(aInput
.size() > 1)
979 std::vector
< basegfx::B2DPolyPolygon
> aResult
;
980 aResult
.reserve((aInput
.size() / 2) + 1);
982 for(sal_uInt32
a(0); a
< aInput
.size(); a
+= 2)
984 if(a
+ 1 < aInput
.size())
986 // a pair for processing
987 aResult
.push_back(solvePolygonOperationOr(aInput
[a
], aInput
[a
+ 1]));
991 // last single PolyPolygon; copy to target to not lose it
992 aResult
.push_back(aInput
[a
]);
999 // third step: get result
1000 if(1 == aInput
.size())
1005 return B2DPolyPolygon();
1008 //////////////////////////////////////////////////////////////////////////////
1010 } // end of namespace tools
1011 } // end of namespace basegfx
1013 //////////////////////////////////////////////////////////////////////////////